Fix bug #10097 - MacOSX 10.9 will not follow path-based DFS referrals handed out...
[Samba.git] / source3 / smbd / dosmode.c
blob677b18c9a011f92105a73f74707a1728ea754cf8
1 /*
2 Unix SMB/CIFS implementation.
3 dos mode handling functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) James Peach 2006
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.
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.
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/>.
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"
28 static uint32_t filter_mode_by_protocol(uint32_t mode)
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;
37 return mode;
40 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
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;
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.
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.
72 ****************************************************************************/
74 mode_t unix_mode(connection_struct *conn, int dosmode,
75 const struct smb_filename *smb_fname,
76 const char *inherit_from_dir)
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. */
82 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
83 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
86 if ((inherit_from_dir != NULL) && lp_inherit_perms(SNUM(conn))) {
87 struct smb_filename *smb_fname_parent = NULL;
88 NTSTATUS status;
90 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
91 smb_fname_str_dbg(smb_fname),
92 inherit_from_dir));
94 status = create_synthetic_smb_fname(talloc_tos(),
95 inherit_from_dir, NULL,
96 NULL, &smb_fname_parent);
97 if (!NT_STATUS_IS_OK(status)) {
98 DEBUG(1,("unix_mode(%s) failed, [dir %s]: %s\n",
99 smb_fname_str_dbg(smb_fname),
100 inherit_from_dir, nt_errstr(status)));
101 return(0);
104 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
105 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
106 smb_fname_str_dbg(smb_fname),
107 inherit_from_dir, strerror(errno)));
108 TALLOC_FREE(smb_fname_parent);
109 return(0); /* *** shouldn't happen! *** */
112 /* Save for later - but explicitly remove setuid bit for safety. */
113 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
114 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
115 smb_fname_str_dbg(smb_fname), (int)dir_mode));
116 /* Clear "result" */
117 result = 0;
118 TALLOC_FREE(smb_fname_parent);
121 if (IS_DOS_DIR(dosmode)) {
122 /* We never make directories read only for the owner as under DOS a user
123 can always create a file in a read-only directory. */
124 result |= (S_IFDIR | S_IWUSR);
126 if (dir_mode) {
127 /* Inherit mode of parent directory. */
128 result |= dir_mode;
129 } else {
130 /* Provisionally add all 'x' bits */
131 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
133 /* Apply directory mask */
134 result &= lp_dir_mask(SNUM(conn));
135 /* Add in force bits */
136 result |= lp_force_dir_mode(SNUM(conn));
138 } else {
139 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
140 result |= S_IXUSR;
142 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
143 result |= S_IXGRP;
145 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
146 result |= S_IXOTH;
148 if (dir_mode) {
149 /* Inherit 666 component of parent directory mode */
150 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
151 } else {
152 /* Apply mode mask */
153 result &= lp_create_mask(SNUM(conn));
154 /* Add in force bits */
155 result |= lp_force_create_mode(SNUM(conn));
159 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
160 (int)result));
161 return(result);
164 /****************************************************************************
165 Change a unix mode to a dos mode.
166 ****************************************************************************/
168 static uint32 dos_mode_from_sbuf(connection_struct *conn,
169 const struct smb_filename *smb_fname)
171 int result = 0;
172 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
174 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
175 /* if we can find out if a file is immutable we should report it r/o */
176 if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
177 result |= FILE_ATTRIBUTE_READONLY;
179 #endif
180 if (ro_opts == MAP_READONLY_YES) {
181 /* Original Samba method - map inverse of user "w" bit. */
182 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
183 result |= FILE_ATTRIBUTE_READONLY;
185 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
186 /* Check actual permissions for read-only. */
187 if (!can_write_to_file(conn, smb_fname)) {
188 result |= FILE_ATTRIBUTE_READONLY;
190 } /* Else never set the readonly bit. */
192 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
193 result |= FILE_ATTRIBUTE_ARCHIVE;
195 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
196 result |= FILE_ATTRIBUTE_SYSTEM;
198 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
199 result |= FILE_ATTRIBUTE_HIDDEN;
201 if (S_ISDIR(smb_fname->st.st_ex_mode))
202 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
204 result |= set_link_read_only_flag(&smb_fname->st);
206 DEBUG(8,("dos_mode_from_sbuf returning "));
208 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
209 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
210 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
211 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
212 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
214 DEBUG(8,("\n"));
215 return result;
218 /****************************************************************************
219 Get DOS attributes from an EA.
220 This can also pull the create time into the stat struct inside smb_fname.
221 ****************************************************************************/
223 static bool get_ea_dos_attribute(connection_struct *conn,
224 struct smb_filename *smb_fname,
225 uint32 *pattr)
227 struct xattr_DOSATTRIB dosattrib;
228 enum ndr_err_code ndr_err;
229 DATA_BLOB blob;
230 ssize_t sizeret;
231 fstring attrstr;
232 uint32_t dosattr;
234 if (!lp_store_dos_attributes(SNUM(conn))) {
235 return False;
238 /* Don't reset pattr to zero as we may already have filename-based attributes we
239 need to preserve. */
241 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
242 SAMBA_XATTR_DOS_ATTRIB, attrstr,
243 sizeof(attrstr));
244 if (sizeret == -1) {
245 if (errno == ENOSYS
246 #if defined(ENOTSUP)
247 || errno == ENOTSUP) {
248 #else
250 #endif
251 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
252 "from EA on file %s: Error = %s\n",
253 smb_fname_str_dbg(smb_fname),
254 strerror(errno)));
255 set_store_dos_attributes(SNUM(conn), False);
257 return False;
260 blob.data = (uint8_t *)attrstr;
261 blob.length = sizeret;
263 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
264 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
266 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
267 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
268 "from EA on file %s: Error = %s\n",
269 smb_fname_str_dbg(smb_fname),
270 ndr_errstr(ndr_err)));
271 return false;
274 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
275 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
277 switch (dosattrib.version) {
278 case 0xFFFF:
279 dosattr = dosattrib.info.compatinfoFFFF.attrib;
280 break;
281 case 1:
282 dosattr = dosattrib.info.info1.attrib;
283 if (!null_nttime(dosattrib.info.info1.create_time)) {
284 struct timespec create_time =
285 nt_time_to_unix_timespec(
286 &dosattrib.info.info1.create_time);
288 update_stat_ex_create_time(&smb_fname->st,
289 create_time);
291 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
292 "set btime %s\n",
293 smb_fname_str_dbg(smb_fname),
294 time_to_asc(convert_timespec_to_time_t(
295 create_time)) ));
297 break;
298 case 2:
299 dosattr = dosattrib.info.oldinfo2.attrib;
300 /* Don't know what flags to check for this case. */
301 break;
302 case 3:
303 dosattr = dosattrib.info.info3.attrib;
304 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
305 !null_nttime(dosattrib.info.info3.create_time)) {
306 struct timespec create_time =
307 nt_time_to_unix_timespec(
308 &dosattrib.info.info3.create_time);
310 update_stat_ex_create_time(&smb_fname->st,
311 create_time);
313 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
314 "set btime %s\n",
315 smb_fname_str_dbg(smb_fname),
316 time_to_asc(convert_timespec_to_time_t(
317 create_time)) ));
319 break;
320 default:
321 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
322 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
323 attrstr));
324 return false;
327 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
328 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
330 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
331 *pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
333 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
335 if (dosattr & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
336 if (dosattr & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
337 if (dosattr & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
338 if (dosattr & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
339 if (dosattr & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
341 DEBUG(8,("\n"));
343 return True;
346 /****************************************************************************
347 Set DOS attributes in an EA.
348 Also sets the create time.
349 ****************************************************************************/
351 static bool set_ea_dos_attribute(connection_struct *conn,
352 struct smb_filename *smb_fname,
353 uint32 dosmode)
355 struct xattr_DOSATTRIB dosattrib;
356 enum ndr_err_code ndr_err;
357 DATA_BLOB blob;
359 if (!lp_store_dos_attributes(SNUM(conn))) {
360 return False;
363 ZERO_STRUCT(dosattrib);
364 ZERO_STRUCT(blob);
366 dosattrib.version = 3;
367 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
368 XATTR_DOSINFO_CREATE_TIME;
369 dosattrib.info.info3.attrib = dosmode;
370 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
371 smb_fname->st.st_ex_btime);
373 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
374 (unsigned int)dosmode,
375 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
376 smb_fname_str_dbg(smb_fname) ));
378 ndr_err = ndr_push_struct_blob(
379 &blob, talloc_tos(), &dosattrib,
380 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
382 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
383 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
384 ndr_errstr(ndr_err)));
385 return false;
388 if (blob.data == NULL || blob.length == 0) {
389 return false;
392 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
393 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
394 0) == -1) {
395 bool ret = false;
396 files_struct *fsp = NULL;
398 if((errno != EPERM) && (errno != EACCES)) {
399 if (errno == ENOSYS
400 #if defined(ENOTSUP)
401 || errno == ENOTSUP) {
402 #else
404 #endif
405 DEBUG(1,("set_ea_dos_attributes: Cannot set "
406 "attribute EA on file %s: Error = %s\n",
407 smb_fname_str_dbg(smb_fname),
408 strerror(errno) ));
409 set_store_dos_attributes(SNUM(conn), False);
411 return false;
414 /* We want DOS semantics, ie allow non owner with write permission to change the
415 bits on a file. Just like file_ntimes below.
418 /* Check if we have write access. */
419 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
420 return false;
422 if (!can_write_to_file(conn, smb_fname)) {
423 return false;
427 * We need to open the file with write access whilst
428 * still in our current user context. This ensures we
429 * are not violating security in doing the setxattr.
432 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
433 &fsp)))
434 return false;
435 become_root();
436 if (SMB_VFS_FSETXATTR(fsp,
437 SAMBA_XATTR_DOS_ATTRIB, blob.data,
438 blob.length, 0) == 0) {
439 ret = true;
441 unbecome_root();
442 close_file(NULL, fsp, NORMAL_CLOSE);
443 return ret;
445 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
446 (unsigned int)dosmode,
447 smb_fname_str_dbg(smb_fname)));
448 return true;
451 /****************************************************************************
452 Change a unix mode to a dos mode for an ms dfs link.
453 ****************************************************************************/
455 uint32 dos_mode_msdfs(connection_struct *conn,
456 const struct smb_filename *smb_fname)
458 uint32 result = 0;
460 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
462 if (!VALID_STAT(smb_fname->st)) {
463 return 0;
466 /* First do any modifications that depend on the path name. */
467 /* hide files with a name starting with a . */
468 if (lp_hide_dot_files(SNUM(conn))) {
469 const char *p = strrchr_m(smb_fname->base_name, '/');
470 if (p) {
471 p++;
472 } else {
473 p = smb_fname->base_name;
476 /* Only . and .. are not hidden. */
477 if (p[0] == '.' && !((p[1] == '\0') ||
478 (p[1] == '.' && p[2] == '\0'))) {
479 result |= FILE_ATTRIBUTE_HIDDEN;
483 result |= dos_mode_from_sbuf(conn, smb_fname);
485 /* Optimization : Only call is_hidden_path if it's not already
486 hidden. */
487 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
488 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
489 result |= FILE_ATTRIBUTE_HIDDEN;
492 if (result == 0) {
493 result = FILE_ATTRIBUTE_NORMAL;
496 result = filter_mode_by_protocol(result);
499 * Add in that it is a reparse point
501 result |= FILE_ATTRIBUTE_REPARSE_POINT;
503 DEBUG(8,("dos_mode_msdfs returning "));
505 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
506 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
507 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
508 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
509 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
510 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
512 DEBUG(8,("\n"));
514 return(result);
517 #ifdef HAVE_STAT_DOS_FLAGS
518 /****************************************************************************
519 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
520 ****************************************************************************/
522 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
524 uint32_t dos_stat_flags = 0;
526 if (dosmode & FILE_ATTRIBUTE_ARCHIVE)
527 dos_stat_flags |= UF_DOS_ARCHIVE;
528 if (dosmode & FILE_ATTRIBUTE_HIDDEN)
529 dos_stat_flags |= UF_DOS_HIDDEN;
530 if (dosmode & FILE_ATTRIBUTE_READONLY)
531 dos_stat_flags |= UF_DOS_RO;
532 if (dosmode & FILE_ATTRIBUTE_SYSTEM)
533 dos_stat_flags |= UF_DOS_SYSTEM;
534 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
535 dos_stat_flags |= UF_DOS_NOINDEX;
537 return dos_stat_flags;
540 /****************************************************************************
541 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
542 ****************************************************************************/
544 static bool get_stat_dos_flags(connection_struct *conn,
545 const struct smb_filename *smb_fname,
546 uint32_t *dosmode)
548 SMB_ASSERT(VALID_STAT(smb_fname->st));
549 SMB_ASSERT(dosmode);
551 if (!lp_store_dos_attributes(SNUM(conn))) {
552 return false;
555 DEBUG(5, ("Getting stat dos attributes for %s.\n",
556 smb_fname_str_dbg(smb_fname)));
558 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
559 *dosmode |= FILE_ATTRIBUTE_ARCHIVE;
560 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
561 *dosmode |= FILE_ATTRIBUTE_HIDDEN;
562 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
563 *dosmode |= FILE_ATTRIBUTE_READONLY;
564 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
565 *dosmode |= FILE_ATTRIBUTE_SYSTEM;
566 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
567 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
568 if (smb_fname->st.st_ex_flags & FILE_ATTRIBUTE_SPARSE)
569 *dosmode |= FILE_ATTRIBUTE_SPARSE;
570 if (S_ISDIR(smb_fname->st.st_ex_mode))
571 *dosmode |= FILE_ATTRIBUTE_DIRECTORY;
573 *dosmode |= set_link_read_only_flag(&smb_fname->st);
575 return true;
578 /****************************************************************************
579 Sets DOS attributes, stored in st_ex_flags of the inode.
580 ****************************************************************************/
582 static bool set_stat_dos_flags(connection_struct *conn,
583 const struct smb_filename *smb_fname,
584 uint32_t dosmode,
585 bool *attributes_changed)
587 uint32_t new_flags = 0;
588 int error = 0;
590 SMB_ASSERT(VALID_STAT(smb_fname->st));
591 SMB_ASSERT(attributes_changed);
593 *attributes_changed = false;
595 if (!lp_store_dos_attributes(SNUM(conn))) {
596 return false;
599 DEBUG(5, ("Setting stat dos attributes for %s.\n",
600 smb_fname_str_dbg(smb_fname)));
602 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
603 dos_attributes_to_stat_dos_flags(dosmode);
605 /* Return early if no flags changed. */
606 if (new_flags == smb_fname->st.st_ex_flags)
607 return true;
609 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
610 smb_fname->st.st_ex_flags));
612 /* Set new flags with chflags. */
613 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
614 if (error) {
615 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
616 "file %s! errno=%d\n", new_flags,
617 smb_fname_str_dbg(smb_fname), errno));
618 return false;
621 *attributes_changed = true;
622 return true;
624 #endif /* HAVE_STAT_DOS_FLAGS */
626 /****************************************************************************
627 Change a unix mode to a dos mode.
628 May also read the create timespec into the stat struct in smb_fname
629 if "store dos attributes" is true.
630 ****************************************************************************/
632 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
634 uint32 result = 0;
635 bool offline, used_stat_dos_flags = false;
637 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
639 if (!VALID_STAT(smb_fname->st)) {
640 return 0;
643 /* First do any modifications that depend on the path name. */
644 /* hide files with a name starting with a . */
645 if (lp_hide_dot_files(SNUM(conn))) {
646 const char *p = strrchr_m(smb_fname->base_name,'/');
647 if (p) {
648 p++;
649 } else {
650 p = smb_fname->base_name;
653 /* Only . and .. are not hidden. */
654 if (p[0] == '.' && !((p[1] == '\0') ||
655 (p[1] == '.' && p[2] == '\0'))) {
656 result |= FILE_ATTRIBUTE_HIDDEN;
660 #ifdef HAVE_STAT_DOS_FLAGS
661 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
662 #endif
663 if (!used_stat_dos_flags) {
664 /* Get the DOS attributes from an EA by preference. */
665 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
666 result |= dos_mode_from_sbuf(conn, smb_fname);
670 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
671 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
672 result |= FILE_ATTRIBUTE_OFFLINE;
675 /* Optimization : Only call is_hidden_path if it's not already
676 hidden. */
677 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
678 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
679 result |= FILE_ATTRIBUTE_HIDDEN;
682 if (result == 0) {
683 result = FILE_ATTRIBUTE_NORMAL;
686 result = filter_mode_by_protocol(result);
688 DEBUG(8,("dos_mode returning "));
690 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
691 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
692 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
693 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
694 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
695 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
697 DEBUG(8,("\n"));
699 return(result);
702 /*******************************************************************
703 chmod a file - but preserve some bits.
704 If "store dos attributes" is also set it will store the create time
705 from the stat struct in smb_fname (in NTTIME format) in the EA
706 attribute also.
707 ********************************************************************/
709 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
710 uint32 dosmode, const char *parent_dir, bool newfile)
712 int mask=0;
713 mode_t tmp;
714 mode_t unixmode;
715 int ret = -1, lret = -1;
716 uint32_t old_mode;
717 struct timespec new_create_timespec;
718 files_struct *fsp = NULL;
720 if (!CAN_WRITE(conn)) {
721 errno = EROFS;
722 return -1;
725 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
726 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
728 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
729 dosmode, smb_fname_str_dbg(smb_fname)));
731 unixmode = smb_fname->st.st_ex_mode;
733 get_acl_group_bits(conn, smb_fname->base_name,
734 &smb_fname->st.st_ex_mode);
736 if (S_ISDIR(smb_fname->st.st_ex_mode))
737 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
738 else
739 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
741 new_create_timespec = smb_fname->st.st_ex_btime;
743 old_mode = dos_mode(conn, smb_fname);
745 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
746 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
747 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
748 if (lret == -1) {
749 DEBUG(0, ("set_dos_mode: client has asked to "
750 "set FILE_ATTRIBUTE_OFFLINE to "
751 "%s/%s but there was an error while "
752 "setting it or it is not "
753 "supported.\n", parent_dir,
754 smb_fname_str_dbg(smb_fname)));
759 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
760 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
762 smb_fname->st.st_ex_btime = new_create_timespec;
764 #ifdef HAVE_STAT_DOS_FLAGS
766 bool attributes_changed;
768 if (set_stat_dos_flags(conn, smb_fname, dosmode,
769 &attributes_changed))
771 if (!newfile && attributes_changed) {
772 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
773 FILE_NOTIFY_CHANGE_ATTRIBUTES,
774 smb_fname->base_name);
776 smb_fname->st.st_ex_mode = unixmode;
777 return 0;
780 #endif
781 /* Store the DOS attributes in an EA by preference. */
782 if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
783 if (!newfile) {
784 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
785 FILE_NOTIFY_CHANGE_ATTRIBUTES,
786 smb_fname->base_name);
788 smb_fname->st.st_ex_mode = unixmode;
789 return 0;
792 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
794 /* preserve the s bits */
795 mask |= (S_ISUID | S_ISGID);
797 /* preserve the t bit */
798 #ifdef S_ISVTX
799 mask |= S_ISVTX;
800 #endif
802 /* possibly preserve the x bits */
803 if (!MAP_ARCHIVE(conn))
804 mask |= S_IXUSR;
805 if (!MAP_SYSTEM(conn))
806 mask |= S_IXGRP;
807 if (!MAP_HIDDEN(conn))
808 mask |= S_IXOTH;
810 unixmode |= (smb_fname->st.st_ex_mode & mask);
812 /* if we previously had any r bits set then leave them alone */
813 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
814 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
815 unixmode |= tmp;
818 /* if we previously had any w bits set then leave them alone
819 whilst adding in the new w bits, if the new mode is not rdonly */
820 if (!IS_DOS_READONLY(dosmode)) {
821 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
825 * From the chmod 2 man page:
827 * "If the calling process is not privileged, and the group of the file
828 * does not match the effective group ID of the process or one of its
829 * supplementary group IDs, the S_ISGID bit will be turned off, but
830 * this will not cause an error to be returned."
832 * Simply refuse to do the chmod in this case.
835 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
836 geteuid() != sec_initial_uid() &&
837 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
838 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
839 "set for directory %s\n",
840 smb_fname_str_dbg(smb_fname)));
841 errno = EPERM;
842 return -1;
845 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
846 if (ret == 0) {
847 if(!newfile || (lret != -1)) {
848 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
849 FILE_NOTIFY_CHANGE_ATTRIBUTES,
850 smb_fname->base_name);
852 smb_fname->st.st_ex_mode = unixmode;
853 return 0;
856 if((errno != EPERM) && (errno != EACCES))
857 return -1;
859 if(!lp_dos_filemode(SNUM(conn)))
860 return -1;
862 /* We want DOS semantics, ie allow non owner with write permission to change the
863 bits on a file. Just like file_ntimes below.
866 if (!can_write_to_file(conn, smb_fname)) {
867 errno = EACCES;
868 return -1;
872 * We need to open the file with write access whilst
873 * still in our current user context. This ensures we
874 * are not violating security in doing the fchmod.
876 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
877 &fsp)))
878 return -1;
879 become_root();
880 ret = SMB_VFS_FCHMOD(fsp, unixmode);
881 unbecome_root();
882 close_file(NULL, fsp, NORMAL_CLOSE);
883 if (!newfile) {
884 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
885 FILE_NOTIFY_CHANGE_ATTRIBUTES,
886 smb_fname->base_name);
888 if (ret == 0) {
889 smb_fname->st.st_ex_mode = unixmode;
892 return( ret );
896 NTSTATUS file_set_sparse(connection_struct *conn,
897 files_struct *fsp,
898 bool sparse)
900 uint32_t old_dosmode;
901 uint32_t new_dosmode;
902 NTSTATUS status;
904 if (!CAN_WRITE(conn)) {
905 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
906 "on readonly share[%s]\n",
907 smb_fname_str_dbg(fsp->fsp_name),
908 sparse,
909 lp_servicename(talloc_tos(), SNUM(conn))));
910 return NT_STATUS_MEDIA_WRITE_PROTECTED;
913 if (!(fsp->access_mask & FILE_WRITE_DATA) &&
914 !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
915 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
916 "access_mask[0x%08X] - access denied\n",
917 smb_fname_str_dbg(fsp->fsp_name),
918 sparse,
919 fsp->access_mask));
920 return NT_STATUS_ACCESS_DENIED;
923 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
924 sparse, smb_fname_str_dbg(fsp->fsp_name)));
926 if (!lp_store_dos_attributes(SNUM(conn))) {
927 return NT_STATUS_INVALID_DEVICE_REQUEST;
930 status = vfs_stat_fsp(fsp);
931 if (!NT_STATUS_IS_OK(status)) {
932 return status;
935 old_dosmode = dos_mode(conn, fsp->fsp_name);
937 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
938 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
939 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
940 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
941 } else {
942 return NT_STATUS_OK;
945 /* Store the DOS attributes in an EA. */
946 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
947 new_dosmode)) {
948 if (errno == 0) {
949 errno = EIO;
951 return map_nt_error_from_unix(errno);
954 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
955 FILE_NOTIFY_CHANGE_ATTRIBUTES,
956 fsp->fsp_name->base_name);
958 fsp->is_sparse = sparse;
960 return NT_STATUS_OK;
963 /*******************************************************************
964 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
965 than POSIX.
966 *******************************************************************/
968 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
969 struct smb_file_time *ft)
971 int ret = -1;
973 errno = 0;
975 DEBUG(6, ("file_ntime: actime: %s",
976 time_to_asc(convert_timespec_to_time_t(ft->atime))));
977 DEBUG(6, ("file_ntime: modtime: %s",
978 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
979 DEBUG(6, ("file_ntime: ctime: %s",
980 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
981 DEBUG(6, ("file_ntime: createtime: %s",
982 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
984 /* Don't update the time on read-only shares */
985 /* We need this as set_filetime (which can be called on
986 close and other paths) can end up calling this function
987 without the NEED_WRITE protection. Found by :
988 Leo Weppelman <leo@wau.mis.ah.nl>
991 if (!CAN_WRITE(conn)) {
992 return 0;
995 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
996 return 0;
999 if((errno != EPERM) && (errno != EACCES)) {
1000 return -1;
1003 if(!lp_dos_filetimes(SNUM(conn))) {
1004 return -1;
1007 /* We have permission (given by the Samba admin) to
1008 break POSIX semantics and allow a user to change
1009 the time on a file they don't own but can write to
1010 (as DOS does).
1013 /* Check if we have write access. */
1014 if (can_write_to_file(conn, smb_fname)) {
1015 /* We are allowed to become root and change the filetime. */
1016 become_root();
1017 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1018 unbecome_root();
1021 return ret;
1024 /******************************************************************
1025 Force a "sticky" write time on a pathname. This will always be
1026 returned on all future write time queries and set on close.
1027 ******************************************************************/
1029 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1031 if (null_timespec(mtime)) {
1032 return true;
1035 if (!set_sticky_write_time(fileid, mtime)) {
1036 return false;
1039 return true;
1042 /******************************************************************
1043 Force a "sticky" write time on an fsp. This will always be
1044 returned on all future write time queries and set on close.
1045 ******************************************************************/
1047 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1049 if (null_timespec(mtime)) {
1050 return true;
1053 fsp->write_time_forced = true;
1054 TALLOC_FREE(fsp->update_write_time_event);
1056 return set_sticky_write_time_path(fsp->file_id, mtime);
1059 /******************************************************************
1060 Set a create time EA.
1061 ******************************************************************/
1063 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1064 const struct smb_filename *psmb_fname,
1065 struct timespec create_time)
1067 NTSTATUS status;
1068 struct smb_filename *smb_fname = NULL;
1069 uint32_t dosmode;
1070 int ret;
1072 if (!lp_store_dos_attributes(SNUM(conn))) {
1073 return NT_STATUS_OK;
1076 status = create_synthetic_smb_fname(talloc_tos(),
1077 psmb_fname->base_name,
1078 NULL, &psmb_fname->st,
1079 &smb_fname);
1081 if (!NT_STATUS_IS_OK(status)) {
1082 return status;
1085 dosmode = dos_mode(conn, smb_fname);
1087 smb_fname->st.st_ex_btime = create_time;
1089 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1090 if (ret == -1) {
1091 map_nt_error_from_unix(errno);
1094 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1095 smb_fname_str_dbg(smb_fname)));
1097 return NT_STATUS_OK;
1100 /******************************************************************
1101 Return a create time.
1102 ******************************************************************/
1104 struct timespec get_create_timespec(connection_struct *conn,
1105 struct files_struct *fsp,
1106 const struct smb_filename *smb_fname)
1108 return smb_fname->st.st_ex_btime;
1111 /******************************************************************
1112 Return a change time (may look at EA in future).
1113 ******************************************************************/
1115 struct timespec get_change_timespec(connection_struct *conn,
1116 struct files_struct *fsp,
1117 const struct smb_filename *smb_fname)
1119 return smb_fname->st.st_ex_mtime;