smbd: ignore symlinks in file_set_dosmode()
[Samba.git] / source3 / smbd / dosmode.c
blob8b51f5a7d8b15f15e426f27698fb61f9d8aba766
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 "globals.h"
23 #include "system/filesys.h"
24 #include "librpc/gen_ndr/ndr_xattr.h"
25 #include "librpc/gen_ndr/ioctl.h"
26 #include "../libcli/security/security.h"
27 #include "smbd/smbd.h"
28 #include "lib/param/loadparm.h"
29 #include "lib/util/tevent_ntstatus.h"
30 #include "lib/util/string_wrappers.h"
31 #include "fake_file.h"
33 static void dos_mode_debug_print(const char *func, uint32_t mode)
35 fstring modestr;
37 if (DEBUGLEVEL < DBGLVL_INFO) {
38 return;
41 modestr[0] = '\0';
43 if (mode & FILE_ATTRIBUTE_HIDDEN) {
44 fstrcat(modestr, "h");
46 if (mode & FILE_ATTRIBUTE_READONLY) {
47 fstrcat(modestr, "r");
49 if (mode & FILE_ATTRIBUTE_SYSTEM) {
50 fstrcat(modestr, "s");
52 if (mode & FILE_ATTRIBUTE_DIRECTORY) {
53 fstrcat(modestr, "d");
55 if (mode & FILE_ATTRIBUTE_ARCHIVE) {
56 fstrcat(modestr, "a");
58 if (mode & FILE_ATTRIBUTE_SPARSE) {
59 fstrcat(modestr, "[sparse]");
61 if (mode & FILE_ATTRIBUTE_OFFLINE) {
62 fstrcat(modestr, "[offline]");
64 if (mode & FILE_ATTRIBUTE_COMPRESSED) {
65 fstrcat(modestr, "[compressed]");
68 DBG_INFO("%s returning (0x%x): \"%s\"\n", func, (unsigned)mode,
69 modestr);
72 static uint32_t filter_mode_by_protocol(uint32_t mode)
74 if (get_Protocol() <= PROTOCOL_LANMAN2) {
75 DEBUG(10,("filter_mode_by_protocol: "
76 "filtering result 0x%x to 0x%x\n",
77 (unsigned int)mode,
78 (unsigned int)(mode & 0x3f) ));
79 mode &= 0x3f;
81 return mode;
84 /****************************************************************************
85 Change a dos mode to a unix mode.
86 Base permission for files:
87 if creating file and inheriting (i.e. parent_dir != NULL)
88 apply read/write bits from parent directory.
89 else
90 everybody gets read bit set
91 dos readonly is represented in unix by removing everyone's write bit
92 dos archive is represented in unix by the user's execute bit
93 dos system is represented in unix by the group's execute bit
94 dos hidden is represented in unix by the other's execute bit
95 if !inheriting {
96 Then apply create mask,
97 then add force bits.
99 Base permission for directories:
100 dos directory is represented in unix by unix's dir bit and the exec bit
101 if !inheriting {
102 Then apply create mask,
103 then add force bits.
105 ****************************************************************************/
107 mode_t unix_mode(connection_struct *conn, int dosmode,
108 const struct smb_filename *smb_fname,
109 struct files_struct *parent_dirfsp)
111 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
112 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
113 * inheriting. */
115 if ((dosmode & FILE_ATTRIBUTE_READONLY) &&
116 !lp_store_dos_attributes(SNUM(conn))) {
117 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
120 if ((parent_dirfsp != NULL) && lp_inherit_permissions(SNUM(conn))) {
121 struct stat_ex sbuf = { .st_ex_nlink = 0, };
122 int ret;
124 DBG_DEBUG("[%s] inheriting from [%s]\n",
125 smb_fname_str_dbg(smb_fname),
126 smb_fname_str_dbg(parent_dirfsp->fsp_name));
128 ret = SMB_VFS_FSTAT(parent_dirfsp, &sbuf);
129 if (ret != 0) {
130 DBG_ERR("fstat failed [%s]: %s\n",
131 smb_fname_str_dbg(parent_dirfsp->fsp_name),
132 strerror(errno));
133 return(0); /* *** shouldn't happen! *** */
136 /* Save for later - but explicitly remove setuid bit for safety. */
137 dir_mode = sbuf.st_ex_mode & ~S_ISUID;
138 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
139 smb_fname_str_dbg(smb_fname), (int)dir_mode));
140 /* Clear "result" */
141 result = 0;
144 if (dosmode & FILE_ATTRIBUTE_DIRECTORY) {
145 /* We never make directories read only for the owner as under DOS a user
146 can always create a file in a read-only directory. */
147 result |= (S_IFDIR | S_IWUSR);
149 if (dir_mode) {
150 /* Inherit mode of parent directory. */
151 result |= dir_mode;
152 } else {
153 /* Provisionally add all 'x' bits */
154 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
156 /* Apply directory mask */
157 result &= lp_directory_mask(SNUM(conn));
158 /* Add in force bits */
159 result |= lp_force_directory_mode(SNUM(conn));
161 } else {
162 if ((dosmode & FILE_ATTRIBUTE_ARCHIVE) &&
163 lp_map_archive(SNUM(conn))) {
164 result |= S_IXUSR;
167 if ((dosmode & FILE_ATTRIBUTE_SYSTEM) &&
168 lp_map_system(SNUM(conn))) {
169 result |= S_IXGRP;
172 if ((dosmode & FILE_ATTRIBUTE_HIDDEN) &&
173 lp_map_hidden(SNUM(conn))) {
174 result |= S_IXOTH;
177 if (dir_mode) {
178 /* Inherit 666 component of parent directory mode */
179 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
180 } else {
181 /* Apply mode mask */
182 result &= lp_create_mask(SNUM(conn));
183 /* Add in force bits */
184 result |= lp_force_create_mode(SNUM(conn));
188 DBG_INFO("unix_mode(%s) returning 0%o\n",
189 smb_fname_str_dbg(smb_fname), (int)result);
191 return(result);
194 /****************************************************************************
195 Change a unix mode to a dos mode.
196 ****************************************************************************/
198 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
199 const struct stat_ex *st,
200 struct files_struct *fsp)
202 int result = 0;
203 enum mapreadonly_options ro_opts =
204 (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
206 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
207 /* if we can find out if a file is immutable we should report it r/o */
208 if (st->st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
209 result |= FILE_ATTRIBUTE_READONLY;
211 #endif
212 if (ro_opts == MAP_READONLY_YES) {
213 /* Original Samba method - map inverse of user "w" bit. */
214 if ((st->st_ex_mode & S_IWUSR) == 0) {
215 result |= FILE_ATTRIBUTE_READONLY;
217 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
218 /* smb_fname->fsp can be NULL for an MS-DFS link. */
219 /* Check actual permissions for read-only. */
220 if ((fsp != NULL) && !can_write_to_fsp(fsp)) {
221 result |= FILE_ATTRIBUTE_READONLY;
223 } /* Else never set the readonly bit. */
225 if (MAP_ARCHIVE(conn) && ((st->st_ex_mode & S_IXUSR) != 0)) {
226 result |= FILE_ATTRIBUTE_ARCHIVE;
229 if (MAP_SYSTEM(conn) && ((st->st_ex_mode & S_IXGRP) != 0)) {
230 result |= FILE_ATTRIBUTE_SYSTEM;
233 if (MAP_HIDDEN(conn) && ((st->st_ex_mode & S_IXOTH) != 0)) {
234 result |= FILE_ATTRIBUTE_HIDDEN;
237 if (S_ISDIR(st->st_ex_mode)) {
238 result = FILE_ATTRIBUTE_DIRECTORY |
239 (result & FILE_ATTRIBUTE_READONLY);
242 dos_mode_debug_print(__func__, result);
244 return result;
247 /****************************************************************************
248 Get DOS attributes from an EA.
249 This can also pull the create time into the stat struct inside smb_fname.
250 ****************************************************************************/
252 NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
253 DATA_BLOB blob,
254 uint32_t *pattr)
256 struct xattr_DOSATTRIB dosattrib;
257 enum ndr_err_code ndr_err;
258 uint32_t dosattr;
260 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
261 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
263 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
264 DBG_WARNING("bad ndr decode "
265 "from EA on file %s: Error = %s\n",
266 smb_fname_str_dbg(smb_fname),
267 ndr_errstr(ndr_err));
268 return ndr_map_error2ntstatus(ndr_err);
271 DBG_DEBUG("%s attr = %s\n",
272 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex);
274 switch (dosattrib.version) {
275 case 0xFFFF:
276 dosattr = dosattrib.info.compatinfoFFFF.attrib;
277 break;
278 case 1:
279 dosattr = dosattrib.info.info1.attrib;
280 if (!null_nttime(dosattrib.info.info1.create_time)) {
281 struct timespec create_time =
282 nt_time_to_unix_timespec(
283 dosattrib.info.info1.create_time);
285 update_stat_ex_create_time(&smb_fname->st,
286 create_time);
288 DBG_DEBUG("file %s case 1 set btime %s",
289 smb_fname_str_dbg(smb_fname),
290 time_to_asc(convert_timespec_to_time_t(
291 create_time)));
293 break;
294 case 2:
295 dosattr = dosattrib.info.oldinfo2.attrib;
296 /* Don't know what flags to check for this case. */
297 break;
298 case 3:
299 dosattr = dosattrib.info.info3.attrib;
300 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
301 !null_nttime(dosattrib.info.info3.create_time)) {
302 struct timespec create_time =
303 nt_time_to_full_timespec(
304 dosattrib.info.info3.create_time);
306 update_stat_ex_create_time(&smb_fname->st,
307 create_time);
309 DBG_DEBUG("file %s case 3 set btime %s",
310 smb_fname_str_dbg(smb_fname),
311 time_to_asc(convert_timespec_to_time_t(
312 create_time)));
314 break;
315 case 4:
316 case 5:
318 uint32_t info_valid_flags;
319 NTTIME info_create_time;
321 if (dosattrib.version == 4) {
322 info_valid_flags = dosattrib.info.info4.valid_flags;
323 info_create_time = dosattrib.info.info4.create_time;
324 dosattr = dosattrib.info.info4.attrib;
325 } else {
326 info_valid_flags = dosattrib.info.info5.valid_flags;
327 info_create_time = dosattrib.info.info5.create_time;
328 dosattr = dosattrib.info.info5.attrib;
331 if ((info_valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
332 !null_nttime(info_create_time))
334 struct timespec creat_time;
336 creat_time = nt_time_to_full_timespec(info_create_time);
337 update_stat_ex_create_time(&smb_fname->st, creat_time);
339 DBG_DEBUG("file [%s] creation time [%s]\n",
340 smb_fname_str_dbg(smb_fname),
341 nt_time_string(talloc_tos(), info_create_time));
344 break;
346 default:
347 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
348 smb_fname_str_dbg(smb_fname), blob.data);
349 /* Should this be INTERNAL_ERROR? */
350 return NT_STATUS_INVALID_PARAMETER;
353 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
354 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
357 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
358 *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
360 dos_mode_debug_print(__func__, *pattr);
362 return NT_STATUS_OK;
365 NTSTATUS fget_ea_dos_attribute(struct files_struct *fsp,
366 uint32_t *pattr)
368 DATA_BLOB blob;
369 ssize_t sizeret;
370 fstring attrstr;
371 NTSTATUS status;
373 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
374 return NT_STATUS_NOT_IMPLEMENTED;
377 /* Don't reset pattr to zero as we may already have filename-based attributes we
378 need to preserve. */
380 sizeret = SMB_VFS_FGETXATTR(fsp,
381 SAMBA_XATTR_DOS_ATTRIB,
382 attrstr,
383 sizeof(attrstr));
384 if (sizeret == -1 && ( errno == EPERM || errno == EACCES )) {
385 /* we may also retrieve dos attribs for unreadable files, this
386 is why we'll retry as root. We don't use root in the first
387 run because in cases like NFS, root might have even less
388 rights than the real user
390 become_root();
391 sizeret = SMB_VFS_FGETXATTR(fsp,
392 SAMBA_XATTR_DOS_ATTRIB,
393 attrstr,
394 sizeof(attrstr));
395 unbecome_root();
397 if (sizeret == -1) {
398 DBG_INFO("Cannot get attribute "
399 "from EA on file %s: Error = %s\n",
400 fsp_str_dbg(fsp), strerror(errno));
401 return map_nt_error_from_unix(errno);
404 blob.data = (uint8_t *)attrstr;
405 blob.length = sizeret;
407 status = parse_dos_attribute_blob(fsp->fsp_name, blob, pattr);
408 if (!NT_STATUS_IS_OK(status)) {
409 return status;
412 return NT_STATUS_OK;
415 /****************************************************************************
416 Set DOS attributes in an EA.
417 Also sets the create time.
418 ****************************************************************************/
420 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
421 struct smb_filename *smb_fname,
422 uint32_t dosmode)
424 struct xattr_DOSATTRIB dosattrib = { .version = 0, };
425 enum ndr_err_code ndr_err;
426 DATA_BLOB blob = { .data = NULL, };
427 struct timespec btime;
428 int ret;
430 if (!lp_store_dos_attributes(SNUM(conn))) {
431 return NT_STATUS_NOT_IMPLEMENTED;
434 if (smb_fname->fsp == NULL) {
435 /* symlink */
436 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
439 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
440 * vfs_default via DMAPI if that is enabled.
442 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
444 dosattrib.version = 5;
445 dosattrib.info.info5.valid_flags = XATTR_DOSINFO_ATTRIB |
446 XATTR_DOSINFO_CREATE_TIME;
447 dosattrib.info.info5.attrib = dosmode;
448 dosattrib.info.info5.create_time = full_timespec_to_nt_time(
449 &smb_fname->st.st_ex_btime);
451 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
452 (unsigned int)dosmode,
453 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
454 smb_fname_str_dbg(smb_fname) ));
456 ndr_err = ndr_push_struct_blob(
457 &blob, talloc_tos(), &dosattrib,
458 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
460 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
461 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
462 ndr_errstr(ndr_err)));
463 return ndr_map_error2ntstatus(ndr_err);
466 if (blob.data == NULL || blob.length == 0) {
467 /* Should this be INTERNAL_ERROR? */
468 return NT_STATUS_INVALID_PARAMETER;
471 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
472 SAMBA_XATTR_DOS_ATTRIB,
473 blob.data, blob.length, 0);
474 if (ret != 0) {
475 NTSTATUS status = NT_STATUS_OK;
476 bool set_dosmode_ok = false;
478 if ((errno != EPERM) && (errno != EACCES)) {
479 DBG_INFO("Cannot set "
480 "attribute EA on file %s: Error = %s\n",
481 smb_fname_str_dbg(smb_fname), strerror(errno));
482 return map_nt_error_from_unix(errno);
485 /* We want DOS semantics, ie allow non owner with write permission to change the
486 bits on a file. Just like file_ntimes below.
489 /* Check if we have write access. */
490 if (!CAN_WRITE(conn)) {
491 return NT_STATUS_ACCESS_DENIED;
494 status = smbd_check_access_rights_fsp(conn->cwd_fsp,
495 smb_fname->fsp,
496 false,
497 FILE_WRITE_ATTRIBUTES);
498 if (NT_STATUS_IS_OK(status)) {
499 set_dosmode_ok = true;
502 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
503 set_dosmode_ok = can_write_to_fsp(smb_fname->fsp);
506 if (!set_dosmode_ok) {
507 return NT_STATUS_ACCESS_DENIED;
510 become_root();
511 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
512 SAMBA_XATTR_DOS_ATTRIB,
513 blob.data, blob.length, 0);
514 if (ret == 0) {
515 status = NT_STATUS_OK;
517 unbecome_root();
518 if (!NT_STATUS_IS_OK(status)) {
519 return status;
524 * We correctly stored the create time.
525 * We *always* set XATTR_DOSINFO_CREATE_TIME,
526 * so now it can no longer be considered
527 * calculated. Make sure to use the value rounded
528 * to NTTIME granularity we've stored in the xattr.
530 btime = nt_time_to_full_timespec(dosattrib.info.info5.create_time);
531 update_stat_ex_create_time(&smb_fname->st, btime);
533 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
534 (unsigned int)dosmode,
535 smb_fname_str_dbg(smb_fname)));
536 return NT_STATUS_OK;
539 static uint32_t
540 dos_mode_from_name(connection_struct *conn, const char *name, uint32_t dosmode)
542 const char *p = NULL;
543 uint32_t result = dosmode;
545 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
546 lp_hide_dot_files(SNUM(conn)))
548 p = strrchr_m(name, '/');
549 if (p) {
550 p++;
551 } else {
552 p = name;
555 /* Only . and .. are not hidden. */
556 if ((p[0] == '.') && !(ISDOT(p) || ISDOTDOT(p))) {
557 result |= FILE_ATTRIBUTE_HIDDEN;
561 if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, name)) {
562 result |= FILE_ATTRIBUTE_HIDDEN;
565 return result;
568 /****************************************************************************
569 Change a unix mode to a dos mode for an ms dfs link.
570 ****************************************************************************/
572 uint32_t dos_mode_msdfs(connection_struct *conn,
573 const char *name,
574 const struct stat_ex *st)
576 uint32_t result = 0;
578 DEBUG(8, ("dos_mode_msdfs: %s\n", name));
580 if (!VALID_STAT(*st)) {
581 return 0;
584 result = dos_mode_from_name(conn, name, result);
585 result |= dos_mode_from_sbuf(conn, st, NULL);
587 if (result == 0) {
588 result = FILE_ATTRIBUTE_NORMAL;
591 result = filter_mode_by_protocol(result);
594 * Add in that it is a reparse point
596 result |= FILE_ATTRIBUTE_REPARSE_POINT;
598 dos_mode_debug_print(__func__, result);
600 return(result);
604 * check whether a file or directory is flagged as compressed.
606 static NTSTATUS dos_mode_check_compressed(struct files_struct *fsp,
607 bool *is_compressed)
609 NTSTATUS status;
610 uint16_t compression_fmt;
612 status = SMB_VFS_FGET_COMPRESSION(
613 fsp->conn, talloc_tos(), fsp, &compression_fmt);
614 if (!NT_STATUS_IS_OK(status)) {
615 return status;
618 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
619 *is_compressed = true;
620 } else {
621 *is_compressed = false;
623 return NT_STATUS_OK;
626 static uint32_t dos_mode_post(uint32_t dosmode,
627 struct files_struct *fsp,
628 const char *func)
630 struct smb_filename *smb_fname = NULL;
631 NTSTATUS status;
633 if (fsp != NULL) {
634 smb_fname = fsp->fsp_name;
636 SMB_ASSERT(smb_fname != NULL);
639 * According to MS-FSA a stream name does not have
640 * separate DOS attribute metadata, so we must return
641 * the DOS attribute from the base filename. With one caveat,
642 * a non-default stream name can never be a directory.
644 * As this is common to all streams data stores, we handle
645 * it here instead of inside all stream VFS modules.
647 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
650 if (is_named_stream(smb_fname)) {
651 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
652 dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
655 if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
656 bool compressed = false;
658 status = dos_mode_check_compressed(fsp, &compressed);
659 if (NT_STATUS_IS_OK(status) && compressed) {
660 dosmode |= FILE_ATTRIBUTE_COMPRESSED;
664 dosmode |= dos_mode_from_name(fsp->conn, smb_fname->base_name, dosmode);
666 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
667 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
668 } else if (dosmode == 0) {
669 dosmode = FILE_ATTRIBUTE_NORMAL;
672 dosmode = filter_mode_by_protocol(dosmode);
674 dos_mode_debug_print(func, dosmode);
675 return dosmode;
678 /****************************************************************************
679 Change a unix mode to a dos mode.
680 May also read the create timespec into the stat struct in smb_fname
681 if "store dos attributes" is true.
682 ****************************************************************************/
684 uint32_t fdos_mode(struct files_struct *fsp)
686 uint32_t result = 0;
687 NTSTATUS status = NT_STATUS_OK;
689 if (fsp == NULL) {
691 * The pathological case where a caller does
692 * fdos_mode(smb_fname->fsp) passing a pathref fsp. But as
693 * smb_fname points at a symlink in POSIX context smb_fname->fsp
694 * is NULL.
696 return FILE_ATTRIBUTE_NORMAL;
699 DBG_DEBUG("%s\n", fsp_str_dbg(fsp));
701 if (fsp->fake_file_handle != NULL) {
702 return dosmode_from_fake_filehandle(fsp->fake_file_handle);
705 if (!VALID_STAT(fsp->fsp_name->st)) {
706 return 0;
709 if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) {
710 return FILE_ATTRIBUTE_NORMAL;
713 if (fsp->fsp_name->st.cached_dos_attributes != FILE_ATTRIBUTES_INVALID) {
714 return fsp->fsp_name->st.cached_dos_attributes;
717 /* Get the DOS attributes via the VFS if we can */
718 status = vfs_fget_dos_attributes(fsp, &result);
719 if (!NT_STATUS_IS_OK(status)) {
721 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
723 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
724 result |= dos_mode_from_sbuf(fsp->conn,
725 &fsp->fsp_name->st,
726 fsp);
730 fsp->fsp_name->st.cached_dos_attributes = dos_mode_post(result, fsp, __func__);
731 return fsp->fsp_name->st.cached_dos_attributes;
734 struct dos_mode_at_state {
735 files_struct *dir_fsp;
736 struct smb_filename *smb_fname;
737 uint32_t dosmode;
740 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
742 struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
743 struct tevent_context *ev,
744 files_struct *dir_fsp,
745 struct smb_filename *smb_fname)
747 struct tevent_req *req = NULL;
748 struct dos_mode_at_state *state = NULL;
749 struct tevent_req *subreq = NULL;
751 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
753 req = tevent_req_create(mem_ctx, &state,
754 struct dos_mode_at_state);
755 if (req == NULL) {
756 return NULL;
759 *state = (struct dos_mode_at_state) {
760 .dir_fsp = dir_fsp,
761 .smb_fname = smb_fname,
764 if (!VALID_STAT(smb_fname->st)) {
765 tevent_req_done(req);
766 return tevent_req_post(req, ev);
769 if (smb_fname->fsp == NULL) {
770 if (ISDOTDOT(smb_fname->base_name)) {
772 * smb_fname->fsp is explicitly closed
773 * for ".." to prevent meta-data leakage.
775 state->dosmode = FILE_ATTRIBUTE_DIRECTORY;
776 } else {
778 * This is a symlink in POSIX context.
779 * FIXME ? Should we move to returning
780 * FILE_ATTRIBUTE_REPARSE_POINT here ?
782 state->dosmode = FILE_ATTRIBUTE_NORMAL;
784 tevent_req_done(req);
785 return tevent_req_post(req, ev);
788 subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
790 dir_fsp,
791 smb_fname);
792 if (tevent_req_nomem(subreq, req)) {
793 return tevent_req_post(req, ev);
795 tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
797 return req;
800 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
802 struct tevent_req *req =
803 tevent_req_callback_data(subreq,
804 struct tevent_req);
805 struct dos_mode_at_state *state =
806 tevent_req_data(req,
807 struct dos_mode_at_state);
808 struct vfs_aio_state aio_state;
809 NTSTATUS status;
810 bool ok;
813 * Make sure we run as the user again
815 ok = change_to_user_and_service_by_fsp(state->dir_fsp);
816 SMB_ASSERT(ok);
818 status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
819 &aio_state,
820 &state->dosmode);
821 TALLOC_FREE(subreq);
822 if (!NT_STATUS_IS_OK(status)) {
824 * Both the sync dos_mode() as well as the async
825 * dos_mode_at_[send|recv] have no real error return, the only
826 * unhandled error is when the stat info in smb_fname is not
827 * valid (cf the checks in dos_mode() and dos_mode_at_send().
829 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
830 * dos_mode_post() which also does the mapping of a last resort
831 * from S_IFMT(st_mode).
833 * Only if we get NT_STATUS_NOT_IMPLEMENTED or
834 * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
835 * fallback to sync processing.
837 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) &&
838 !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED))
841 * state->dosmode should still be 0, but reset
842 * it to be sure.
844 state->dosmode = 0;
845 status = NT_STATUS_OK;
848 if (NT_STATUS_IS_OK(status)) {
849 state->dosmode = dos_mode_post(state->dosmode,
850 state->smb_fname->fsp,
851 __func__);
852 tevent_req_done(req);
853 return;
857 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
860 state->dosmode = fdos_mode(state->smb_fname->fsp);
861 tevent_req_done(req);
862 return;
865 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
867 struct dos_mode_at_state *state =
868 tevent_req_data(req,
869 struct dos_mode_at_state);
870 NTSTATUS status;
872 if (tevent_req_is_nterror(req, &status)) {
873 tevent_req_received(req);
874 return status;
877 *dosmode = state->dosmode;
878 tevent_req_received(req);
879 return NT_STATUS_OK;
882 /*******************************************************************
883 chmod a file - but preserve some bits.
884 If "store dos attributes" is also set it will store the create time
885 from the stat struct in smb_fname (in NTTIME format) in the EA
886 attribute also.
887 ********************************************************************/
889 int file_set_dosmode(connection_struct *conn,
890 struct smb_filename *smb_fname,
891 uint32_t dosmode,
892 struct smb_filename *parent_dir,
893 bool newfile)
895 int mask=0;
896 mode_t tmp;
897 mode_t unixmode;
898 int ret = -1;
899 NTSTATUS status;
901 if (!CAN_WRITE(conn)) {
902 errno = EROFS;
903 return -1;
906 if (S_ISLNK(smb_fname->st.st_ex_mode)) {
907 /* A symlink in POSIX context, ignore */
908 return 0;
911 if ((S_ISDIR(smb_fname->st.st_ex_mode)) &&
912 (dosmode & FILE_ATTRIBUTE_TEMPORARY))
914 errno = EINVAL;
915 return -1;
918 dosmode &= SAMBA_ATTRIBUTES_MASK;
920 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
921 dosmode, smb_fname_str_dbg(smb_fname)));
923 unixmode = smb_fname->st.st_ex_mode;
925 if (smb_fname->fsp != NULL) {
926 get_acl_group_bits(
927 conn, smb_fname->fsp, &smb_fname->st.st_ex_mode);
930 if (S_ISDIR(smb_fname->st.st_ex_mode))
931 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
932 else
933 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
935 if (smb_fname->fsp != NULL) {
936 /* Store the DOS attributes in an EA by preference. */
937 status = SMB_VFS_FSET_DOS_ATTRIBUTES(
938 conn, metadata_fsp(smb_fname->fsp), dosmode);
939 } else {
940 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
943 if (NT_STATUS_IS_OK(status)) {
944 smb_fname->st.cached_dos_attributes = dosmode;
945 ret = 0;
946 goto done;
950 * Only fall back to using UNIX modes if
951 * we get NOT_IMPLEMENTED.
953 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
954 errno = map_errno_from_nt_status(status);
955 return -1;
958 /* Fall back to UNIX modes. */
959 unixmode = unix_mode(
960 conn,
961 dosmode,
962 smb_fname,
963 parent_dir != NULL ? parent_dir->fsp : NULL);
965 /* preserve the file type bits */
966 mask |= S_IFMT;
968 /* preserve the s bits */
969 mask |= (S_ISUID | S_ISGID);
971 /* preserve the t bit */
972 #ifdef S_ISVTX
973 mask |= S_ISVTX;
974 #endif
976 /* possibly preserve the x bits */
977 if (!MAP_ARCHIVE(conn))
978 mask |= S_IXUSR;
979 if (!MAP_SYSTEM(conn))
980 mask |= S_IXGRP;
981 if (!MAP_HIDDEN(conn))
982 mask |= S_IXOTH;
984 unixmode |= (smb_fname->st.st_ex_mode & mask);
986 /* if we previously had any r bits set then leave them alone */
987 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
988 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
989 unixmode |= tmp;
992 /* if we previously had any w bits set then leave them alone
993 whilst adding in the new w bits, if the new mode is not rdonly */
994 if (!(dosmode & FILE_ATTRIBUTE_READONLY)) {
995 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
999 * From the chmod 2 man page:
1001 * "If the calling process is not privileged, and the group of the file
1002 * does not match the effective group ID of the process or one of its
1003 * supplementary group IDs, the S_ISGID bit will be turned off, but
1004 * this will not cause an error to be returned."
1006 * Simply refuse to do the chmod in this case.
1009 if (S_ISDIR(smb_fname->st.st_ex_mode) &&
1010 (unixmode & S_ISGID) &&
1011 geteuid() != sec_initial_uid() &&
1012 !current_user_in_group(conn, smb_fname->st.st_ex_gid))
1014 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1015 "set for directory %s\n",
1016 smb_fname_str_dbg(smb_fname)));
1017 errno = EPERM;
1018 return -1;
1021 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1022 if (ret == 0) {
1023 goto done;
1026 if((errno != EPERM) && (errno != EACCES))
1027 return -1;
1029 if(!lp_dos_filemode(SNUM(conn)))
1030 return -1;
1032 /* We want DOS semantics, ie allow non owner with write permission to change the
1033 bits on a file. Just like file_ntimes below.
1036 if (!can_write_to_fsp(smb_fname->fsp))
1038 errno = EACCES;
1039 return -1;
1042 become_root();
1043 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1044 unbecome_root();
1046 done:
1047 if (!newfile) {
1048 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1049 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1050 smb_fname->base_name);
1052 if (ret == 0) {
1053 smb_fname->st.st_ex_mode = unixmode;
1056 return( ret );
1060 NTSTATUS file_set_sparse(connection_struct *conn,
1061 files_struct *fsp,
1062 bool sparse)
1064 const struct loadparm_substitution *lp_sub =
1065 loadparm_s3_global_substitution();
1066 uint32_t old_dosmode;
1067 uint32_t new_dosmode;
1068 NTSTATUS status;
1070 if (!CAN_WRITE(conn)) {
1071 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1072 "on readonly share[%s]\n",
1073 smb_fname_str_dbg(fsp->fsp_name),
1074 sparse,
1075 lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
1076 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1080 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1081 * following access flags are granted.
1083 if ((fsp->access_mask & (FILE_WRITE_DATA
1084 | FILE_WRITE_ATTRIBUTES
1085 | SEC_FILE_APPEND_DATA)) == 0) {
1086 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1087 "access_mask[0x%08X] - access denied\n",
1088 smb_fname_str_dbg(fsp->fsp_name),
1089 sparse,
1090 fsp->access_mask));
1091 return NT_STATUS_ACCESS_DENIED;
1094 if (fsp->fsp_flags.is_directory) {
1095 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1096 (sparse ? "set" : "clear"),
1097 smb_fname_str_dbg(fsp->fsp_name)));
1098 return NT_STATUS_INVALID_PARAMETER;
1101 if (IS_IPC(conn) || IS_PRINT(conn)) {
1102 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1103 (sparse ? "set" : "clear")));
1104 return NT_STATUS_INVALID_PARAMETER;
1107 if (fsp_is_alternate_stream(fsp)) {
1109 * MS-FSA 2.1.1.5 IsSparse
1111 * This is a per stream attribute, but our backends don't
1112 * support it a consistent way, therefore just pretend
1113 * success and ignore the request.
1115 DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
1116 "[%s]\n", fsp_str_dbg(fsp));
1117 return NT_STATUS_OK;
1120 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1121 sparse, smb_fname_str_dbg(fsp->fsp_name)));
1123 if (!lp_store_dos_attributes(SNUM(conn))) {
1124 return NT_STATUS_INVALID_DEVICE_REQUEST;
1127 status = vfs_stat_fsp(fsp);
1128 if (!NT_STATUS_IS_OK(status)) {
1129 return status;
1132 old_dosmode = fdos_mode(fsp);
1134 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1135 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1136 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1137 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1138 } else {
1139 return NT_STATUS_OK;
1142 /* Store the DOS attributes in an EA. */
1143 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1144 if (!NT_STATUS_IS_OK(status)) {
1145 return status;
1148 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1149 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1150 fsp->fsp_name->base_name);
1152 fsp->fsp_name->st.cached_dos_attributes = new_dosmode;
1153 fsp->fsp_flags.is_sparse = sparse;
1155 return NT_STATUS_OK;
1158 /*******************************************************************
1159 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1160 than POSIX.
1161 *******************************************************************/
1163 int file_ntimes(connection_struct *conn,
1164 files_struct *fsp,
1165 struct smb_file_time *ft)
1167 int ret = -1;
1169 errno = 0;
1171 DBG_INFO("actime: %s",
1172 time_to_asc(convert_timespec_to_time_t(ft->atime)));
1173 DBG_INFO("modtime: %s",
1174 time_to_asc(convert_timespec_to_time_t(ft->mtime)));
1175 DBG_INFO("ctime: %s",
1176 time_to_asc(convert_timespec_to_time_t(ft->ctime)));
1177 DBG_INFO("createtime: %s",
1178 time_to_asc(convert_timespec_to_time_t(ft->create_time)));
1180 /* Don't update the time on read-only shares */
1181 /* We need this as set_filetime (which can be called on
1182 close and other paths) can end up calling this function
1183 without the NEED_WRITE protection. Found by :
1184 Leo Weppelman <leo@wau.mis.ah.nl>
1187 if (!CAN_WRITE(conn)) {
1188 return 0;
1191 if (SMB_VFS_FNTIMES(fsp, ft) == 0) {
1192 return 0;
1195 if((errno != EPERM) && (errno != EACCES)) {
1196 return -1;
1199 if(!lp_dos_filetimes(SNUM(conn))) {
1200 return -1;
1203 /* We have permission (given by the Samba admin) to
1204 break POSIX semantics and allow a user to change
1205 the time on a file they don't own but can write to
1206 (as DOS does).
1209 /* Check if we have write access. */
1210 if (can_write_to_fsp(fsp)) {
1211 /* We are allowed to become root and change the filetime. */
1212 become_root();
1213 ret = SMB_VFS_FNTIMES(fsp, ft);
1214 unbecome_root();
1217 return ret;
1220 /******************************************************************
1221 Force a "sticky" write time on a pathname. This will always be
1222 returned on all future write time queries and set on close.
1223 ******************************************************************/
1225 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1227 if (is_omit_timespec(&mtime)) {
1228 return true;
1231 if (!set_sticky_write_time(fileid, mtime)) {
1232 return false;
1235 return true;
1238 /******************************************************************
1239 Force a "sticky" write time on an fsp. This will always be
1240 returned on all future write time queries and set on close.
1241 ******************************************************************/
1243 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1245 if (is_omit_timespec(&mtime)) {
1246 return true;
1249 fsp->fsp_flags.write_time_forced = true;
1250 TALLOC_FREE(fsp->update_write_time_event);
1252 return set_sticky_write_time_path(fsp->file_id, mtime);
1255 /******************************************************************
1256 Set a create time EA.
1257 ******************************************************************/
1259 NTSTATUS set_create_timespec_ea(struct files_struct *fsp,
1260 struct timespec create_time)
1262 uint32_t dosmode;
1263 int ret;
1265 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
1266 return NT_STATUS_OK;
1269 dosmode = fdos_mode(fsp);
1271 fsp->fsp_name->st.st_ex_btime = create_time;
1272 ret = file_set_dosmode(fsp->conn, fsp->fsp_name, dosmode, NULL, false);
1273 if (ret == -1) {
1274 return map_nt_error_from_unix(errno);
1277 DBG_DEBUG("wrote create time EA for file %s\n",
1278 smb_fname_str_dbg(fsp->fsp_name));
1280 return NT_STATUS_OK;
1283 /******************************************************************
1284 Return a create time.
1285 ******************************************************************/
1287 struct timespec get_create_timespec(connection_struct *conn,
1288 struct files_struct *fsp,
1289 const struct smb_filename *smb_fname)
1291 return smb_fname->st.st_ex_btime;
1294 /******************************************************************
1295 Return a change time (may look at EA in future).
1296 ******************************************************************/
1298 struct timespec get_change_timespec(connection_struct *conn,
1299 struct files_struct *fsp,
1300 const struct smb_filename *smb_fname)
1302 return smb_fname->st.st_ex_mtime;