s3 sticky write time: Removed unused args and tighten up a function by making an...
[nivanova/samba-autobuild/.git] / source3 / smbd / dosmode.c
1 /* 
2    Unix SMB/CIFS implementation.
3    dos mode handling functions
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) James Peach 2006
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22
23 static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
24 {
25 #if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
26         if (sbuf->st_ex_size > sbuf->st_ex_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
27                 return FILE_ATTRIBUTE_SPARSE;
28         }
29 #endif
30         return 0;
31 }
32
33 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
34 {
35 #ifdef S_ISLNK
36 #if LINKS_READ_ONLY
37         if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
38                 return aRONLY;
39 #endif
40 #endif
41         return 0;
42 }
43
44 /****************************************************************************
45  Change a dos mode to a unix mode.
46     Base permission for files:
47          if creating file and inheriting (i.e. parent_dir != NULL)
48            apply read/write bits from parent directory.
49          else   
50            everybody gets read bit set
51          dos readonly is represented in unix by removing everyone's write bit
52          dos archive is represented in unix by the user's execute bit
53          dos system is represented in unix by the group's execute bit
54          dos hidden is represented in unix by the other's execute bit
55          if !inheriting {
56            Then apply create mask,
57            then add force bits.
58          }
59     Base permission for directories:
60          dos directory is represented in unix by unix's dir bit and the exec bit
61          if !inheriting {
62            Then apply create mask,
63            then add force bits.
64          }
65 ****************************************************************************/
66
67 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
68                  const char *inherit_from_dir)
69 {
70         mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
71         mode_t dir_mode = 0; /* Mode of the inherit_from directory if
72                               * inheriting. */
73
74         if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
75                 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
76         }
77
78         if (fname && (inherit_from_dir != NULL)
79             && lp_inherit_perms(SNUM(conn))) {
80                 SMB_STRUCT_STAT sbuf;
81
82                 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
83                           inherit_from_dir));
84                 if (vfs_stat_smb_fname(conn, inherit_from_dir, &sbuf) != 0) {
85                         DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
86                                  inherit_from_dir, strerror(errno)));
87                         return(0);      /* *** shouldn't happen! *** */
88                 }
89
90                 /* Save for later - but explicitly remove setuid bit for safety. */
91                 dir_mode = sbuf.st_ex_mode & ~S_ISUID;
92                 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
93                 /* Clear "result" */
94                 result = 0;
95         } 
96
97         if (IS_DOS_DIR(dosmode)) {
98                 /* We never make directories read only for the owner as under DOS a user
99                 can always create a file in a read-only directory. */
100                 result |= (S_IFDIR | S_IWUSR);
101
102                 if (dir_mode) {
103                         /* Inherit mode of parent directory. */
104                         result |= dir_mode;
105                 } else {
106                         /* Provisionally add all 'x' bits */
107                         result |= (S_IXUSR | S_IXGRP | S_IXOTH);                 
108
109                         /* Apply directory mask */
110                         result &= lp_dir_mask(SNUM(conn));
111                         /* Add in force bits */
112                         result |= lp_force_dir_mode(SNUM(conn));
113                 }
114         } else { 
115                 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
116                         result |= S_IXUSR;
117
118                 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
119                         result |= S_IXGRP;
120
121                 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
122                         result |= S_IXOTH;  
123
124                 if (dir_mode) {
125                         /* Inherit 666 component of parent directory mode */
126                         result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
127                 } else {
128                         /* Apply mode mask */
129                         result &= lp_create_mask(SNUM(conn));
130                         /* Add in force bits */
131                         result |= lp_force_create_mode(SNUM(conn));
132                 }
133         }
134
135         DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
136         return(result);
137 }
138
139 /****************************************************************************
140  Change a unix mode to a dos mode.
141 ****************************************************************************/
142
143 static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, const SMB_STRUCT_STAT *sbuf)
144 {
145         int result = 0;
146         enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
147
148         if (ro_opts == MAP_READONLY_YES) {
149                 /* Original Samba method - map inverse of user "w" bit. */
150                 if ((sbuf->st_ex_mode & S_IWUSR) == 0) {
151                         result |= aRONLY;
152                 }
153         } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
154                 struct smb_filename *smb_fname = NULL;
155                 NTSTATUS status;
156
157                 status = create_synthetic_smb_fname_split(talloc_tos(), path,
158                                                           sbuf, &smb_fname);
159                 if (NT_STATUS_IS_OK(status)) {
160                         /* Check actual permissions for read-only. */
161                         if (!can_write_to_file(conn, smb_fname)) {
162                                 result |= aRONLY;
163                         }
164                 }
165                 TALLOC_FREE(smb_fname);
166         } /* Else never set the readonly bit. */
167
168         if (MAP_ARCHIVE(conn) && ((sbuf->st_ex_mode & S_IXUSR) != 0))
169                 result |= aARCH;
170
171         if (MAP_SYSTEM(conn) && ((sbuf->st_ex_mode & S_IXGRP) != 0))
172                 result |= aSYSTEM;
173
174         if (MAP_HIDDEN(conn) && ((sbuf->st_ex_mode & S_IXOTH) != 0))
175                 result |= aHIDDEN;   
176
177         if (S_ISDIR(sbuf->st_ex_mode))
178                 result = aDIR | (result & aRONLY);
179
180         result |= set_sparse_flag(sbuf);
181         result |= set_link_read_only_flag(sbuf);
182
183         DEBUG(8,("dos_mode_from_sbuf returning "));
184
185         if (result & aHIDDEN) DEBUG(8, ("h"));
186         if (result & aRONLY ) DEBUG(8, ("r"));
187         if (result & aSYSTEM) DEBUG(8, ("s"));
188         if (result & aDIR   ) DEBUG(8, ("d"));
189         if (result & aARCH  ) DEBUG(8, ("a"));
190
191         DEBUG(8,("\n"));
192         return result;
193 }
194
195 /****************************************************************************
196  Get DOS attributes from an EA.
197 ****************************************************************************/
198
199 static bool get_ea_dos_attribute(connection_struct *conn, const char *path, const SMB_STRUCT_STAT *sbuf, uint32 *pattr)
200 {
201         ssize_t sizeret;
202         fstring attrstr;
203         unsigned int dosattr;
204
205         if (!lp_store_dos_attributes(SNUM(conn))) {
206                 return False;
207         }
208
209         /* Don't reset pattr to zero as we may already have filename-based attributes we
210            need to preserve. */
211
212         sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
213         if (sizeret == -1) {
214                 if (errno == ENOSYS
215 #if defined(ENOTSUP)
216                         || errno == ENOTSUP) {
217 #else
218                                 ) {
219 #endif
220                         DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
221                                 path, strerror(errno) ));
222                         set_store_dos_attributes(SNUM(conn), False);
223                 }
224                 return False;
225         }
226         /* Null terminate string. */
227         attrstr[sizeret] = 0;
228         DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
229
230         if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
231                         sscanf(attrstr, "%x", &dosattr) != 1) {
232                 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
233                 return False;
234         }
235
236         if (S_ISDIR(sbuf->st_ex_mode)) {
237                 dosattr |= aDIR;
238         }
239         *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
240
241         DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
242
243         if (dosattr & aHIDDEN) DEBUG(8, ("h"));
244         if (dosattr & aRONLY ) DEBUG(8, ("r"));
245         if (dosattr & aSYSTEM) DEBUG(8, ("s"));
246         if (dosattr & aDIR   ) DEBUG(8, ("d"));
247         if (dosattr & aARCH  ) DEBUG(8, ("a"));
248
249         DEBUG(8,("\n"));
250
251         return True;
252 }
253
254 /****************************************************************************
255  Set DOS attributes in an EA.
256 ****************************************************************************/
257
258 static bool set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
259 {
260         fstring attrstr;
261         files_struct *fsp = NULL;
262         bool ret = False;
263
264         if (!lp_store_dos_attributes(SNUM(conn))) {
265                 return False;
266         }
267
268         snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
269         if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
270                 if((errno != EPERM) && (errno != EACCES)) {
271                         if (errno == ENOSYS
272 #if defined(ENOTSUP)
273                                 || errno == ENOTSUP) {
274 #else
275                                 ) {
276 #endif
277                                 DEBUG(1,("set_ea_dos_attributes: Cannot set attribute EA on file %s: Error = %s\n",
278                                         path, strerror(errno) ));
279                                 set_store_dos_attributes(SNUM(conn), False);
280                         }
281                         return False;
282                 }
283
284                 /* We want DOS semantics, ie allow non owner with write permission to change the
285                         bits on a file. Just like file_ntimes below.
286                 */
287
288                 /* Check if we have write access. */
289                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
290                         return False;
291
292                 /*
293                  * We need to open the file with write access whilst
294                  * still in our current user context. This ensures we
295                  * are not violating security in doing the setxattr.
296                  */
297
298                 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, path, sbuf,
299                                                       &fsp)))
300                         return ret;
301                 become_root();
302                 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
303                         ret = True;
304                 }
305                 unbecome_root();
306                 close_file_fchmod(NULL, fsp);
307                 return ret;
308         }
309         DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
310         return True;
311 }
312
313 /****************************************************************************
314  Change a unix mode to a dos mode for an ms dfs link.
315 ****************************************************************************/
316
317 uint32 dos_mode_msdfs(connection_struct *conn, const char *path, const SMB_STRUCT_STAT *psbuf)
318 {
319         SMB_STRUCT_STAT sbuf = *psbuf;
320         uint32 result = 0;
321
322         DEBUG(8,("dos_mode_msdfs: %s\n", path));
323
324         if (!VALID_STAT(sbuf)) {
325                 return 0;
326         }
327
328         /* First do any modifications that depend on the path name. */
329         /* hide files with a name starting with a . */
330         if (lp_hide_dot_files(SNUM(conn))) {
331                 const char *p = strrchr_m(path,'/');
332                 if (p) {
333                         p++;
334                 } else {
335                         p = path;
336                 }
337
338                 /* Only . and .. are not hidden. */
339                 if (p[0] == '.' && !((p[1] == '\0') ||
340                                 (p[1] == '.' && p[2] == '\0'))) {
341                         result |= aHIDDEN;
342                 }
343         }
344
345         result |= dos_mode_from_sbuf(conn, path, &sbuf);
346
347         /* Optimization : Only call is_hidden_path if it's not already
348            hidden. */
349         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
350                 result |= aHIDDEN;
351         }
352
353         DEBUG(8,("dos_mode_msdfs returning "));
354
355         if (result & aHIDDEN) DEBUG(8, ("h"));
356         if (result & aRONLY ) DEBUG(8, ("r"));
357         if (result & aSYSTEM) DEBUG(8, ("s"));
358         if (result & aDIR   ) DEBUG(8, ("d"));
359         if (result & aARCH  ) DEBUG(8, ("a"));
360         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
361
362         DEBUG(8,("\n"));
363
364         return(result);
365 }
366
367 #ifdef HAVE_STAT_DOS_FLAGS
368 /****************************************************************************
369  Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
370 ****************************************************************************/
371
372 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
373 {
374         uint32_t dos_stat_flags = 0;
375
376         if (dosmode & aARCH)
377                 dos_stat_flags |= UF_DOS_ARCHIVE;
378         if (dosmode & aHIDDEN)
379                 dos_stat_flags |= UF_DOS_HIDDEN;
380         if (dosmode & aRONLY)
381                 dos_stat_flags |= UF_DOS_RO;
382         if (dosmode & aSYSTEM)
383                 dos_stat_flags |= UF_DOS_SYSTEM;
384         if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
385                 dos_stat_flags |= UF_DOS_NOINDEX;
386
387         return dos_stat_flags;
388 }
389
390 /****************************************************************************
391  Gets DOS attributes, accessed via st_ex_flags in the stat struct.
392 ****************************************************************************/
393
394 static bool get_stat_dos_flags(connection_struct *conn,
395                                const char *fname,
396                                const SMB_STRUCT_STAT *sbuf,
397                                uint32_t *dosmode)
398 {
399         SMB_ASSERT(sbuf && VALID_STAT(*sbuf));
400         SMB_ASSERT(dosmode);
401
402         if (!lp_store_dos_attributes(SNUM(conn))) {
403                 return false;
404         }
405
406         DEBUG(5, ("Getting stat dos attributes for %s.\n", fname));
407
408         if (sbuf->st_ex_flags & UF_DOS_ARCHIVE)
409                 *dosmode |= aARCH;
410         if (sbuf->st_ex_flags & UF_DOS_HIDDEN)
411                 *dosmode |= aHIDDEN;
412         if (sbuf->st_ex_flags & UF_DOS_RO)
413                 *dosmode |= aRONLY;
414         if (sbuf->st_ex_flags & UF_DOS_SYSTEM)
415                 *dosmode |= aSYSTEM;
416         if (sbuf->st_ex_flags & UF_DOS_NOINDEX)
417                 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
418         if (S_ISDIR(sbuf->st_ex_mode))
419                 *dosmode |= aDIR;
420
421         *dosmode |= set_sparse_flag(sbuf);
422         *dosmode |= set_link_read_only_flag(sbuf);
423
424         return true;
425 }
426
427 /****************************************************************************
428  Sets DOS attributes, stored in st_ex_flags of the inode.
429 ****************************************************************************/
430
431 static bool set_stat_dos_flags(connection_struct *conn,
432                                const struct smb_filename *smb_fname,
433                                uint32_t dosmode,
434                                bool *attributes_changed)
435 {
436         uint32_t new_flags = 0;
437         int error = 0;
438
439         SMB_ASSERT(VALID_STAT(smb_fname->st));
440         SMB_ASSERT(attributes_changed);
441
442         *attributes_changed = false;
443
444         if (!lp_store_dos_attributes(SNUM(conn))) {
445                 return false;
446         }
447
448         DEBUG(5, ("Setting stat dos attributes for %s.\n",
449                   smb_fname_str_dbg(smb_fname)));
450
451         new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
452                      dos_attributes_to_stat_dos_flags(dosmode);
453
454         /* Return early if no flags changed. */
455         if (new_flags == smb_fname->st.st_ex_flags)
456                 return true;
457
458         DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
459                   smb_fname->st.st_ex_flags));
460
461         /* Set new flags with chflags. */
462         error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
463         if (error) {
464                 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
465                           "file %s! errno=%d\n", new_flags,
466                           smb_fname_str_dbg(smb_fname), errno));
467                 return false;
468         }
469
470         *attributes_changed = true;
471         return true;
472 }
473 #endif /* HAVE_STAT_DOS_FLAGS */
474
475 /****************************************************************************
476  Change a unix mode to a dos mode.
477 ****************************************************************************/
478
479 uint32 dos_mode(connection_struct *conn, const char *path, const SMB_STRUCT_STAT *psbuf)
480 {
481         SMB_STRUCT_STAT sbuf = *psbuf;
482         uint32 result = 0;
483         bool offline, used_stat_dos_flags = false;
484
485         DEBUG(8,("dos_mode: %s\n", path));
486
487         if (!VALID_STAT(sbuf)) {
488                 return 0;
489         }
490
491         /* First do any modifications that depend on the path name. */
492         /* hide files with a name starting with a . */
493         if (lp_hide_dot_files(SNUM(conn))) {
494                 const char *p = strrchr_m(path,'/');
495                 if (p) {
496                         p++;
497                 } else {
498                         p = path;
499                 }
500
501                 /* Only . and .. are not hidden. */
502                 if (p[0] == '.' && !((p[1] == '\0') ||
503                                 (p[1] == '.' && p[2] == '\0'))) {
504                         result |= aHIDDEN;
505                 }
506         }
507
508 #ifdef HAVE_STAT_DOS_FLAGS
509         used_stat_dos_flags = get_stat_dos_flags(conn, path, &sbuf, &result);
510 #endif
511         if (!used_stat_dos_flags) {
512                 /* Get the DOS attributes from an EA by preference. */
513                 if (get_ea_dos_attribute(conn, path, &sbuf, &result)) {
514                         result |= set_sparse_flag(&sbuf);
515                 } else {
516                         result |= dos_mode_from_sbuf(conn, path, &sbuf);
517                 }
518         }
519
520         offline = SMB_VFS_IS_OFFLINE(conn, path, &sbuf);
521         if (S_ISREG(sbuf.st_ex_mode) && offline) {
522                 result |= FILE_ATTRIBUTE_OFFLINE;
523         }
524
525         /* Optimization : Only call is_hidden_path if it's not already
526            hidden. */
527         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
528                 result |= aHIDDEN;
529         }
530
531         DEBUG(8,("dos_mode returning "));
532
533         if (result & aHIDDEN) DEBUG(8, ("h"));
534         if (result & aRONLY ) DEBUG(8, ("r"));
535         if (result & aSYSTEM) DEBUG(8, ("s"));
536         if (result & aDIR   ) DEBUG(8, ("d"));
537         if (result & aARCH  ) DEBUG(8, ("a"));
538         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
539
540         DEBUG(8,("\n"));
541
542         return(result);
543 }
544
545 /*******************************************************************
546  chmod a file - but preserve some bits.
547 ********************************************************************/
548
549 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
550                      uint32 dosmode, const char *parent_dir, bool newfile)
551 {
552         int mask=0;
553         mode_t tmp;
554         mode_t unixmode;
555         int ret = -1, lret = -1;
556         uint32_t old_mode;
557         char *fname = NULL;
558         NTSTATUS status;
559
560         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
561         dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
562
563         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
564                   dosmode, smb_fname_str_dbg(smb_fname)));
565
566         if (!VALID_STAT(smb_fname->st)) {
567                 if (SMB_VFS_STAT(conn, smb_fname))
568                         return(-1);
569         }
570
571         unixmode = smb_fname->st.st_ex_mode;
572
573         get_acl_group_bits(conn, smb_fname->base_name,
574                            &smb_fname->st.st_ex_mode);
575
576         if (S_ISDIR(smb_fname->st.st_ex_mode))
577                 dosmode |= aDIR;
578         else
579                 dosmode &= ~aDIR;
580
581         status = get_full_smb_filename(talloc_tos(), smb_fname, &fname);
582         if (!NT_STATUS_IS_OK(status)) {
583                 errno = map_errno_from_nt_status(status);
584                 return -1;
585         }
586
587         old_mode = dos_mode(conn, fname, &smb_fname->st);
588
589         if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
590                 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
591                         lret = SMB_VFS_SET_OFFLINE(conn, fname);
592                         if (lret == -1) {
593                                 DEBUG(0, ("set_dos_mode: client has asked to "
594                                           "set FILE_ATTRIBUTE_OFFLINE to "
595                                           "%s/%s but there was an error while "
596                                           "setting it or it is not "
597                                           "supported.\n", parent_dir,
598                                           smb_fname_str_dbg(smb_fname)));
599                         }
600                 }
601         }
602
603         dosmode  &= ~FILE_ATTRIBUTE_OFFLINE;
604         old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
605
606         if (old_mode == dosmode) {
607                 smb_fname->st.st_ex_mode = unixmode;
608                 return(0);
609         }
610
611 #ifdef HAVE_STAT_DOS_FLAGS
612         {
613                 bool attributes_changed;
614
615                 if (set_stat_dos_flags(conn, smb_fname, dosmode,
616                                        &attributes_changed))
617                 {
618                         if (!newfile && attributes_changed) {
619                                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
620                                     FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
621                         }
622                         smb_fname->st.st_ex_mode = unixmode;
623                         return 0;
624                 }
625         }
626 #endif
627
628         /* Store the DOS attributes in an EA by preference. */
629         if (set_ea_dos_attribute(conn, fname, &smb_fname->st, dosmode)) {
630                 if (!newfile) {
631                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
632                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
633                 }
634                 smb_fname->st.st_ex_mode = unixmode;
635                 return 0;
636         }
637
638         unixmode = unix_mode(conn,dosmode,fname, parent_dir);
639
640         /* preserve the s bits */
641         mask |= (S_ISUID | S_ISGID);
642
643         /* preserve the t bit */
644 #ifdef S_ISVTX
645         mask |= S_ISVTX;
646 #endif
647
648         /* possibly preserve the x bits */
649         if (!MAP_ARCHIVE(conn))
650                 mask |= S_IXUSR;
651         if (!MAP_SYSTEM(conn))
652                 mask |= S_IXGRP;
653         if (!MAP_HIDDEN(conn))
654                 mask |= S_IXOTH;
655
656         unixmode |= (smb_fname->st.st_ex_mode & mask);
657
658         /* if we previously had any r bits set then leave them alone */
659         if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
660                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
661                 unixmode |= tmp;
662         }
663
664         /* if we previously had any w bits set then leave them alone 
665                 whilst adding in the new w bits, if the new mode is not rdonly */
666         if (!IS_DOS_READONLY(dosmode)) {
667                 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
668         }
669
670         ret = SMB_VFS_CHMOD(conn, fname, unixmode);
671         if (ret == 0) {
672                 if(!newfile || (lret != -1)) {
673                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
674                                      FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
675                 }
676                 smb_fname->st.st_ex_mode = unixmode;
677                 return 0;
678         }
679
680         if((errno != EPERM) && (errno != EACCES))
681                 return -1;
682
683         if(!lp_dos_filemode(SNUM(conn)))
684                 return -1;
685
686         /* We want DOS semantics, ie allow non owner with write permission to change the
687                 bits on a file. Just like file_ntimes below.
688         */
689
690         /* Check if we have write access. */
691         if (CAN_WRITE(conn)) {
692                 /*
693                  * We need to open the file with write access whilst
694                  * still in our current user context. This ensures we
695                  * are not violating security in doing the fchmod.
696                  * This file open does *not* break any oplocks we are
697                  * holding. We need to review this.... may need to
698                  * break batch oplocks open by others. JRA.
699                  */
700                 files_struct *fsp;
701                 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, fname,
702                                      &smb_fname->st, &fsp)))
703                         return -1;
704                 become_root();
705                 ret = SMB_VFS_FCHMOD(fsp, unixmode);
706                 unbecome_root();
707                 close_file_fchmod(NULL, fsp);
708                 if (!newfile) {
709                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
710                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
711                 }
712                 if (ret == 0) {
713                         smb_fname->st.st_ex_mode = unixmode;
714                 }
715         }
716
717         return( ret );
718 }
719
720 /*******************************************************************
721  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
722  than POSIX.
723 *******************************************************************/
724
725 int file_ntimes(connection_struct *conn, const char *fname,
726                 struct smb_file_time *ft, const SMB_STRUCT_STAT *psbuf)
727 {
728         struct smb_filename *smb_fname = NULL;
729         NTSTATUS status;
730         int ret = -1;
731
732         errno = 0;
733
734         DEBUG(6, ("file_ntime: actime: %s",
735                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
736         DEBUG(6, ("file_ntime: modtime: %s",
737                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
738         DEBUG(6, ("file_ntime: createtime: %s",
739                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
740
741         /* Don't update the time on read-only shares */
742         /* We need this as set_filetime (which can be called on
743            close and other paths) can end up calling this function
744            without the NEED_WRITE protection. Found by : 
745            Leo Weppelman <leo@wau.mis.ah.nl>
746         */
747
748         if (!CAN_WRITE(conn)) {
749                 return 0;
750         }
751
752         if(SMB_VFS_NTIMES(conn, fname, ft) == 0) {
753                 return 0;
754         }
755
756         if((errno != EPERM) && (errno != EACCES)) {
757                 return -1;
758         }
759
760         if(!lp_dos_filetimes(SNUM(conn))) {
761                 return -1;
762         }
763
764         /* We have permission (given by the Samba admin) to
765            break POSIX semantics and allow a user to change
766            the time on a file they don't own but can write to
767            (as DOS does).
768          */
769
770         status = create_synthetic_smb_fname_split(talloc_tos(), fname, psbuf,
771                                                   &smb_fname);
772
773         if (!NT_STATUS_IS_OK(status)) {
774                 return -1;
775         }
776
777         /* Check if we have write access. */
778         if (can_write_to_file(conn, smb_fname)) {
779                 /* We are allowed to become root and change the filetime. */
780                 become_root();
781                 ret = SMB_VFS_NTIMES(conn, fname, ft);
782                 unbecome_root();
783         }
784         TALLOC_FREE(smb_fname);
785
786         return ret;
787 }
788
789 /******************************************************************
790  Force a "sticky" write time on a pathname. This will always be
791  returned on all future write time queries and set on close.
792 ******************************************************************/
793
794 bool set_sticky_write_time_path(const struct file_id fileid,
795                                 const struct timespec mtime)
796 {
797         if (null_timespec(mtime)) {
798                 return true;
799         }
800
801         if (!set_sticky_write_time(fileid, mtime)) {
802                 return false;
803         }
804
805         return true;
806 }
807
808 /******************************************************************
809  Force a "sticky" write time on an fsp. This will always be
810  returned on all future write time queries and set on close.
811 ******************************************************************/
812
813 bool set_sticky_write_time_fsp(struct files_struct *fsp, const struct timespec mtime)
814 {
815         fsp->write_time_forced = true;
816         TALLOC_FREE(fsp->update_write_time_event);
817
818         return set_sticky_write_time_path(fsp->file_id, mtime);
819 }
820
821 /******************************************************************
822  Update a write time immediately, without the 2 second delay.
823 ******************************************************************/
824
825 bool update_write_time(struct files_struct *fsp)
826 {
827         if (!set_write_time(fsp->file_id, timespec_current())) {
828                 return false;
829         }
830
831         notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
832                         FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name);
833
834         return true;
835 }