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