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