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