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