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