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