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