Ensure dos_mode can return FILE_ATTRIBUTE_NORMAL, then filter the returned attributes...
[kai/samba-autobuild/.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         DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
268                   smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
269
270         switch (dosattrib.version) {
271                 case 0xFFFF:
272                         dosattr = dosattrib.info.compatinfoFFFF.attrib;
273                         break;
274                 case 1:
275                         dosattr = dosattrib.info.info1.attrib;
276                         if (!null_nttime(dosattrib.info.info1.create_time)) {
277                                 struct timespec create_time =
278                                         nt_time_to_unix_timespec(
279                                                 &dosattrib.info.info1.create_time);
280
281                                 update_stat_ex_create_time(&smb_fname->st,
282                                                         create_time);
283
284                                 DEBUG(10,("get_ea_dos_attributes: file %s case 1 "
285                                         "set btime %s\n",
286                                         smb_fname_str_dbg(smb_fname),
287                                         time_to_asc(convert_timespec_to_time_t(
288                                                 create_time)) ));
289                         }
290                         break;
291                 case 2:
292                         dosattr = dosattrib.info.oldinfo2.attrib;
293                         /* Don't know what flags to check for this case. */
294                         break;
295                 case 3:
296                         dosattr = dosattrib.info.info3.attrib;
297                         if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
298                                         !null_nttime(dosattrib.info.info3.create_time)) {
299                                 struct timespec create_time =
300                                         nt_time_to_unix_timespec(
301                                                 &dosattrib.info.info3.create_time);
302
303                                 update_stat_ex_create_time(&smb_fname->st,
304                                                         create_time);
305
306                                 DEBUG(10,("get_ea_dos_attributes: file %s case 3 "
307                                         "set btime %s\n",
308                                         smb_fname_str_dbg(smb_fname),
309                                         time_to_asc(convert_timespec_to_time_t(
310                                                 create_time)) ));
311                         }
312                         break;
313                         default:
314                                 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on "
315                                          "file %s - %s\n", smb_fname_str_dbg(smb_fname),
316                                          attrstr));
317                         return false;
318         }
319
320         if (S_ISDIR(smb_fname->st.st_ex_mode)) {
321                 dosattr |= aDIR;
322         }
323         *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
324
325         DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
326
327         if (dosattr & aHIDDEN) DEBUG(8, ("h"));
328         if (dosattr & aRONLY ) DEBUG(8, ("r"));
329         if (dosattr & aSYSTEM) DEBUG(8, ("s"));
330         if (dosattr & aDIR   ) DEBUG(8, ("d"));
331         if (dosattr & aARCH  ) DEBUG(8, ("a"));
332
333         DEBUG(8,("\n"));
334
335         return True;
336 }
337
338 /****************************************************************************
339  Set DOS attributes in an EA.
340  Also sets the create time.
341 ****************************************************************************/
342
343 static bool set_ea_dos_attribute(connection_struct *conn,
344                                  struct smb_filename *smb_fname,
345                                  uint32 dosmode)
346 {
347         struct xattr_DOSATTRIB dosattrib;
348         enum ndr_err_code ndr_err;
349         DATA_BLOB blob;
350         files_struct *fsp = NULL;
351         bool ret = false;
352
353         if (!lp_store_dos_attributes(SNUM(conn))) {
354                 return False;
355         }
356
357         ZERO_STRUCT(dosattrib);
358         ZERO_STRUCT(blob);
359
360         dosattrib.version = 3;
361         dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
362                                         XATTR_DOSINFO_CREATE_TIME;
363         dosattrib.info.info3.attrib = dosmode;
364         unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
365                                 smb_fname->st.st_ex_btime);
366
367         ndr_err = ndr_push_struct_blob(
368                         &blob, talloc_tos(), NULL, &dosattrib,
369                         (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
370
371         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
372                 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
373                         ndr_errstr(ndr_err)));
374                 return false;
375         }
376
377         if (blob.data == NULL || blob.length == 0) {
378                 return false;
379         }
380
381         if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
382                              SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
383                              0) == -1) {
384                 if((errno != EPERM) && (errno != EACCES)) {
385                         if (errno == ENOSYS
386 #if defined(ENOTSUP)
387                                 || errno == ENOTSUP) {
388 #else
389                                 ) {
390 #endif
391                                 DEBUG(1,("set_ea_dos_attributes: Cannot set "
392                                          "attribute EA on file %s: Error = %s\n",
393                                          smb_fname_str_dbg(smb_fname),
394                                          strerror(errno) ));
395                                 set_store_dos_attributes(SNUM(conn), False);
396                         }
397                         return false;
398                 }
399
400                 /* We want DOS semantics, ie allow non owner with write permission to change the
401                         bits on a file. Just like file_ntimes below.
402                 */
403
404                 /* Check if we have write access. */
405                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
406                         return false;
407
408                 /*
409                  * We need to open the file with write access whilst
410                  * still in our current user context. This ensures we
411                  * are not violating security in doing the setxattr.
412                  */
413
414                 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
415                                                       &fsp)))
416                         return ret;
417                 become_root();
418                 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
419                                      SAMBA_XATTR_DOS_ATTRIB, blob.data,
420                                      blob.length, 0) == 0) {
421                         ret = true;
422                 }
423                 unbecome_root();
424                 close_file_fchmod(NULL, fsp);
425                 return ret;
426         }
427         DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
428                 (unsigned int)dosmode,
429                 smb_fname_str_dbg(smb_fname)));
430         return true;
431 }
432
433 /****************************************************************************
434  Change a unix mode to a dos mode for an ms dfs link.
435 ****************************************************************************/
436
437 uint32 dos_mode_msdfs(connection_struct *conn,
438                       const struct smb_filename *smb_fname)
439 {
440         uint32 result = 0;
441
442         DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
443
444         if (!VALID_STAT(smb_fname->st)) {
445                 return 0;
446         }
447
448         /* First do any modifications that depend on the path name. */
449         /* hide files with a name starting with a . */
450         if (lp_hide_dot_files(SNUM(conn))) {
451                 const char *p = strrchr_m(smb_fname->base_name, '/');
452                 if (p) {
453                         p++;
454                 } else {
455                         p = smb_fname->base_name;
456                 }
457
458                 /* Only . and .. are not hidden. */
459                 if (p[0] == '.' && !((p[1] == '\0') ||
460                                 (p[1] == '.' && p[2] == '\0'))) {
461                         result |= aHIDDEN;
462                 }
463         }
464
465         result |= dos_mode_from_sbuf(conn, smb_fname);
466
467         /* Optimization : Only call is_hidden_path if it's not already
468            hidden. */
469         if (!(result & aHIDDEN) &&
470             IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
471                 result |= aHIDDEN;
472         }
473
474         if (result == 0) {
475                 result = FILE_ATTRIBUTE_NORMAL;
476         }
477
478         result = filter_mode_by_protocol(result);
479
480         DEBUG(8,("dos_mode_msdfs returning "));
481
482         if (result & aHIDDEN) DEBUG(8, ("h"));
483         if (result & aRONLY ) DEBUG(8, ("r"));
484         if (result & aSYSTEM) DEBUG(8, ("s"));
485         if (result & aDIR   ) DEBUG(8, ("d"));
486         if (result & aARCH  ) DEBUG(8, ("a"));
487         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
488
489         DEBUG(8,("\n"));
490
491         return(result);
492 }
493
494 #ifdef HAVE_STAT_DOS_FLAGS
495 /****************************************************************************
496  Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
497 ****************************************************************************/
498
499 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
500 {
501         uint32_t dos_stat_flags = 0;
502
503         if (dosmode & aARCH)
504                 dos_stat_flags |= UF_DOS_ARCHIVE;
505         if (dosmode & aHIDDEN)
506                 dos_stat_flags |= UF_DOS_HIDDEN;
507         if (dosmode & aRONLY)
508                 dos_stat_flags |= UF_DOS_RO;
509         if (dosmode & aSYSTEM)
510                 dos_stat_flags |= UF_DOS_SYSTEM;
511         if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
512                 dos_stat_flags |= UF_DOS_NOINDEX;
513
514         return dos_stat_flags;
515 }
516
517 /****************************************************************************
518  Gets DOS attributes, accessed via st_ex_flags in the stat struct.
519 ****************************************************************************/
520
521 static bool get_stat_dos_flags(connection_struct *conn,
522                                const struct smb_filename *smb_fname,
523                                uint32_t *dosmode)
524 {
525         SMB_ASSERT(VALID_STAT(smb_fname->st));
526         SMB_ASSERT(dosmode);
527
528         if (!lp_store_dos_attributes(SNUM(conn))) {
529                 return false;
530         }
531
532         DEBUG(5, ("Getting stat dos attributes for %s.\n",
533                   smb_fname_str_dbg(smb_fname)));
534
535         if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
536                 *dosmode |= aARCH;
537         if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
538                 *dosmode |= aHIDDEN;
539         if (smb_fname->st.st_ex_flags & UF_DOS_RO)
540                 *dosmode |= aRONLY;
541         if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
542                 *dosmode |= aSYSTEM;
543         if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
544                 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
545         if (S_ISDIR(smb_fname->st.st_ex_mode))
546                 *dosmode |= aDIR;
547
548         *dosmode |= set_sparse_flag(&smb_fname->st);
549         *dosmode |= set_link_read_only_flag(&smb_fname->st);
550
551         return true;
552 }
553
554 /****************************************************************************
555  Sets DOS attributes, stored in st_ex_flags of the inode.
556 ****************************************************************************/
557
558 static bool set_stat_dos_flags(connection_struct *conn,
559                                const struct smb_filename *smb_fname,
560                                uint32_t dosmode,
561                                bool *attributes_changed)
562 {
563         uint32_t new_flags = 0;
564         int error = 0;
565
566         SMB_ASSERT(VALID_STAT(smb_fname->st));
567         SMB_ASSERT(attributes_changed);
568
569         *attributes_changed = false;
570
571         if (!lp_store_dos_attributes(SNUM(conn))) {
572                 return false;
573         }
574
575         DEBUG(5, ("Setting stat dos attributes for %s.\n",
576                   smb_fname_str_dbg(smb_fname)));
577
578         new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
579                      dos_attributes_to_stat_dos_flags(dosmode);
580
581         /* Return early if no flags changed. */
582         if (new_flags == smb_fname->st.st_ex_flags)
583                 return true;
584
585         DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
586                   smb_fname->st.st_ex_flags));
587
588         /* Set new flags with chflags. */
589         error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
590         if (error) {
591                 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
592                           "file %s! errno=%d\n", new_flags,
593                           smb_fname_str_dbg(smb_fname), errno));
594                 return false;
595         }
596
597         *attributes_changed = true;
598         return true;
599 }
600 #endif /* HAVE_STAT_DOS_FLAGS */
601
602 /****************************************************************************
603  Change a unix mode to a dos mode.
604  May also read the create timespec into the stat struct in smb_fname
605  if "store dos attributes" is true.
606 ****************************************************************************/
607
608 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
609 {
610         uint32 result = 0;
611         bool offline, used_stat_dos_flags = false;
612
613         DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
614
615         if (!VALID_STAT(smb_fname->st)) {
616                 return 0;
617         }
618
619         /* First do any modifications that depend on the path name. */
620         /* hide files with a name starting with a . */
621         if (lp_hide_dot_files(SNUM(conn))) {
622                 const char *p = strrchr_m(smb_fname->base_name,'/');
623                 if (p) {
624                         p++;
625                 } else {
626                         p = smb_fname->base_name;
627                 }
628
629                 /* Only . and .. are not hidden. */
630                 if (p[0] == '.' && !((p[1] == '\0') ||
631                                 (p[1] == '.' && p[2] == '\0'))) {
632                         result |= aHIDDEN;
633                 }
634         }
635
636 #ifdef HAVE_STAT_DOS_FLAGS
637         used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
638 #endif
639         if (!used_stat_dos_flags) {
640                 /* Get the DOS attributes from an EA by preference. */
641                 if (get_ea_dos_attribute(conn, smb_fname, &result)) {
642                         result |= set_sparse_flag(&smb_fname->st);
643                 } else {
644                         result |= dos_mode_from_sbuf(conn, smb_fname);
645                 }
646         }
647
648         offline = SMB_VFS_IS_OFFLINE(conn, smb_fname->base_name, &smb_fname->st);
649         if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
650                 result |= FILE_ATTRIBUTE_OFFLINE;
651         }
652
653         /* Optimization : Only call is_hidden_path if it's not already
654            hidden. */
655         if (!(result & aHIDDEN) &&
656             IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
657                 result |= aHIDDEN;
658         }
659
660         if (result == 0) {
661                 result = FILE_ATTRIBUTE_NORMAL;
662         }
663
664         result = filter_mode_by_protocol(result);
665
666         DEBUG(8,("dos_mode returning "));
667
668         if (result & aHIDDEN) DEBUG(8, ("h"));
669         if (result & aRONLY ) DEBUG(8, ("r"));
670         if (result & aSYSTEM) DEBUG(8, ("s"));
671         if (result & aDIR   ) DEBUG(8, ("d"));
672         if (result & aARCH  ) DEBUG(8, ("a"));
673         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
674
675         DEBUG(8,("\n"));
676
677         return(result);
678 }
679
680 /*******************************************************************
681  chmod a file - but preserve some bits.
682  If "store dos attributes" is also set it will store the create time
683  from the stat struct in smb_fname (in NTTIME format) in the EA
684  attribute also.
685 ********************************************************************/
686
687 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
688                      uint32 dosmode, const char *parent_dir, bool newfile)
689 {
690         int mask=0;
691         mode_t tmp;
692         mode_t unixmode;
693         int ret = -1, lret = -1;
694         uint32_t old_mode;
695         struct timespec new_create_timespec;
696
697         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
698         dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
699
700         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
701                   dosmode, smb_fname_str_dbg(smb_fname)));
702
703         unixmode = smb_fname->st.st_ex_mode;
704
705         get_acl_group_bits(conn, smb_fname->base_name,
706                            &smb_fname->st.st_ex_mode);
707
708         if (S_ISDIR(smb_fname->st.st_ex_mode))
709                 dosmode |= aDIR;
710         else
711                 dosmode &= ~aDIR;
712
713         new_create_timespec = smb_fname->st.st_ex_btime;
714
715         old_mode = dos_mode(conn, smb_fname);
716
717         if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
718                 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
719                         lret = SMB_VFS_SET_OFFLINE(conn, smb_fname->base_name);
720                         if (lret == -1) {
721                                 DEBUG(0, ("set_dos_mode: client has asked to "
722                                           "set FILE_ATTRIBUTE_OFFLINE to "
723                                           "%s/%s but there was an error while "
724                                           "setting it or it is not "
725                                           "supported.\n", parent_dir,
726                                           smb_fname_str_dbg(smb_fname)));
727                         }
728                 }
729         }
730
731         dosmode  &= ~FILE_ATTRIBUTE_OFFLINE;
732         old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
733
734         if (old_mode == dosmode &&
735                         (timespec_compare(&new_create_timespec,
736                                 &smb_fname->st.st_ex_btime) == 0)) {
737                 smb_fname->st.st_ex_mode = unixmode;
738                 return(0);
739         }
740
741         smb_fname->st.st_ex_btime = new_create_timespec;
742
743 #ifdef HAVE_STAT_DOS_FLAGS
744         {
745                 bool attributes_changed;
746
747                 if (set_stat_dos_flags(conn, smb_fname, dosmode,
748                                        &attributes_changed))
749                 {
750                         if (!newfile && attributes_changed) {
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 #endif
760         /* Store the DOS attributes in an EA by preference. */
761         if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
762                 if (!newfile) {
763                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
764                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
765                                      smb_fname->base_name);
766                 }
767                 smb_fname->st.st_ex_mode = unixmode;
768                 return 0;
769         }
770
771         unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
772
773         /* preserve the s bits */
774         mask |= (S_ISUID | S_ISGID);
775
776         /* preserve the t bit */
777 #ifdef S_ISVTX
778         mask |= S_ISVTX;
779 #endif
780
781         /* possibly preserve the x bits */
782         if (!MAP_ARCHIVE(conn))
783                 mask |= S_IXUSR;
784         if (!MAP_SYSTEM(conn))
785                 mask |= S_IXGRP;
786         if (!MAP_HIDDEN(conn))
787                 mask |= S_IXOTH;
788
789         unixmode |= (smb_fname->st.st_ex_mode & mask);
790
791         /* if we previously had any r bits set then leave them alone */
792         if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
793                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
794                 unixmode |= tmp;
795         }
796
797         /* if we previously had any w bits set then leave them alone 
798                 whilst adding in the new w bits, if the new mode is not rdonly */
799         if (!IS_DOS_READONLY(dosmode)) {
800                 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
801         }
802
803         ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
804         if (ret == 0) {
805                 if(!newfile || (lret != -1)) {
806                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
807                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
808                                      smb_fname->base_name);
809                 }
810                 smb_fname->st.st_ex_mode = unixmode;
811                 return 0;
812         }
813
814         if((errno != EPERM) && (errno != EACCES))
815                 return -1;
816
817         if(!lp_dos_filemode(SNUM(conn)))
818                 return -1;
819
820         /* We want DOS semantics, ie allow non owner with write permission to change the
821                 bits on a file. Just like file_ntimes below.
822         */
823
824         /* Check if we have write access. */
825         if (CAN_WRITE(conn)) {
826                 /*
827                  * We need to open the file with write access whilst
828                  * still in our current user context. This ensures we
829                  * are not violating security in doing the fchmod.
830                  * This file open does *not* break any oplocks we are
831                  * holding. We need to review this.... may need to
832                  * break batch oplocks open by others. JRA.
833                  */
834                 files_struct *fsp;
835                 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
836                                      &fsp)))
837                         return -1;
838                 become_root();
839                 ret = SMB_VFS_FCHMOD(fsp, unixmode);
840                 unbecome_root();
841                 close_file_fchmod(NULL, fsp);
842                 if (!newfile) {
843                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
844                                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
845                                      smb_fname->base_name);
846                 }
847                 if (ret == 0) {
848                         smb_fname->st.st_ex_mode = unixmode;
849                 }
850         }
851
852         return( ret );
853 }
854
855 /*******************************************************************
856  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
857  than POSIX.
858 *******************************************************************/
859
860 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
861                 struct smb_file_time *ft)
862 {
863         int ret = -1;
864
865         errno = 0;
866
867         DEBUG(6, ("file_ntime: actime: %s",
868                   time_to_asc(convert_timespec_to_time_t(ft->atime))));
869         DEBUG(6, ("file_ntime: modtime: %s",
870                   time_to_asc(convert_timespec_to_time_t(ft->mtime))));
871         DEBUG(6, ("file_ntime: ctime: %s",
872                   time_to_asc(convert_timespec_to_time_t(ft->ctime))));
873         DEBUG(6, ("file_ntime: createtime: %s",
874                   time_to_asc(convert_timespec_to_time_t(ft->create_time))));
875
876         /* Don't update the time on read-only shares */
877         /* We need this as set_filetime (which can be called on
878            close and other paths) can end up calling this function
879            without the NEED_WRITE protection. Found by : 
880            Leo Weppelman <leo@wau.mis.ah.nl>
881         */
882
883         if (!CAN_WRITE(conn)) {
884                 return 0;
885         }
886
887         if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
888                 return 0;
889         }
890
891         if((errno != EPERM) && (errno != EACCES)) {
892                 return -1;
893         }
894
895         if(!lp_dos_filetimes(SNUM(conn))) {
896                 return -1;
897         }
898
899         /* We have permission (given by the Samba admin) to
900            break POSIX semantics and allow a user to change
901            the time on a file they don't own but can write to
902            (as DOS does).
903          */
904
905         /* Check if we have write access. */
906         if (can_write_to_file(conn, smb_fname)) {
907                 /* We are allowed to become root and change the filetime. */
908                 become_root();
909                 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
910                 unbecome_root();
911         }
912
913         return ret;
914 }
915
916 /******************************************************************
917  Force a "sticky" write time on a pathname. This will always be
918  returned on all future write time queries and set on close.
919 ******************************************************************/
920
921 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
922 {
923         if (null_timespec(mtime)) {
924                 return true;
925         }
926
927         if (!set_sticky_write_time(fileid, mtime)) {
928                 return false;
929         }
930
931         return true;
932 }
933
934 /******************************************************************
935  Force a "sticky" write time on an fsp. This will always be
936  returned on all future write time queries and set on close.
937 ******************************************************************/
938
939 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
940 {
941         if (null_timespec(mtime)) {
942                 return true;
943         }
944
945         fsp->write_time_forced = true;
946         TALLOC_FREE(fsp->update_write_time_event);
947
948         return set_sticky_write_time_path(fsp->file_id, mtime);
949 }
950
951 /******************************************************************
952  Set a create time EA.
953 ******************************************************************/
954
955 NTSTATUS set_create_timespec_ea(connection_struct *conn,
956                                 const struct smb_filename *psmb_fname,
957                                 struct timespec create_time)
958 {
959         NTSTATUS status;
960         struct smb_filename *smb_fname = NULL;
961         uint32_t dosmode;
962         int ret;
963
964         if (!lp_store_dos_attributes(SNUM(conn))) {
965                 return NT_STATUS_OK;
966         }
967
968         status = create_synthetic_smb_fname(talloc_tos(),
969                                 psmb_fname->base_name,
970                                 NULL, &psmb_fname->st,
971                                 &smb_fname);
972
973         if (!NT_STATUS_IS_OK(status)) {
974                 return status;
975         }
976
977         dosmode = dos_mode(conn, smb_fname);
978
979         smb_fname->st.st_ex_btime = create_time;
980
981         ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
982         if (ret == -1) {
983                 map_nt_error_from_unix(errno);
984         }
985
986         DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
987                 smb_fname_str_dbg(smb_fname)));
988
989         return NT_STATUS_OK;
990 }
991
992 /******************************************************************
993  Return a create time.
994 ******************************************************************/
995
996 struct timespec get_create_timespec(connection_struct *conn,
997                                 struct files_struct *fsp,
998                                 const struct smb_filename *smb_fname)
999 {
1000         return smb_fname->st.st_ex_btime;
1001 }
1002
1003 /******************************************************************
1004  Return a change time (may look at EA in future).
1005 ******************************************************************/
1006
1007 struct timespec get_change_timespec(connection_struct *conn,
1008                                 struct files_struct *fsp,
1009                                 const struct smb_filename *smb_fname)
1010 {
1011         return smb_fname->st.st_ex_mtime;
1012 }