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