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/>.
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"
32 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
33 const struct smb_filename *smb_fname,
34 files_struct **ret_fsp,
37 static void dos_mode_debug_print(const char *func, uint32_t mode)
41 if (DEBUGLEVEL < DBGLVL_INFO) {
47 if (mode & FILE_ATTRIBUTE_HIDDEN) {
48 fstrcat(modestr, "h");
50 if (mode & FILE_ATTRIBUTE_READONLY) {
51 fstrcat(modestr, "r");
53 if (mode & FILE_ATTRIBUTE_SYSTEM) {
54 fstrcat(modestr, "s");
56 if (mode & FILE_ATTRIBUTE_DIRECTORY) {
57 fstrcat(modestr, "d");
59 if (mode & FILE_ATTRIBUTE_ARCHIVE) {
60 fstrcat(modestr, "a");
62 if (mode & FILE_ATTRIBUTE_SPARSE) {
63 fstrcat(modestr, "[sparse]");
65 if (mode & FILE_ATTRIBUTE_OFFLINE) {
66 fstrcat(modestr, "[offline]");
68 if (mode & FILE_ATTRIBUTE_COMPRESSED) {
69 fstrcat(modestr, "[compressed]");
72 DBG_INFO("%s returning (0x%x): \"%s\"\n", func, (unsigned)mode,
76 static uint32_t filter_mode_by_protocol(uint32_t mode)
78 if (get_Protocol() <= PROTOCOL_LANMAN2) {
79 DEBUG(10,("filter_mode_by_protocol: "
80 "filtering result 0x%x to 0x%x\n",
82 (unsigned int)(mode & 0x3f) ));
88 /****************************************************************************
89 Change a dos mode to a unix mode.
90 Base permission for files:
91 if creating file and inheriting (i.e. parent_dir != NULL)
92 apply read/write bits from parent directory.
94 everybody gets read bit set
95 dos readonly is represented in unix by removing everyone's write bit
96 dos archive is represented in unix by the user's execute bit
97 dos system is represented in unix by the group's execute bit
98 dos hidden is represented in unix by the other's execute bit
100 Then apply create mask,
103 Base permission for directories:
104 dos directory is represented in unix by unix's dir bit and the exec bit
106 Then apply create mask,
109 ****************************************************************************/
111 mode_t unix_mode(connection_struct *conn, int dosmode,
112 const struct smb_filename *smb_fname,
113 struct smb_filename *smb_fname_parent)
115 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
116 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
119 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
120 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
123 if ((smb_fname_parent != NULL) && lp_inherit_permissions(SNUM(conn))) {
124 DBG_DEBUG("[%s] inheriting from [%s]\n",
125 smb_fname_str_dbg(smb_fname),
126 smb_fname_str_dbg(smb_fname_parent));
128 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
129 DBG_ERR("stat failed [%s]: %s\n",
130 smb_fname_str_dbg(smb_fname_parent),
132 return(0); /* *** shouldn't happen! *** */
135 /* Save for later - but explicitly remove setuid bit for safety. */
136 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
137 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
138 smb_fname_str_dbg(smb_fname), (int)dir_mode));
143 if (IS_DOS_DIR(dosmode)) {
144 /* We never make directories read only for the owner as under DOS a user
145 can always create a file in a read-only directory. */
146 result |= (S_IFDIR | S_IWUSR);
149 /* Inherit mode of parent directory. */
152 /* Provisionally add all 'x' bits */
153 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
155 /* Apply directory mask */
156 result &= lp_directory_mask(SNUM(conn));
157 /* Add in force bits */
158 result |= lp_force_directory_mode(SNUM(conn));
161 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
164 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
167 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
171 /* Inherit 666 component of parent directory mode */
172 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
174 /* Apply mode mask */
175 result &= lp_create_mask(SNUM(conn));
176 /* Add in force bits */
177 result |= lp_force_create_mode(SNUM(conn));
181 DBG_INFO("unix_mode(%s) returning 0%o\n",
182 smb_fname_str_dbg(smb_fname), (int)result);
187 /****************************************************************************
188 Change a unix mode to a dos mode.
189 ****************************************************************************/
191 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
192 const struct smb_filename *smb_fname)
195 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
197 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
198 /* if we can find out if a file is immutable we should report it r/o */
199 if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
200 result |= FILE_ATTRIBUTE_READONLY;
203 if (ro_opts == MAP_READONLY_YES) {
204 /* Original Samba method - map inverse of user "w" bit. */
205 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
206 result |= FILE_ATTRIBUTE_READONLY;
208 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
209 /* Check actual permissions for read-only. */
210 if (!can_write_to_file(conn,
214 result |= FILE_ATTRIBUTE_READONLY;
216 } /* Else never set the readonly bit. */
218 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
219 result |= FILE_ATTRIBUTE_ARCHIVE;
221 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
222 result |= FILE_ATTRIBUTE_SYSTEM;
224 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
225 result |= FILE_ATTRIBUTE_HIDDEN;
227 if (S_ISDIR(smb_fname->st.st_ex_mode))
228 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
230 dos_mode_debug_print(__func__, result);
235 /****************************************************************************
236 Get DOS attributes from an EA.
237 This can also pull the create time into the stat struct inside smb_fname.
238 ****************************************************************************/
240 NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
244 struct xattr_DOSATTRIB dosattrib;
245 enum ndr_err_code ndr_err;
248 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
249 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
251 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
252 DBG_WARNING("bad ndr decode "
253 "from EA on file %s: Error = %s\n",
254 smb_fname_str_dbg(smb_fname),
255 ndr_errstr(ndr_err));
256 return ndr_map_error2ntstatus(ndr_err);
259 DBG_DEBUG("%s attr = %s\n",
260 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex);
262 switch (dosattrib.version) {
264 dosattr = dosattrib.info.compatinfoFFFF.attrib;
267 dosattr = dosattrib.info.info1.attrib;
268 if (!null_nttime(dosattrib.info.info1.create_time)) {
269 struct timespec create_time =
270 nt_time_to_unix_timespec(
271 dosattrib.info.info1.create_time);
273 update_stat_ex_create_time(&smb_fname->st,
276 DBG_DEBUG("file %s case 1 set btime %s\n",
277 smb_fname_str_dbg(smb_fname),
278 time_to_asc(convert_timespec_to_time_t(
283 dosattr = dosattrib.info.oldinfo2.attrib;
284 /* Don't know what flags to check for this case. */
287 dosattr = dosattrib.info.info3.attrib;
288 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
289 !null_nttime(dosattrib.info.info3.create_time)) {
290 struct timespec create_time =
291 nt_time_to_full_timespec(
292 dosattrib.info.info3.create_time);
294 update_stat_ex_create_time(&smb_fname->st,
297 DBG_DEBUG("file %s case 3 set btime %s\n",
298 smb_fname_str_dbg(smb_fname),
299 time_to_asc(convert_timespec_to_time_t(
305 struct xattr_DosInfo4 *info = &dosattrib.info.info4;
307 dosattr = info->attrib;
309 if ((info->valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
310 !null_nttime(info->create_time))
312 struct timespec creat_time;
314 creat_time = nt_time_to_full_timespec(info->create_time);
315 update_stat_ex_create_time(&smb_fname->st, creat_time);
317 DBG_DEBUG("file [%s] creation time [%s]\n",
318 smb_fname_str_dbg(smb_fname),
319 nt_time_string(talloc_tos(), info->create_time));
322 if (info->valid_flags & XATTR_DOSINFO_ITIME) {
323 struct timespec itime;
326 itime = nt_time_to_unix_timespec(info->itime);
327 if (smb_fname->st.st_ex_iflags &
328 ST_EX_IFLAG_CALCULATED_ITIME)
330 update_stat_ex_itime(&smb_fname->st, itime);
333 file_id = make_file_id_from_itime(&smb_fname->st);
334 if (smb_fname->st.st_ex_iflags &
335 ST_EX_IFLAG_CALCULATED_FILE_ID)
337 update_stat_ex_file_id(&smb_fname->st, file_id);
340 DBG_DEBUG("file [%s] itime [%s] fileid [%"PRIx64"]\n",
341 smb_fname_str_dbg(smb_fname),
342 nt_time_string(talloc_tos(), info->itime),
348 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
349 smb_fname_str_dbg(smb_fname), blob.data);
350 /* Should this be INTERNAL_ERROR? */
351 return NT_STATUS_INVALID_PARAMETER;
354 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
355 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
358 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
359 *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
361 dos_mode_debug_print(__func__, *pattr);
366 NTSTATUS get_ea_dos_attribute(connection_struct *conn,
367 struct smb_filename *smb_fname,
375 if (!lp_store_dos_attributes(SNUM(conn))) {
376 return NT_STATUS_NOT_IMPLEMENTED;
379 /* Don't reset pattr to zero as we may already have filename-based attributes we
382 sizeret = SMB_VFS_GETXATTR(conn, smb_fname,
383 SAMBA_XATTR_DOS_ATTRIB, attrstr,
385 if (sizeret == -1 && errno == EACCES) {
389 * According to MS-FSA 2.1.5.1.2.1 "Algorithm to Check Access to
390 * an Existing File" FILE_LIST_DIRECTORY on a directory implies
391 * FILE_READ_ATTRIBUTES for directory entries. Being able to
392 * stat() a file implies FILE_LIST_DIRECTORY for the directory
393 * containing the file.
396 if (!VALID_STAT(smb_fname->st)) {
398 * Safety net: dos_mode() already checks this, but as we
399 * become root based on this, add an additional layer of
402 DBG_ERR("Rejecting root override, invalid stat [%s]\n",
403 smb_fname_str_dbg(smb_fname));
404 return NT_STATUS_ACCESS_DENIED;
408 sizeret = SMB_VFS_GETXATTR(conn, smb_fname,
409 SAMBA_XATTR_DOS_ATTRIB,
417 if (saved_errno != 0) {
422 DBG_INFO("Cannot get attribute "
423 "from EA on file %s: Error = %s\n",
424 smb_fname_str_dbg(smb_fname), strerror(errno));
425 return map_nt_error_from_unix(errno);
428 blob.data = (uint8_t *)attrstr;
429 blob.length = sizeret;
431 status = parse_dos_attribute_blob(smb_fname, blob, pattr);
432 if (!NT_STATUS_IS_OK(status)) {
439 /****************************************************************************
440 Set DOS attributes in an EA.
441 Also sets the create time.
442 ****************************************************************************/
444 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
445 const struct smb_filename *smb_fname,
448 struct xattr_DOSATTRIB dosattrib;
449 enum ndr_err_code ndr_err;
453 if (!lp_store_dos_attributes(SNUM(conn))) {
454 return NT_STATUS_NOT_IMPLEMENTED;
458 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
459 * vfs_default via DMAPI if that is enabled.
461 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
463 ZERO_STRUCT(dosattrib);
466 dosattrib.version = 4;
467 dosattrib.info.info4.valid_flags = XATTR_DOSINFO_ATTRIB |
468 XATTR_DOSINFO_CREATE_TIME;
469 dosattrib.info.info4.attrib = dosmode;
470 dosattrib.info.info4.create_time = full_timespec_to_nt_time(
471 &smb_fname->st.st_ex_btime);
473 if (!(smb_fname->st.st_ex_iflags & ST_EX_IFLAG_CALCULATED_ITIME)) {
474 dosattrib.info.info4.valid_flags |= XATTR_DOSINFO_ITIME;
475 dosattrib.info.info4.itime = full_timespec_to_nt_time(
476 &smb_fname->st.st_ex_itime);
479 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
480 (unsigned int)dosmode,
481 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
482 smb_fname_str_dbg(smb_fname) ));
484 ndr_err = ndr_push_struct_blob(
485 &blob, talloc_tos(), &dosattrib,
486 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
488 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
489 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
490 ndr_errstr(ndr_err)));
491 return ndr_map_error2ntstatus(ndr_err);
494 if (blob.data == NULL || blob.length == 0) {
495 /* Should this be INTERNAL_ERROR? */
496 return NT_STATUS_INVALID_PARAMETER;
499 ret = SMB_VFS_SETXATTR(conn, smb_fname,
500 SAMBA_XATTR_DOS_ATTRIB,
501 blob.data, blob.length, 0);
503 NTSTATUS status = NT_STATUS_OK;
504 bool need_close = false;
505 files_struct *fsp = NULL;
506 bool set_dosmode_ok = false;
508 if ((errno != EPERM) && (errno != EACCES)) {
509 DBG_INFO("Cannot set "
510 "attribute EA on file %s: Error = %s\n",
511 smb_fname_str_dbg(smb_fname), strerror(errno));
512 return map_nt_error_from_unix(errno);
515 /* We want DOS semantics, ie allow non owner with write permission to change the
516 bits on a file. Just like file_ntimes below.
519 /* Check if we have write access. */
520 if (!CAN_WRITE(conn)) {
521 return NT_STATUS_ACCESS_DENIED;
524 status = smbd_check_access_rights(conn,
528 FILE_WRITE_ATTRIBUTES);
529 if (NT_STATUS_IS_OK(status)) {
530 set_dosmode_ok = true;
533 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
534 set_dosmode_ok = can_write_to_file(conn,
539 if (!set_dosmode_ok) {
540 return NT_STATUS_ACCESS_DENIED;
544 * We need to get an open file handle to do the
545 * metadata operation under root.
548 status = get_file_handle_for_metadata(conn,
552 if (!NT_STATUS_IS_OK(status)) {
557 ret = SMB_VFS_FSETXATTR(fsp,
558 SAMBA_XATTR_DOS_ATTRIB,
559 blob.data, blob.length, 0);
561 status = NT_STATUS_OK;
565 close_file(NULL, fsp, NORMAL_CLOSE);
569 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
570 (unsigned int)dosmode,
571 smb_fname_str_dbg(smb_fname)));
575 /****************************************************************************
576 Change a unix mode to a dos mode for an ms dfs link.
577 ****************************************************************************/
579 uint32_t dos_mode_msdfs(connection_struct *conn,
580 const struct smb_filename *smb_fname)
584 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
586 if (!VALID_STAT(smb_fname->st)) {
590 /* First do any modifications that depend on the path name. */
591 /* hide files with a name starting with a . */
592 if (lp_hide_dot_files(SNUM(conn))) {
593 const char *p = strrchr_m(smb_fname->base_name, '/');
597 p = smb_fname->base_name;
600 /* Only . and .. are not hidden. */
601 if (p[0] == '.' && !((p[1] == '\0') ||
602 (p[1] == '.' && p[2] == '\0'))) {
603 result |= FILE_ATTRIBUTE_HIDDEN;
607 result |= dos_mode_from_sbuf(conn, smb_fname);
609 /* Optimization : Only call is_hidden_path if it's not already
611 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
612 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
613 result |= FILE_ATTRIBUTE_HIDDEN;
617 result = FILE_ATTRIBUTE_NORMAL;
620 result = filter_mode_by_protocol(result);
623 * Add in that it is a reparse point
625 result |= FILE_ATTRIBUTE_REPARSE_POINT;
627 dos_mode_debug_print(__func__, result);
633 * check whether a file or directory is flagged as compressed.
635 static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
636 struct smb_filename *smb_fname,
640 uint16_t compression_fmt;
641 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
642 if (tmp_ctx == NULL) {
643 status = NT_STATUS_NO_MEMORY;
647 status = SMB_VFS_GET_COMPRESSION(conn, tmp_ctx, NULL, smb_fname,
649 if (!NT_STATUS_IS_OK(status)) {
653 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
654 *is_compressed = true;
656 *is_compressed = false;
658 status = NT_STATUS_OK;
661 talloc_free(tmp_ctx);
666 static uint32_t dos_mode_from_name(connection_struct *conn,
667 const struct smb_filename *smb_fname,
670 const char *p = NULL;
671 uint32_t result = dosmode;
673 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
674 lp_hide_dot_files(SNUM(conn)))
676 p = strrchr_m(smb_fname->base_name, '/');
680 p = smb_fname->base_name;
683 /* Only . and .. are not hidden. */
685 !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0')))
687 result |= FILE_ATTRIBUTE_HIDDEN;
691 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
692 IS_HIDDEN_PATH(conn, smb_fname->base_name))
694 result |= FILE_ATTRIBUTE_HIDDEN;
700 static uint32_t dos_mode_post(uint32_t dosmode,
701 connection_struct *conn,
702 struct smb_filename *smb_fname,
708 * According to MS-FSA a stream name does not have
709 * separate DOS attribute metadata, so we must return
710 * the DOS attribute from the base filename. With one caveat,
711 * a non-default stream name can never be a directory.
713 * As this is common to all streams data stores, we handle
714 * it here instead of inside all stream VFS modules.
716 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
719 if (is_named_stream(smb_fname)) {
720 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
721 dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
724 if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
725 bool compressed = false;
727 status = dos_mode_check_compressed(conn, smb_fname,
729 if (NT_STATUS_IS_OK(status) && compressed) {
730 dosmode |= FILE_ATTRIBUTE_COMPRESSED;
734 dosmode |= dos_mode_from_name(conn, smb_fname, dosmode);
736 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
737 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
738 } else if (dosmode == 0) {
739 dosmode = FILE_ATTRIBUTE_NORMAL;
742 dosmode = filter_mode_by_protocol(dosmode);
744 dos_mode_debug_print(func, dosmode);
748 /****************************************************************************
749 Change a unix mode to a dos mode.
750 May also read the create timespec into the stat struct in smb_fname
751 if "store dos attributes" is true.
752 ****************************************************************************/
754 uint32_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
757 NTSTATUS status = NT_STATUS_OK;
759 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
761 if (!VALID_STAT(smb_fname->st)) {
765 /* Get the DOS attributes via the VFS if we can */
766 status = SMB_VFS_GET_DOS_ATTRIBUTES(conn, smb_fname, &result);
767 if (!NT_STATUS_IS_OK(status)) {
769 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
771 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
772 result |= dos_mode_from_sbuf(conn, smb_fname);
776 result = dos_mode_post(result, conn, smb_fname, __func__);
780 struct dos_mode_at_state {
781 files_struct *dir_fsp;
782 struct smb_filename *smb_fname;
786 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
788 struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
789 struct tevent_context *ev,
790 files_struct *dir_fsp,
791 struct smb_filename *smb_fname)
793 struct tevent_req *req = NULL;
794 struct dos_mode_at_state *state = NULL;
795 struct tevent_req *subreq = NULL;
797 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
799 req = tevent_req_create(mem_ctx, &state,
800 struct dos_mode_at_state);
805 *state = (struct dos_mode_at_state) {
807 .smb_fname = smb_fname,
810 if (!VALID_STAT(smb_fname->st)) {
811 tevent_req_done(req);
812 return tevent_req_post(req, ev);
815 subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
819 if (tevent_req_nomem(subreq, req)) {
820 return tevent_req_post(req, ev);
822 tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
827 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
829 struct tevent_req *req =
830 tevent_req_callback_data(subreq,
832 struct dos_mode_at_state *state =
834 struct dos_mode_at_state);
836 struct smb_filename *smb_path = NULL;
837 struct vfs_aio_state aio_state;
842 * Make sure we run as the user again
844 ok = change_to_user_and_service_by_fsp(state->dir_fsp);
847 status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
851 if (!NT_STATUS_IS_OK(status)) {
853 * Both the sync dos_mode() as well as the async
854 * dos_mode_at_[send|recv] have no real error return, the only
855 * unhandled error is when the stat info in smb_fname is not
856 * valid (cf the checks in dos_mode() and dos_mode_at_send().
858 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
859 * dos_mode_post() which also does the mapping of a last ressort
860 * from S_IFMT(st_mode).
862 * Only if we get NT_STATUS_NOT_IMPLEMENTED from a stacked VFS
863 * module we must fallback to sync processing.
865 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
867 * state->dosmode should still be 0, but reset
871 status = NT_STATUS_OK;
874 if (NT_STATUS_IS_OK(status)) {
875 state->dosmode = dos_mode_post(state->dosmode,
876 state->dir_fsp->conn,
879 tevent_req_done(req);
884 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
887 path = talloc_asprintf(state,
889 state->dir_fsp->fsp_name->base_name,
890 state->smb_fname->base_name);
891 if (tevent_req_nomem(path, req)) {
895 smb_path = synthetic_smb_fname(state,
898 &state->smb_fname->st,
899 state->smb_fname->twrp,
901 if (tevent_req_nomem(smb_path, req)) {
905 state->dosmode = dos_mode(state->dir_fsp->conn, smb_path);
906 tevent_req_done(req);
910 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
912 struct dos_mode_at_state *state =
914 struct dos_mode_at_state);
917 if (tevent_req_is_nterror(req, &status)) {
918 tevent_req_received(req);
922 *dosmode = state->dosmode;
923 tevent_req_received(req);
927 /*******************************************************************
928 chmod a file - but preserve some bits.
929 If "store dos attributes" is also set it will store the create time
930 from the stat struct in smb_fname (in NTTIME format) in the EA
932 ********************************************************************/
934 int file_set_dosmode(connection_struct *conn,
935 struct smb_filename *smb_fname,
937 struct smb_filename *parent_dir,
943 int ret = -1, lret = -1;
944 files_struct *fsp = NULL;
945 bool need_close = false;
948 if (!CAN_WRITE(conn)) {
953 dosmode &= SAMBA_ATTRIBUTES_MASK;
955 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
956 dosmode, smb_fname_str_dbg(smb_fname)));
958 unixmode = smb_fname->st.st_ex_mode;
960 get_acl_group_bits(conn, smb_fname,
961 &smb_fname->st.st_ex_mode);
963 if (S_ISDIR(smb_fname->st.st_ex_mode))
964 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
966 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
968 /* Store the DOS attributes in an EA by preference. */
969 status = SMB_VFS_SET_DOS_ATTRIBUTES(conn, smb_fname, dosmode);
970 if (NT_STATUS_IS_OK(status)) {
972 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
973 FILE_NOTIFY_CHANGE_ATTRIBUTES,
974 smb_fname->base_name);
976 smb_fname->st.st_ex_mode = unixmode;
980 * Only fall back to using UNIX modes if
981 * we get NOT_IMPLEMENTED.
983 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
984 errno = map_errno_from_nt_status(status);
989 /* Fall back to UNIX modes. */
990 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
992 /* preserve the file type bits */
995 /* preserve the s bits */
996 mask |= (S_ISUID | S_ISGID);
998 /* preserve the t bit */
1003 /* possibly preserve the x bits */
1004 if (!MAP_ARCHIVE(conn))
1006 if (!MAP_SYSTEM(conn))
1008 if (!MAP_HIDDEN(conn))
1011 unixmode |= (smb_fname->st.st_ex_mode & mask);
1013 /* if we previously had any r bits set then leave them alone */
1014 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
1015 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
1019 /* if we previously had any w bits set then leave them alone
1020 whilst adding in the new w bits, if the new mode is not rdonly */
1021 if (!IS_DOS_READONLY(dosmode)) {
1022 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
1026 * From the chmod 2 man page:
1028 * "If the calling process is not privileged, and the group of the file
1029 * does not match the effective group ID of the process or one of its
1030 * supplementary group IDs, the S_ISGID bit will be turned off, but
1031 * this will not cause an error to be returned."
1033 * Simply refuse to do the chmod in this case.
1036 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
1037 geteuid() != sec_initial_uid() &&
1038 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
1039 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1040 "set for directory %s\n",
1041 smb_fname_str_dbg(smb_fname)));
1046 ret = SMB_VFS_CHMOD(conn, smb_fname, unixmode);
1048 if(!newfile || (lret != -1)) {
1049 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1050 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1051 smb_fname->base_name);
1053 smb_fname->st.st_ex_mode = unixmode;
1057 if((errno != EPERM) && (errno != EACCES))
1060 if(!lp_dos_filemode(SNUM(conn)))
1063 /* We want DOS semantics, ie allow non owner with write permission to change the
1064 bits on a file. Just like file_ntimes below.
1067 if (!can_write_to_file(conn,
1076 * We need to get an open file handle to do the
1077 * metadata operation under root.
1080 status = get_file_handle_for_metadata(conn,
1084 if (!NT_STATUS_IS_OK(status)) {
1085 errno = map_errno_from_nt_status(status);
1090 ret = SMB_VFS_FCHMOD(fsp, unixmode);
1093 close_file(NULL, fsp, NORMAL_CLOSE);
1096 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1097 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1098 smb_fname->base_name);
1101 smb_fname->st.st_ex_mode = unixmode;
1108 NTSTATUS file_set_sparse(connection_struct *conn,
1112 const struct loadparm_substitution *lp_sub =
1113 loadparm_s3_global_substitution();
1114 uint32_t old_dosmode;
1115 uint32_t new_dosmode;
1118 if (!CAN_WRITE(conn)) {
1119 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1120 "on readonly share[%s]\n",
1121 smb_fname_str_dbg(fsp->fsp_name),
1123 lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
1124 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1128 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1129 * following access flags are granted.
1131 if ((fsp->access_mask & (FILE_WRITE_DATA
1132 | FILE_WRITE_ATTRIBUTES
1133 | SEC_FILE_APPEND_DATA)) == 0) {
1134 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1135 "access_mask[0x%08X] - access denied\n",
1136 smb_fname_str_dbg(fsp->fsp_name),
1139 return NT_STATUS_ACCESS_DENIED;
1142 if (fsp->fsp_flags.is_directory) {
1143 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1144 (sparse ? "set" : "clear"),
1145 smb_fname_str_dbg(fsp->fsp_name)));
1146 return NT_STATUS_INVALID_PARAMETER;
1149 if (IS_IPC(conn) || IS_PRINT(conn)) {
1150 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1151 (sparse ? "set" : "clear")));
1152 return NT_STATUS_INVALID_PARAMETER;
1155 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1156 sparse, smb_fname_str_dbg(fsp->fsp_name)));
1158 if (!lp_store_dos_attributes(SNUM(conn))) {
1159 return NT_STATUS_INVALID_DEVICE_REQUEST;
1162 status = vfs_stat_fsp(fsp);
1163 if (!NT_STATUS_IS_OK(status)) {
1167 old_dosmode = dos_mode(conn, fsp->fsp_name);
1169 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1170 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1171 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1172 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1174 return NT_STATUS_OK;
1177 /* Store the DOS attributes in an EA. */
1178 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1179 if (!NT_STATUS_IS_OK(status)) {
1183 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1184 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1185 fsp->fsp_name->base_name);
1187 fsp->fsp_flags.is_sparse = sparse;
1189 return NT_STATUS_OK;
1192 /*******************************************************************
1193 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1195 *******************************************************************/
1197 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
1198 struct smb_file_time *ft)
1204 DEBUG(6, ("file_ntime: actime: %s",
1205 time_to_asc(convert_timespec_to_time_t(ft->atime))));
1206 DEBUG(6, ("file_ntime: modtime: %s",
1207 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
1208 DEBUG(6, ("file_ntime: ctime: %s",
1209 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
1210 DEBUG(6, ("file_ntime: createtime: %s",
1211 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
1213 /* Don't update the time on read-only shares */
1214 /* We need this as set_filetime (which can be called on
1215 close and other paths) can end up calling this function
1216 without the NEED_WRITE protection. Found by :
1217 Leo Weppelman <leo@wau.mis.ah.nl>
1220 if (!CAN_WRITE(conn)) {
1224 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
1228 if((errno != EPERM) && (errno != EACCES)) {
1232 if(!lp_dos_filetimes(SNUM(conn))) {
1236 /* We have permission (given by the Samba admin) to
1237 break POSIX semantics and allow a user to change
1238 the time on a file they don't own but can write to
1242 /* Check if we have write access. */
1243 if (can_write_to_file(conn,
1247 /* We are allowed to become root and change the filetime. */
1249 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1256 /******************************************************************
1257 Force a "sticky" write time on a pathname. This will always be
1258 returned on all future write time queries and set on close.
1259 ******************************************************************/
1261 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1263 if (is_omit_timespec(&mtime)) {
1267 if (!set_sticky_write_time(fileid, mtime)) {
1274 /******************************************************************
1275 Force a "sticky" write time on an fsp. This will always be
1276 returned on all future write time queries and set on close.
1277 ******************************************************************/
1279 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1281 if (is_omit_timespec(&mtime)) {
1285 fsp->fsp_flags.write_time_forced = true;
1286 TALLOC_FREE(fsp->update_write_time_event);
1288 return set_sticky_write_time_path(fsp->file_id, mtime);
1291 /******************************************************************
1292 Set a create time EA.
1293 ******************************************************************/
1295 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1296 const struct smb_filename *psmb_fname,
1297 struct timespec create_time)
1299 struct smb_filename *smb_fname;
1303 if (!lp_store_dos_attributes(SNUM(conn))) {
1304 return NT_STATUS_OK;
1307 smb_fname = synthetic_smb_fname(talloc_tos(),
1308 psmb_fname->base_name,
1314 if (smb_fname == NULL) {
1315 return NT_STATUS_NO_MEMORY;
1318 dosmode = dos_mode(conn, smb_fname);
1320 smb_fname->st.st_ex_btime = create_time;
1322 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1324 return map_nt_error_from_unix(errno);
1327 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1328 smb_fname_str_dbg(smb_fname)));
1330 return NT_STATUS_OK;
1333 /******************************************************************
1334 Return a create time.
1335 ******************************************************************/
1337 struct timespec get_create_timespec(connection_struct *conn,
1338 struct files_struct *fsp,
1339 const struct smb_filename *smb_fname)
1341 return smb_fname->st.st_ex_btime;
1344 /******************************************************************
1345 Return a change time (may look at EA in future).
1346 ******************************************************************/
1348 struct timespec get_change_timespec(connection_struct *conn,
1349 struct files_struct *fsp,
1350 const struct smb_filename *smb_fname)
1352 return smb_fname->st.st_ex_mtime;
1355 /****************************************************************************
1356 Get a real open file handle we can do meta-data operations on. As it's
1357 going to be used under root access only on meta-data we should look for
1358 any existing open file handle first, and use that in preference (also to
1359 avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1360 ****************************************************************************/
1362 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1363 const struct smb_filename *smb_fname,
1364 files_struct **ret_fsp,
1369 struct file_id file_id;
1370 struct smb_filename *smb_fname_cp = NULL;
1372 *need_close = false;
1374 if (!VALID_STAT(smb_fname->st)) {
1375 return NT_STATUS_INVALID_PARAMETER;
1378 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1380 for(fsp = file_find_di_first(conn->sconn, file_id);
1382 fsp = file_find_di_next(fsp)) {
1383 if (fsp->fh->fd != -1) {
1385 return NT_STATUS_OK;
1389 smb_fname_cp = cp_smb_filename(talloc_tos(),
1391 if (smb_fname_cp == NULL) {
1392 return NT_STATUS_NO_MEMORY;
1395 /* Opens an INTERNAL_OPEN_ONLY write handle. */
1396 status = SMB_VFS_CREATE_FILE(
1399 smb_fname_cp, /* fname */
1400 FILE_WRITE_ATTRIBUTES, /* access_mask */
1401 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1403 FILE_OPEN, /* create_disposition*/
1404 0, /* create_options */
1405 0, /* file_attributes */
1406 INTERNAL_OPEN_ONLY, /* oplock_request */
1408 0, /* allocation_size */
1409 0, /* private_flags */
1412 ret_fsp, /* result */
1414 NULL, NULL); /* create context */
1416 TALLOC_FREE(smb_fname_cp);
1418 if (NT_STATUS_IS_OK(status)) {