afebb9efd0e2efa933a8e0e20762bd4867118b8b
[ira/wip.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                 /* Check actual permissions for read-only. */
155                 if (!can_write_to_file(conn, path, sbuf)) {
156                         result |= aRONLY;
157                 }
158         } /* Else never set the readonly bit. */
159
160         if (MAP_ARCHIVE(conn) && ((sbuf->st_ex_mode & S_IXUSR) != 0))
161                 result |= aARCH;
162
163         if (MAP_SYSTEM(conn) && ((sbuf->st_ex_mode & S_IXGRP) != 0))
164                 result |= aSYSTEM;
165
166         if (MAP_HIDDEN(conn) && ((sbuf->st_ex_mode & S_IXOTH) != 0))
167                 result |= aHIDDEN;   
168
169         if (S_ISDIR(sbuf->st_ex_mode))
170                 result = aDIR | (result & aRONLY);
171
172         result |= set_sparse_flag(sbuf);
173         result |= set_link_read_only_flag(sbuf);
174
175         DEBUG(8,("dos_mode_from_sbuf returning "));
176
177         if (result & aHIDDEN) DEBUG(8, ("h"));
178         if (result & aRONLY ) DEBUG(8, ("r"));
179         if (result & aSYSTEM) DEBUG(8, ("s"));
180         if (result & aDIR   ) DEBUG(8, ("d"));
181         if (result & aARCH  ) DEBUG(8, ("a"));
182
183         DEBUG(8,("\n"));
184         return result;
185 }
186
187 /****************************************************************************
188  Get DOS attributes from an EA.
189 ****************************************************************************/
190
191 static bool get_ea_dos_attribute(connection_struct *conn, const char *path, const SMB_STRUCT_STAT *sbuf, uint32 *pattr)
192 {
193         ssize_t sizeret;
194         fstring attrstr;
195         unsigned int dosattr;
196
197         if (!lp_store_dos_attributes(SNUM(conn))) {
198                 return False;
199         }
200
201         /* Don't reset pattr to zero as we may already have filename-based attributes we
202            need to preserve. */
203
204         sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
205         if (sizeret == -1) {
206                 if (errno == ENOSYS
207 #if defined(ENOTSUP)
208                         || errno == ENOTSUP) {
209 #else
210                                 ) {
211 #endif
212                         DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
213                                 path, strerror(errno) ));
214                         set_store_dos_attributes(SNUM(conn), False);
215                 }
216                 return False;
217         }
218         /* Null terminate string. */
219         attrstr[sizeret] = 0;
220         DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
221
222         if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
223                         sscanf(attrstr, "%x", &dosattr) != 1) {
224                 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
225                 return False;
226         }
227
228         if (S_ISDIR(sbuf->st_ex_mode)) {
229                 dosattr |= aDIR;
230         }
231         *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
232
233         DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
234
235         if (dosattr & aHIDDEN) DEBUG(8, ("h"));
236         if (dosattr & aRONLY ) DEBUG(8, ("r"));
237         if (dosattr & aSYSTEM) DEBUG(8, ("s"));
238         if (dosattr & aDIR   ) DEBUG(8, ("d"));
239         if (dosattr & aARCH  ) DEBUG(8, ("a"));
240
241         DEBUG(8,("\n"));
242
243         return True;
244 }
245
246 /****************************************************************************
247  Set DOS attributes in an EA.
248 ****************************************************************************/
249
250 static bool set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
251 {
252         fstring attrstr;
253         files_struct *fsp = NULL;
254         bool ret = False;
255
256         if (!lp_store_dos_attributes(SNUM(conn))) {
257                 return False;
258         }
259
260         snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
261         if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
262                 if((errno != EPERM) && (errno != EACCES)) {
263                         if (errno == ENOSYS
264 #if defined(ENOTSUP)
265                                 || errno == ENOTSUP) {
266 #else
267                                 ) {
268 #endif
269                                 DEBUG(1,("set_ea_dos_attributes: Cannot set attribute EA on file %s: Error = %s\n",
270                                         path, strerror(errno) ));
271                                 set_store_dos_attributes(SNUM(conn), False);
272                         }
273                         return False;
274                 }
275
276                 /* We want DOS semantics, ie allow non owner with write permission to change the
277                         bits on a file. Just like file_ntimes below.
278                 */
279
280                 /* Check if we have write access. */
281                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
282                         return False;
283
284                 /*
285                  * We need to open the file with write access whilst
286                  * still in our current user context. This ensures we
287                  * are not violating security in doing the setxattr.
288                  */
289
290                 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, path, sbuf,
291                                                       &fsp)))
292                         return ret;
293                 become_root();
294                 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
295                         ret = True;
296                 }
297                 unbecome_root();
298                 close_file_fchmod(NULL, fsp);
299                 return ret;
300         }
301         DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
302         return True;
303 }
304
305 /****************************************************************************
306  Change a unix mode to a dos mode for an ms dfs link.
307 ****************************************************************************/
308
309 uint32 dos_mode_msdfs(connection_struct *conn, const char *path, const SMB_STRUCT_STAT *psbuf)
310 {
311         SMB_STRUCT_STAT sbuf = *psbuf;
312         uint32 result = 0;
313
314         DEBUG(8,("dos_mode_msdfs: %s\n", path));
315
316         if (!VALID_STAT(sbuf)) {
317                 return 0;
318         }
319
320         /* First do any modifications that depend on the path name. */
321         /* hide files with a name starting with a . */
322         if (lp_hide_dot_files(SNUM(conn))) {
323                 const char *p = strrchr_m(path,'/');
324                 if (p) {
325                         p++;
326                 } else {
327                         p = path;
328                 }
329
330                 /* Only . and .. are not hidden. */
331                 if (p[0] == '.' && !((p[1] == '\0') ||
332                                 (p[1] == '.' && p[2] == '\0'))) {
333                         result |= aHIDDEN;
334                 }
335         }
336
337         result |= dos_mode_from_sbuf(conn, path, &sbuf);
338
339         /* Optimization : Only call is_hidden_path if it's not already
340            hidden. */
341         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
342                 result |= aHIDDEN;
343         }
344
345         DEBUG(8,("dos_mode_msdfs returning "));
346
347         if (result & aHIDDEN) DEBUG(8, ("h"));
348         if (result & aRONLY ) DEBUG(8, ("r"));
349         if (result & aSYSTEM) DEBUG(8, ("s"));
350         if (result & aDIR   ) DEBUG(8, ("d"));
351         if (result & aARCH  ) DEBUG(8, ("a"));
352         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
353
354         DEBUG(8,("\n"));
355
356         return(result);
357 }
358
359 #ifdef HAVE_STAT_DOS_FLAGS
360 /****************************************************************************
361  Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
362 ****************************************************************************/
363
364 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
365 {
366         uint32_t dos_stat_flags = 0;
367
368         if (dosmode & aARCH)
369                 dos_stat_flags |= UF_DOS_ARCHIVE;
370         if (dosmode & aHIDDEN)
371                 dos_stat_flags |= UF_DOS_HIDDEN;
372         if (dosmode & aRONLY)
373                 dos_stat_flags |= UF_DOS_RO;
374         if (dosmode & aSYSTEM)
375                 dos_stat_flags |= UF_DOS_SYSTEM;
376         if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
377                 dos_stat_flags |= UF_DOS_NOINDEX;
378
379         return dos_stat_flags;
380 }
381
382 /****************************************************************************
383  Gets DOS attributes, accessed via st_ex_flags in the stat struct.
384 ****************************************************************************/
385
386 static bool get_stat_dos_flags(connection_struct *conn,
387                                const char *fname,
388                                const SMB_STRUCT_STAT *sbuf,
389                                uint32_t *dosmode)
390 {
391         SMB_ASSERT(sbuf && VALID_STAT(*sbuf));
392         SMB_ASSERT(dosmode);
393
394         if (!lp_store_dos_attributes(SNUM(conn))) {
395                 return false;
396         }
397
398         DEBUG(5, ("Getting stat dos attributes for %s.\n", fname));
399
400         if (sbuf->st_ex_flags & UF_DOS_ARCHIVE)
401                 *dosmode |= aARCH;
402         if (sbuf->st_ex_flags & UF_DOS_HIDDEN)
403                 *dosmode |= aHIDDEN;
404         if (sbuf->st_ex_flags & UF_DOS_RO)
405                 *dosmode |= aRONLY;
406         if (sbuf->st_ex_flags & UF_DOS_SYSTEM)
407                 *dosmode |= aSYSTEM;
408         if (sbuf->st_ex_flags & UF_DOS_NOINDEX)
409                 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
410         if (S_ISDIR(sbuf->st_ex_mode))
411                 *dosmode |= aDIR;
412
413         *dosmode |= set_sparse_flag(sbuf);
414         *dosmode |= set_link_read_only_flag(sbuf);
415
416         return true;
417 }
418
419 /****************************************************************************
420  Sets DOS attributes, stored in st_ex_flags of the inode.
421 ****************************************************************************/
422
423 static bool set_stat_dos_flags(connection_struct *conn,
424                                 const char *fname,
425                                 SMB_STRUCT_STAT *sbuf,
426                                 uint32_t dosmode,
427                                 bool *attributes_changed)
428 {
429         uint32_t new_flags = 0;
430         int error = 0;
431
432         SMB_ASSERT(sbuf && VALID_STAT(*sbuf));
433         SMB_ASSERT(attributes_changed);
434
435         *attributes_changed = false;
436
437         if (!lp_store_dos_attributes(SNUM(conn))) {
438                 return false;
439         }
440
441         DEBUG(5, ("Setting stat dos attributes for %s.\n", fname));
442
443         new_flags = (sbuf->st_ex_flags & ~UF_DOS_FLAGS) |
444                      dos_attributes_to_stat_dos_flags(dosmode);
445
446         /* Return early if no flags changed. */
447         if (new_flags == sbuf->st_ex_flags)
448                 return true;
449
450         DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
451                   sbuf->st_ex_flags));
452
453         /* Set new flags with chflags. */
454         error = SMB_VFS_CHFLAGS(conn, fname, new_flags);
455         if (error) {
456                 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
457                           "file %s! errno=%d\n", new_flags, fname, errno));
458                 return false;
459         }
460
461         *attributes_changed = true;
462         return true;
463 }
464 #endif /* HAVE_STAT_DOS_FLAGS */
465
466 /****************************************************************************
467  Change a unix mode to a dos mode.
468 ****************************************************************************/
469
470 uint32 dos_mode(connection_struct *conn, const char *path, const SMB_STRUCT_STAT *psbuf)
471 {
472         SMB_STRUCT_STAT sbuf = *psbuf;
473         uint32 result = 0;
474         bool offline, used_stat_dos_flags = false;
475
476         DEBUG(8,("dos_mode: %s\n", path));
477
478         if (!VALID_STAT(sbuf)) {
479                 return 0;
480         }
481
482         /* First do any modifications that depend on the path name. */
483         /* hide files with a name starting with a . */
484         if (lp_hide_dot_files(SNUM(conn))) {
485                 const char *p = strrchr_m(path,'/');
486                 if (p) {
487                         p++;
488                 } else {
489                         p = path;
490                 }
491
492                 /* Only . and .. are not hidden. */
493                 if (p[0] == '.' && !((p[1] == '\0') ||
494                                 (p[1] == '.' && p[2] == '\0'))) {
495                         result |= aHIDDEN;
496                 }
497         }
498
499 #ifdef HAVE_STAT_DOS_FLAGS
500         used_stat_dos_flags = get_stat_dos_flags(conn, path, &sbuf, &result);
501 #endif
502         if (!used_stat_dos_flags) {
503                 /* Get the DOS attributes from an EA by preference. */
504                 if (get_ea_dos_attribute(conn, path, &sbuf, &result)) {
505                         result |= set_sparse_flag(&sbuf);
506                 } else {
507                         result |= dos_mode_from_sbuf(conn, path, &sbuf);
508                 }
509         }
510
511         offline = SMB_VFS_IS_OFFLINE(conn, path, &sbuf);
512         if (S_ISREG(sbuf.st_ex_mode) && offline) {
513                 result |= FILE_ATTRIBUTE_OFFLINE;
514         }
515
516         /* Optimization : Only call is_hidden_path if it's not already
517            hidden. */
518         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
519                 result |= aHIDDEN;
520         }
521
522         DEBUG(8,("dos_mode returning "));
523
524         if (result & aHIDDEN) DEBUG(8, ("h"));
525         if (result & aRONLY ) DEBUG(8, ("r"));
526         if (result & aSYSTEM) DEBUG(8, ("s"));
527         if (result & aDIR   ) DEBUG(8, ("d"));
528         if (result & aARCH  ) DEBUG(8, ("a"));
529         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
530
531         DEBUG(8,("\n"));
532
533         return(result);
534 }
535
536 /*******************************************************************
537  chmod a file - but preserve some bits.
538 ********************************************************************/
539
540 int file_set_dosmode(connection_struct *conn, const char *fname,
541                      uint32 dosmode, SMB_STRUCT_STAT *st,
542                      const char *parent_dir,
543                      bool newfile)
544 {
545         SMB_STRUCT_STAT st1;
546         int mask=0;
547         mode_t tmp;
548         mode_t unixmode;
549         int ret = -1, lret = -1;
550         uint32_t old_mode;
551
552         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
553         dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
554
555         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
556
557         if (st == NULL) {
558                 SET_STAT_INVALID(st1);
559                 st = &st1;
560         }
561
562         if (!VALID_STAT(*st)) {
563                 if (vfs_stat_smb_fname(conn,fname,st))
564                         return(-1);
565         }
566
567         unixmode = st->st_ex_mode;
568
569         get_acl_group_bits(conn, fname, &st->st_ex_mode);
570
571         if (S_ISDIR(st->st_ex_mode))
572                 dosmode |= aDIR;
573         else
574                 dosmode &= ~aDIR;
575
576         old_mode = dos_mode(conn,fname,st);
577
578         if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
579                 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
580                         lret = SMB_VFS_SET_OFFLINE(conn, fname);
581                         if (lret == -1) {
582                                 DEBUG(0, ("set_dos_mode: client has asked to set "
583                                           "FILE_ATTRIBUTE_OFFLINE to %s/%s but there was "
584                                           "an error while setting it or it is not supported.\n",
585                                           parent_dir, fname));
586                         }
587                 }
588         }
589
590         dosmode  &= ~FILE_ATTRIBUTE_OFFLINE;
591         old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
592
593         if (old_mode == dosmode) {
594                 st->st_ex_mode = unixmode;
595                 return(0);
596         }
597
598 #ifdef HAVE_STAT_DOS_FLAGS
599         {
600                 bool attributes_changed;
601
602                 if (set_stat_dos_flags(conn, fname, st, dosmode,
603                                        &attributes_changed))
604                 {
605                         if (!newfile && attributes_changed) {
606                                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
607                                     FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
608                         }
609                         st->st_ex_mode = unixmode;
610                         return 0;
611                 }
612         }
613 #endif
614
615         /* Store the DOS attributes in an EA by preference. */
616         if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
617                 if (!newfile) {
618                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
619                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
620                 }
621                 st->st_ex_mode = unixmode;
622                 return 0;
623         }
624
625         unixmode = unix_mode(conn,dosmode,fname, parent_dir);
626
627         /* preserve the s bits */
628         mask |= (S_ISUID | S_ISGID);
629
630         /* preserve the t bit */
631 #ifdef S_ISVTX
632         mask |= S_ISVTX;
633 #endif
634
635         /* possibly preserve the x bits */
636         if (!MAP_ARCHIVE(conn))
637                 mask |= S_IXUSR;
638         if (!MAP_SYSTEM(conn))
639                 mask |= S_IXGRP;
640         if (!MAP_HIDDEN(conn))
641                 mask |= S_IXOTH;
642
643         unixmode |= (st->st_ex_mode & mask);
644
645         /* if we previously had any r bits set then leave them alone */
646         if ((tmp = st->st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
647                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
648                 unixmode |= tmp;
649         }
650
651         /* if we previously had any w bits set then leave them alone 
652                 whilst adding in the new w bits, if the new mode is not rdonly */
653         if (!IS_DOS_READONLY(dosmode)) {
654                 unixmode |= (st->st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
655         }
656
657         ret = SMB_VFS_CHMOD(conn, fname, unixmode);
658         if (ret == 0) {
659                 if(!newfile || (lret != -1)) {
660                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
661                                      FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
662                 }
663                 st->st_ex_mode = unixmode;
664                 return 0;
665         }
666
667         if((errno != EPERM) && (errno != EACCES))
668                 return -1;
669
670         if(!lp_dos_filemode(SNUM(conn)))
671                 return -1;
672
673         /* We want DOS semantics, ie allow non owner with write permission to change the
674                 bits on a file. Just like file_ntimes below.
675         */
676
677         /* Check if we have write access. */
678         if (CAN_WRITE(conn)) {
679                 /*
680                  * We need to open the file with write access whilst
681                  * still in our current user context. This ensures we
682                  * are not violating security in doing the fchmod.
683                  * This file open does *not* break any oplocks we are
684                  * holding. We need to review this.... may need to
685                  * break batch oplocks open by others. JRA.
686                  */
687                 files_struct *fsp;
688                 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, fname, st,
689                                                       &fsp)))
690                         return -1;
691                 become_root();
692                 ret = SMB_VFS_FCHMOD(fsp, unixmode);
693                 unbecome_root();
694                 close_file_fchmod(NULL, fsp);
695                 if (!newfile) {
696                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
697                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
698                 }
699                 if (ret == 0) {
700                         st->st_ex_mode = unixmode;
701                 }
702         }
703
704         return( ret );
705 }
706
707 /*******************************************************************
708  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
709  than POSIX.
710 *******************************************************************/
711
712 int file_ntimes(connection_struct *conn, const char *fname,
713                 struct smb_file_time *ft)
714 {
715         SMB_STRUCT_STAT sbuf;
716         int ret = -1;
717
718         errno = 0;
719         ZERO_STRUCT(sbuf);
720
721         DEBUG(6, ("file_ntime: actime: %s",
722                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
723         DEBUG(6, ("file_ntime: modtime: %s",
724                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
725         DEBUG(6, ("file_ntime: createtime: %s",
726                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
727
728         /* Don't update the time on read-only shares */
729         /* We need this as set_filetime (which can be called on
730            close and other paths) can end up calling this function
731            without the NEED_WRITE protection. Found by : 
732            Leo Weppelman <leo@wau.mis.ah.nl>
733         */
734
735         if (!CAN_WRITE(conn)) {
736                 return 0;
737         }
738
739         if(SMB_VFS_NTIMES(conn, fname, ft) == 0) {
740                 return 0;
741         }
742
743         if((errno != EPERM) && (errno != EACCES)) {
744                 return -1;
745         }
746
747         if(!lp_dos_filetimes(SNUM(conn))) {
748                 return -1;
749         }
750
751         /* We have permission (given by the Samba admin) to
752            break POSIX semantics and allow a user to change
753            the time on a file they don't own but can write to
754            (as DOS does).
755          */
756
757         /* Check if we have write access. */
758         if (can_write_to_file(conn, fname, &sbuf)) {
759                 /* We are allowed to become root and change the filetime. */
760                 become_root();
761                 ret = SMB_VFS_NTIMES(conn, fname, ft);
762                 unbecome_root();
763         }
764
765         return ret;
766 }
767
768 /******************************************************************
769  Force a "sticky" write time on a pathname. This will always be
770  returned on all future write time queries and set on close.
771 ******************************************************************/
772
773 bool set_sticky_write_time_path(connection_struct *conn, const char *fname,
774                          struct file_id fileid, const struct timespec mtime)
775 {
776         if (null_timespec(mtime)) {
777                 return true;
778         }
779
780         if (!set_sticky_write_time(fileid, mtime)) {
781                 return false;
782         }
783
784         return true;
785 }
786
787 /******************************************************************
788  Force a "sticky" write time on an fsp. This will always be
789  returned on all future write time queries and set on close.
790 ******************************************************************/
791
792 bool set_sticky_write_time_fsp(struct files_struct *fsp, const struct timespec mtime)
793 {
794         fsp->write_time_forced = true;
795         TALLOC_FREE(fsp->update_write_time_event);
796
797         return set_sticky_write_time_path(fsp->conn, fsp->fsp_name,
798                         fsp->file_id, mtime);
799 }
800
801 /******************************************************************
802  Update a write time immediately, without the 2 second delay.
803 ******************************************************************/
804
805 bool update_write_time(struct files_struct *fsp)
806 {
807         if (!set_write_time(fsp->file_id, timespec_current())) {
808                 return false;
809         }
810
811         notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
812                         FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name);
813
814         return true;
815 }