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