e5d6bc32e855537a4b907c990ac82a431ba57011
[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 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         if (old_mode == dosmode &&
743                         (timespec_compare(&new_create_timespec,
744                                 &smb_fname->st.st_ex_btime) == 0)) {
745                 smb_fname->st.st_ex_mode = unixmode;
746                 return(0);
747         }
748
749         smb_fname->st.st_ex_btime = new_create_timespec;
750
751 #ifdef HAVE_STAT_DOS_FLAGS
752         {
753                 bool attributes_changed;
754
755                 if (set_stat_dos_flags(conn, smb_fname, dosmode,
756                                        &attributes_changed))
757                 {
758                         if (!newfile && attributes_changed) {
759                                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
760                                     FILE_NOTIFY_CHANGE_ATTRIBUTES,
761                                     smb_fname->base_name);
762                         }
763                         smb_fname->st.st_ex_mode = unixmode;
764                         return 0;
765                 }
766         }
767 #endif
768         /* Store the DOS attributes in an EA by preference. */
769         if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
770                 if (!newfile) {
771                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
772                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
773                                      smb_fname->base_name);
774                 }
775                 smb_fname->st.st_ex_mode = unixmode;
776                 return 0;
777         }
778
779         unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
780
781         /* preserve the s bits */
782         mask |= (S_ISUID | S_ISGID);
783
784         /* preserve the t bit */
785 #ifdef S_ISVTX
786         mask |= S_ISVTX;
787 #endif
788
789         /* possibly preserve the x bits */
790         if (!MAP_ARCHIVE(conn))
791                 mask |= S_IXUSR;
792         if (!MAP_SYSTEM(conn))
793                 mask |= S_IXGRP;
794         if (!MAP_HIDDEN(conn))
795                 mask |= S_IXOTH;
796
797         unixmode |= (smb_fname->st.st_ex_mode & mask);
798
799         /* if we previously had any r bits set then leave them alone */
800         if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
801                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
802                 unixmode |= tmp;
803         }
804
805         /* if we previously had any w bits set then leave them alone 
806                 whilst adding in the new w bits, if the new mode is not rdonly */
807         if (!IS_DOS_READONLY(dosmode)) {
808                 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
809         }
810
811         ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
812         if (ret == 0) {
813                 if(!newfile || (lret != -1)) {
814                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
815                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
816                                      smb_fname->base_name);
817                 }
818                 smb_fname->st.st_ex_mode = unixmode;
819                 return 0;
820         }
821
822         if((errno != EPERM) && (errno != EACCES))
823                 return -1;
824
825         if(!lp_dos_filemode(SNUM(conn)))
826                 return -1;
827
828         /* We want DOS semantics, ie allow non owner with write permission to change the
829                 bits on a file. Just like file_ntimes below.
830         */
831
832         /* Check if we have write access. */
833         if (CAN_WRITE(conn)) {
834                 /*
835                  * We need to open the file with write access whilst
836                  * still in our current user context. This ensures we
837                  * are not violating security in doing the fchmod.
838                  * This file open does *not* break any oplocks we are
839                  * holding. We need to review this.... may need to
840                  * break batch oplocks open by others. JRA.
841                  */
842                 files_struct *fsp;
843                 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
844                                      &fsp)))
845                         return -1;
846                 become_root();
847                 ret = SMB_VFS_FCHMOD(fsp, unixmode);
848                 unbecome_root();
849                 close_file_fchmod(NULL, fsp);
850                 if (!newfile) {
851                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
852                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
853                                      smb_fname->base_name);
854                 }
855                 if (ret == 0) {
856                         smb_fname->st.st_ex_mode = unixmode;
857                 }
858         }
859
860         return( ret );
861 }
862
863 /*******************************************************************
864  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
865  than POSIX.
866 *******************************************************************/
867
868 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
869                 struct smb_file_time *ft)
870 {
871         int ret = -1;
872
873         errno = 0;
874
875         DEBUG(6, ("file_ntime: actime: %s",
876                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
877         DEBUG(6, ("file_ntime: modtime: %s",
878                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
879         DEBUG(6, ("file_ntime: ctime: %s",
880                   time_to_asc(convert_timespec_to_time_t(ft->ctime))));
881         DEBUG(6, ("file_ntime: createtime: %s",
882                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
883
884         /* Don't update the time on read-only shares */
885         /* We need this as set_filetime (which can be called on
886            close and other paths) can end up calling this function
887            without the NEED_WRITE protection. Found by : 
888            Leo Weppelman <leo@wau.mis.ah.nl>
889         */
890
891         if (!CAN_WRITE(conn)) {
892                 return 0;
893         }
894
895         if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
896                 return 0;
897         }
898
899         if((errno != EPERM) && (errno != EACCES)) {
900                 return -1;
901         }
902
903         if(!lp_dos_filetimes(SNUM(conn))) {
904                 return -1;
905         }
906
907         /* We have permission (given by the Samba admin) to
908            break POSIX semantics and allow a user to change
909            the time on a file they don't own but can write to
910            (as DOS does).
911          */
912
913         /* Check if we have write access. */
914         if (can_write_to_file(conn, smb_fname)) {
915                 /* We are allowed to become root and change the filetime. */
916                 become_root();
917                 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
918                 unbecome_root();
919         }
920
921         return ret;
922 }
923
924 /******************************************************************
925  Force a "sticky" write time on a pathname. This will always be
926  returned on all future write time queries and set on close.
927 ******************************************************************/
928
929 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
930 {
931         if (null_timespec(mtime)) {
932                 return true;
933         }
934
935         if (!set_sticky_write_time(fileid, mtime)) {
936                 return false;
937         }
938
939         return true;
940 }
941
942 /******************************************************************
943  Force a "sticky" write time on an fsp. This will always be
944  returned on all future write time queries and set on close.
945 ******************************************************************/
946
947 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
948 {
949         if (null_timespec(mtime)) {
950                 return true;
951         }
952
953         fsp->write_time_forced = true;
954         TALLOC_FREE(fsp->update_write_time_event);
955
956         return set_sticky_write_time_path(fsp->file_id, mtime);
957 }
958
959 /******************************************************************
960  Set a create time EA.
961 ******************************************************************/
962
963 NTSTATUS set_create_timespec_ea(connection_struct *conn,
964                                 const struct smb_filename *psmb_fname,
965                                 struct timespec create_time)
966 {
967         NTSTATUS status;
968         struct smb_filename *smb_fname = NULL;
969         uint32_t dosmode;
970         int ret;
971
972         if (!lp_store_dos_attributes(SNUM(conn))) {
973                 return NT_STATUS_OK;
974         }
975
976         status = create_synthetic_smb_fname(talloc_tos(),
977                                 psmb_fname->base_name,
978                                 NULL, &psmb_fname->st,
979                                 &smb_fname);
980
981         if (!NT_STATUS_IS_OK(status)) {
982                 return status;
983         }
984
985         dosmode = dos_mode(conn, smb_fname);
986
987         smb_fname->st.st_ex_btime = create_time;
988
989         ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
990         if (ret == -1) {
991                 map_nt_error_from_unix(errno);
992         }
993
994         DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
995                 smb_fname_str_dbg(smb_fname)));
996
997         return NT_STATUS_OK;
998 }
999
1000 /******************************************************************
1001  Return a create time.
1002 ******************************************************************/
1003
1004 struct timespec get_create_timespec(connection_struct *conn,
1005                                 struct files_struct *fsp,
1006                                 const struct smb_filename *smb_fname)
1007 {
1008         return smb_fname->st.st_ex_btime;
1009 }
1010
1011 /******************************************************************
1012  Return a change time (may look at EA in future).
1013 ******************************************************************/
1014
1015 struct timespec get_change_timespec(connection_struct *conn,
1016                                 struct files_struct *fsp,
1017                                 const struct smb_filename *smb_fname)
1018 {
1019         return smb_fname->st.st_ex_mtime;
1020 }