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