9a3470312fa51b8e1e2f167c8c9eca84a4fea234
[ira/wip.git] / source3 / smbd / dosmode.c
1 /* 
2    Unix SMB/CIFS implementation.
3    dos mode handling functions
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) James Peach 2006
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22
23 static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
24 {
25 #if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
26         if (sbuf->st_ex_size > sbuf->st_ex_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
27                 return FILE_ATTRIBUTE_SPARSE;
28         }
29 #endif
30         return 0;
31 }
32
33 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
34 {
35 #ifdef S_ISLNK
36 #if LINKS_READ_ONLY
37         if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
38                 return aRONLY;
39 #endif
40 #endif
41         return 0;
42 }
43
44 /****************************************************************************
45  Change a dos mode to a unix mode.
46     Base permission for files:
47          if creating file and inheriting (i.e. parent_dir != NULL)
48            apply read/write bits from parent directory.
49          else   
50            everybody gets read bit set
51          dos readonly is represented in unix by removing everyone's write bit
52          dos archive is represented in unix by the user's execute bit
53          dos system is represented in unix by the group's execute bit
54          dos hidden is represented in unix by the other's execute bit
55          if !inheriting {
56            Then apply create mask,
57            then add force bits.
58          }
59     Base permission for directories:
60          dos directory is represented in unix by unix's dir bit and the exec bit
61          if !inheriting {
62            Then apply create mask,
63            then add force bits.
64          }
65 ****************************************************************************/
66
67 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
68                  const char *inherit_from_dir)
69 {
70         mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
71         mode_t dir_mode = 0; /* Mode of the inherit_from directory if
72                               * inheriting. */
73
74         if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
75                 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
76         }
77
78         if (fname && (inherit_from_dir != NULL)
79             && lp_inherit_perms(SNUM(conn))) {
80                 SMB_STRUCT_STAT sbuf;
81
82                 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
83                           inherit_from_dir));
84                 if (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_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_flags & UF_DOS_ARCHIVE)
400                 *dosmode |= aARCH;
401         if (sbuf->st_flags & UF_DOS_HIDDEN)
402                 *dosmode |= aHIDDEN;
403         if (sbuf->st_flags & UF_DOS_RO)
404                 *dosmode |= aRONLY;
405         if (sbuf->st_flags & UF_DOS_SYSTEM)
406                 *dosmode |= aSYSTEM;
407         if (sbuf->st_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_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_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_flags)
447                 return true;
448
449         DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
450                   sbuf->st_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         
510         offline = SMB_VFS_IS_OFFLINE(conn, path, sbuf);
511         if (S_ISREG(sbuf->st_ex_mode) && offline) {
512                 result |= FILE_ATTRIBUTE_OFFLINE;
513         }
514
515         /* Optimization : Only call is_hidden_path if it's not already
516            hidden. */
517         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
518                 result |= aHIDDEN;
519         }
520
521         DEBUG(8,("dos_mode returning "));
522
523         if (result & aHIDDEN) DEBUG(8, ("h"));
524         if (result & aRONLY ) DEBUG(8, ("r"));
525         if (result & aSYSTEM) DEBUG(8, ("s"));
526         if (result & aDIR   ) DEBUG(8, ("d"));
527         if (result & aARCH  ) DEBUG(8, ("a"));
528         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
529         
530         DEBUG(8,("\n"));
531
532         return(result);
533 }
534
535 /*******************************************************************
536  chmod a file - but preserve some bits.
537 ********************************************************************/
538
539 int file_set_dosmode(connection_struct *conn, const char *fname,
540                      uint32 dosmode, SMB_STRUCT_STAT *st,
541                      const char *parent_dir,
542                      bool newfile)
543 {
544         SMB_STRUCT_STAT st1;
545         int mask=0;
546         mode_t tmp;
547         mode_t unixmode;
548         int ret = -1, lret = -1;
549         uint32_t old_mode;
550
551         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
552         dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
553
554         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
555
556         if (st == NULL) {
557                 SET_STAT_INVALID(st1);
558                 st = &st1;
559         }
560
561         if (!VALID_STAT(*st)) {
562                 if (SMB_VFS_STAT(conn,fname,st))
563                         return(-1);
564         }
565
566         unixmode = st->st_ex_mode;
567
568         get_acl_group_bits(conn, fname, &st->st_ex_mode);
569
570         if (S_ISDIR(st->st_ex_mode))
571                 dosmode |= aDIR;
572         else
573                 dosmode &= ~aDIR;
574
575         old_mode = dos_mode(conn,fname,st);
576         
577         if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
578                 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
579                         lret = SMB_VFS_SET_OFFLINE(conn, fname);
580                         if (lret == -1) {
581                                 DEBUG(0, ("set_dos_mode: client has asked to set "
582                                           "FILE_ATTRIBUTE_OFFLINE to %s/%s but there was "
583                                           "an error while setting it or it is not supported.\n",
584                                           parent_dir, fname));
585                         }
586                 }
587         }
588
589         dosmode  &= ~FILE_ATTRIBUTE_OFFLINE;
590         old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
591
592         if (old_mode == dosmode) {
593                 st->st_ex_mode = unixmode;
594                 return(0);
595         }
596
597 #ifdef HAVE_STAT_DOS_FLAGS
598         {
599                 bool attributes_changed;
600
601                 if (set_stat_dos_flags(conn, fname, st, dosmode,
602                                        &attributes_changed))
603                 {
604                         if (!newfile && attributes_changed) {
605                                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
606                                     FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
607                         }
608                         st->st_ex_mode = unixmode;
609                         return 0;
610                 }
611         }
612 #endif
613
614         /* Store the DOS attributes in an EA by preference. */
615         if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
616                 if (!newfile) {
617                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
618                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
619                 }
620                 st->st_ex_mode = unixmode;
621                 return 0;
622         }
623
624         unixmode = unix_mode(conn,dosmode,fname, parent_dir);
625
626         /* preserve the s bits */
627         mask |= (S_ISUID | S_ISGID);
628
629         /* preserve the t bit */
630 #ifdef S_ISVTX
631         mask |= S_ISVTX;
632 #endif
633
634         /* possibly preserve the x bits */
635         if (!MAP_ARCHIVE(conn))
636                 mask |= S_IXUSR;
637         if (!MAP_SYSTEM(conn))
638                 mask |= S_IXGRP;
639         if (!MAP_HIDDEN(conn))
640                 mask |= S_IXOTH;
641
642         unixmode |= (st->st_ex_mode & mask);
643
644         /* if we previously had any r bits set then leave them alone */
645         if ((tmp = st->st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
646                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
647                 unixmode |= tmp;
648         }
649
650         /* if we previously had any w bits set then leave them alone 
651                 whilst adding in the new w bits, if the new mode is not rdonly */
652         if (!IS_DOS_READONLY(dosmode)) {
653                 unixmode |= (st->st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
654         }
655
656         ret = SMB_VFS_CHMOD(conn, fname, unixmode);
657         if (ret == 0) {
658                 if(!newfile || (lret != -1)) {
659                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
660                                      FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
661                 }
662                 st->st_ex_mode = unixmode;
663                 return 0;
664         }
665
666         if((errno != EPERM) && (errno != EACCES))
667                 return -1;
668
669         if(!lp_dos_filemode(SNUM(conn)))
670                 return -1;
671
672         /* We want DOS semantics, ie allow non owner with write permission to change the
673                 bits on a file. Just like file_ntimes below.
674         */
675
676         /* Check if we have write access. */
677         if (CAN_WRITE(conn)) {
678                 /*
679                  * We need to open the file with write access whilst
680                  * still in our current user context. This ensures we
681                  * are not violating security in doing the fchmod.
682                  * This file open does *not* break any oplocks we are
683                  * holding. We need to review this.... may need to
684                  * break batch oplocks open by others. JRA.
685                  */
686                 files_struct *fsp;
687                 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, fname, st,
688                                                       &fsp)))
689                         return -1;
690                 become_root();
691                 ret = SMB_VFS_FCHMOD(fsp, unixmode);
692                 unbecome_root();
693                 close_file_fchmod(NULL, fsp);
694                 if (!newfile) {
695                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
696                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
697                 }
698                 if (ret == 0) {
699                         st->st_ex_mode = unixmode;
700                 }
701         }
702
703         return( ret );
704 }
705
706 /*******************************************************************
707  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
708  than POSIX.
709 *******************************************************************/
710
711 int file_ntimes(connection_struct *conn, const char *fname,
712                 struct smb_file_time *ft)
713 {
714         SMB_STRUCT_STAT sbuf;
715         int ret = -1;
716
717         errno = 0;
718         ZERO_STRUCT(sbuf);
719
720         DEBUG(6, ("file_ntime: actime: %s",
721                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
722         DEBUG(6, ("file_ntime: modtime: %s",
723                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
724         DEBUG(6, ("file_ntime: createtime: %s",
725                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
726
727         /* Don't update the time on read-only shares */
728         /* We need this as set_filetime (which can be called on
729            close and other paths) can end up calling this function
730            without the NEED_WRITE protection. Found by : 
731            Leo Weppelman <leo@wau.mis.ah.nl>
732         */
733
734         if (!CAN_WRITE(conn)) {
735                 return 0;
736         }
737
738         if(SMB_VFS_NTIMES(conn, fname, ft) == 0) {
739                 return 0;
740         }
741
742         if((errno != EPERM) && (errno != EACCES)) {
743                 return -1;
744         }
745
746         if(!lp_dos_filetimes(SNUM(conn))) {
747                 return -1;
748         }
749
750         /* We have permission (given by the Samba admin) to
751            break POSIX semantics and allow a user to change
752            the time on a file they don't own but can write to
753            (as DOS does).
754          */
755
756         /* Check if we have write access. */
757         if (can_write_to_file(conn, fname, &sbuf)) {
758                 /* We are allowed to become root and change the filetime. */
759                 become_root();
760                 ret = SMB_VFS_NTIMES(conn, fname, ft);
761                 unbecome_root();
762         }
763
764         return ret;
765 }
766
767 /******************************************************************
768  Force a "sticky" write time on a pathname. This will always be
769  returned on all future write time queries and set on close.
770 ******************************************************************/
771
772 bool set_sticky_write_time_path(connection_struct *conn, const char *fname,
773                          struct file_id fileid, const struct timespec mtime)
774 {
775         if (null_timespec(mtime)) {
776                 return true;
777         }
778
779         if (!set_sticky_write_time(fileid, mtime)) {
780                 return false;
781         }
782
783         return true;
784 }
785
786 /******************************************************************
787  Force a "sticky" write time on an fsp. This will always be
788  returned on all future write time queries and set on close.
789 ******************************************************************/
790
791 bool set_sticky_write_time_fsp(struct files_struct *fsp, const struct timespec mtime)
792 {
793         fsp->write_time_forced = true;
794         TALLOC_FREE(fsp->update_write_time_event);
795
796         return set_sticky_write_time_path(fsp->conn, fsp->fsp_name,
797                         fsp->file_id, mtime);
798 }
799
800 /******************************************************************
801  Update a write time immediately, without the 2 second delay.
802 ******************************************************************/
803
804 bool update_write_time(struct files_struct *fsp)
805 {
806         if (!set_write_time(fsp->file_id, timespec_current())) {
807                 return false;
808         }
809
810         notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
811                         FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name);
812
813         return true;
814 }