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