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 fget_ea_dos_attribute(struct files_struct *fsp,
374 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
375 return NT_STATUS_NOT_IMPLEMENTED;
378 /* Don't reset pattr to zero as we may already have filename-based attributes we
381 sizeret = SMB_VFS_FGETXATTR(fsp->base_fsp ? fsp->base_fsp : fsp,
382 SAMBA_XATTR_DOS_ATTRIB,
386 DBG_INFO("Cannot get attribute "
387 "from EA on file %s: Error = %s\n",
388 fsp_str_dbg(fsp), strerror(errno));
389 return map_nt_error_from_unix(errno);
392 blob.data = (uint8_t *)attrstr;
393 blob.length = sizeret;
395 status = parse_dos_attribute_blob(fsp->fsp_name, blob, pattr);
396 if (!NT_STATUS_IS_OK(status)) {
403 /****************************************************************************
404 Set DOS attributes in an EA.
405 Also sets the create time.
406 ****************************************************************************/
408 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
409 const struct smb_filename *smb_fname,
412 struct xattr_DOSATTRIB dosattrib;
413 enum ndr_err_code ndr_err;
417 if (!lp_store_dos_attributes(SNUM(conn))) {
418 return NT_STATUS_NOT_IMPLEMENTED;
422 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
423 * vfs_default via DMAPI if that is enabled.
425 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
427 ZERO_STRUCT(dosattrib);
430 dosattrib.version = 4;
431 dosattrib.info.info4.valid_flags = XATTR_DOSINFO_ATTRIB |
432 XATTR_DOSINFO_CREATE_TIME;
433 dosattrib.info.info4.attrib = dosmode;
434 dosattrib.info.info4.create_time = full_timespec_to_nt_time(
435 &smb_fname->st.st_ex_btime);
437 if (!(smb_fname->st.st_ex_iflags & ST_EX_IFLAG_CALCULATED_ITIME)) {
438 dosattrib.info.info4.valid_flags |= XATTR_DOSINFO_ITIME;
439 dosattrib.info.info4.itime = full_timespec_to_nt_time(
440 &smb_fname->st.st_ex_itime);
443 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
444 (unsigned int)dosmode,
445 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
446 smb_fname_str_dbg(smb_fname) ));
448 ndr_err = ndr_push_struct_blob(
449 &blob, talloc_tos(), &dosattrib,
450 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
452 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
453 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
454 ndr_errstr(ndr_err)));
455 return ndr_map_error2ntstatus(ndr_err);
458 if (blob.data == NULL || blob.length == 0) {
459 /* Should this be INTERNAL_ERROR? */
460 return NT_STATUS_INVALID_PARAMETER;
463 ret = SMB_VFS_SETXATTR(conn, smb_fname,
464 SAMBA_XATTR_DOS_ATTRIB,
465 blob.data, blob.length, 0);
467 NTSTATUS status = NT_STATUS_OK;
468 bool need_close = false;
469 files_struct *fsp = NULL;
470 bool set_dosmode_ok = false;
472 if ((errno != EPERM) && (errno != EACCES)) {
473 DBG_INFO("Cannot set "
474 "attribute EA on file %s: Error = %s\n",
475 smb_fname_str_dbg(smb_fname), strerror(errno));
476 return map_nt_error_from_unix(errno);
479 /* We want DOS semantics, ie allow non owner with write permission to change the
480 bits on a file. Just like file_ntimes below.
483 /* Check if we have write access. */
484 if (!CAN_WRITE(conn)) {
485 return NT_STATUS_ACCESS_DENIED;
488 status = smbd_check_access_rights(conn,
492 FILE_WRITE_ATTRIBUTES);
493 if (NT_STATUS_IS_OK(status)) {
494 set_dosmode_ok = true;
497 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
498 set_dosmode_ok = can_write_to_file(conn,
503 if (!set_dosmode_ok) {
504 return NT_STATUS_ACCESS_DENIED;
508 * We need to get an open file handle to do the
509 * metadata operation under root.
512 status = get_file_handle_for_metadata(conn,
516 if (!NT_STATUS_IS_OK(status)) {
521 ret = SMB_VFS_FSETXATTR(fsp,
522 SAMBA_XATTR_DOS_ATTRIB,
523 blob.data, blob.length, 0);
525 status = NT_STATUS_OK;
529 close_file(NULL, fsp, NORMAL_CLOSE);
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)));
539 /****************************************************************************
540 Change a unix mode to a dos mode for an ms dfs link.
541 ****************************************************************************/
543 uint32_t dos_mode_msdfs(connection_struct *conn,
544 const struct smb_filename *smb_fname)
548 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
550 if (!VALID_STAT(smb_fname->st)) {
554 /* First do any modifications that depend on the path name. */
555 /* hide files with a name starting with a . */
556 if (lp_hide_dot_files(SNUM(conn))) {
557 const char *p = strrchr_m(smb_fname->base_name, '/');
561 p = smb_fname->base_name;
564 /* Only . and .. are not hidden. */
565 if (p[0] == '.' && !((p[1] == '\0') ||
566 (p[1] == '.' && p[2] == '\0'))) {
567 result |= FILE_ATTRIBUTE_HIDDEN;
571 result |= dos_mode_from_sbuf(conn, smb_fname);
573 /* Optimization : Only call is_hidden_path if it's not already
575 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
576 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
577 result |= FILE_ATTRIBUTE_HIDDEN;
581 result = FILE_ATTRIBUTE_NORMAL;
584 result = filter_mode_by_protocol(result);
587 * Add in that it is a reparse point
589 result |= FILE_ATTRIBUTE_REPARSE_POINT;
591 dos_mode_debug_print(__func__, result);
597 * check whether a file or directory is flagged as compressed.
599 static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
600 struct files_struct *fsp,
601 struct smb_filename *smb_fname,
605 uint16_t compression_fmt;
606 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
607 if (tmp_ctx == NULL) {
608 status = NT_STATUS_NO_MEMORY;
612 status = SMB_VFS_FGET_COMPRESSION(conn, tmp_ctx, fsp,
614 if (!NT_STATUS_IS_OK(status)) {
618 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
619 *is_compressed = true;
621 *is_compressed = false;
623 status = NT_STATUS_OK;
626 talloc_free(tmp_ctx);
631 static uint32_t dos_mode_from_name(connection_struct *conn,
632 const struct smb_filename *smb_fname,
635 const char *p = NULL;
636 uint32_t result = dosmode;
638 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
639 lp_hide_dot_files(SNUM(conn)))
641 p = strrchr_m(smb_fname->base_name, '/');
645 p = smb_fname->base_name;
648 /* Only . and .. are not hidden. */
650 !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0')))
652 result |= FILE_ATTRIBUTE_HIDDEN;
656 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
657 IS_HIDDEN_PATH(conn, smb_fname->base_name))
659 result |= FILE_ATTRIBUTE_HIDDEN;
665 static uint32_t dos_mode_post(uint32_t dosmode,
666 connection_struct *conn,
667 struct files_struct *fsp,
668 struct smb_filename *smb_fname,
674 smb_fname = fsp->fsp_name;
678 * According to MS-FSA a stream name does not have
679 * separate DOS attribute metadata, so we must return
680 * the DOS attribute from the base filename. With one caveat,
681 * a non-default stream name can never be a directory.
683 * As this is common to all streams data stores, we handle
684 * it here instead of inside all stream VFS modules.
686 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
689 if (is_named_stream(smb_fname)) {
690 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
691 dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
694 if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
695 bool compressed = false;
697 status = dos_mode_check_compressed(conn, fsp, smb_fname,
699 if (NT_STATUS_IS_OK(status) && compressed) {
700 dosmode |= FILE_ATTRIBUTE_COMPRESSED;
704 dosmode |= dos_mode_from_name(conn, smb_fname, dosmode);
706 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
707 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
708 } else if (dosmode == 0) {
709 dosmode = FILE_ATTRIBUTE_NORMAL;
712 dosmode = filter_mode_by_protocol(dosmode);
714 dos_mode_debug_print(func, dosmode);
718 /****************************************************************************
719 Change a unix mode to a dos mode.
720 May also read the create timespec into the stat struct in smb_fname
721 if "store dos attributes" is true.
722 ****************************************************************************/
724 uint32_t fdos_mode(struct files_struct *fsp)
727 NTSTATUS status = NT_STATUS_OK;
731 * The pathological case where a callers does
732 * fdos_mode(smb_fname->fsp) passing a pathref fsp. But as
733 * smb_fname points at a symlink in POSIX context smb_fname->fsp
736 return FILE_ATTRIBUTE_NORMAL;
739 DBG_DEBUG("%s\n", fsp_str_dbg(fsp));
741 if (!VALID_STAT(fsp->fsp_name->st)) {
745 if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) {
746 return FILE_ATTRIBUTE_NORMAL;
749 /* Get the DOS attributes via the VFS if we can */
750 status = SMB_VFS_FGET_DOS_ATTRIBUTES(fsp->conn, fsp, &result);
751 if (!NT_STATUS_IS_OK(status)) {
753 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
755 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
756 result |= dos_mode_from_sbuf(fsp->conn, fsp->fsp_name);
760 result = dos_mode_post(result, fsp->conn, fsp, NULL, __func__);
764 struct dos_mode_at_state {
765 files_struct *dir_fsp;
766 struct smb_filename *smb_fname;
770 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
772 struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
773 struct tevent_context *ev,
774 files_struct *dir_fsp,
775 struct smb_filename *smb_fname)
777 struct tevent_req *req = NULL;
778 struct dos_mode_at_state *state = NULL;
779 struct tevent_req *subreq = NULL;
781 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
783 req = tevent_req_create(mem_ctx, &state,
784 struct dos_mode_at_state);
789 *state = (struct dos_mode_at_state) {
791 .smb_fname = smb_fname,
794 if (!VALID_STAT(smb_fname->st)) {
795 tevent_req_done(req);
796 return tevent_req_post(req, ev);
799 subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
803 if (tevent_req_nomem(subreq, req)) {
804 return tevent_req_post(req, ev);
806 tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
811 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
813 struct tevent_req *req =
814 tevent_req_callback_data(subreq,
816 struct dos_mode_at_state *state =
818 struct dos_mode_at_state);
820 struct smb_filename *smb_path = NULL;
821 struct vfs_aio_state aio_state;
826 * Make sure we run as the user again
828 ok = change_to_user_and_service_by_fsp(state->dir_fsp);
831 status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
835 if (!NT_STATUS_IS_OK(status)) {
837 * Both the sync dos_mode() as well as the async
838 * dos_mode_at_[send|recv] have no real error return, the only
839 * unhandled error is when the stat info in smb_fname is not
840 * valid (cf the checks in dos_mode() and dos_mode_at_send().
842 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
843 * dos_mode_post() which also does the mapping of a last ressort
844 * from S_IFMT(st_mode).
846 * Only if we get NT_STATUS_NOT_IMPLEMENTED from a stacked VFS
847 * module we must fallback to sync processing.
849 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
851 * state->dosmode should still be 0, but reset
855 status = NT_STATUS_OK;
858 if (NT_STATUS_IS_OK(status)) {
859 state->dosmode = dos_mode_post(state->dosmode,
860 state->dir_fsp->conn,
864 tevent_req_done(req);
869 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
872 path = talloc_asprintf(state,
874 state->dir_fsp->fsp_name->base_name,
875 state->smb_fname->base_name);
876 if (tevent_req_nomem(path, req)) {
880 smb_path = synthetic_smb_fname(state,
883 &state->smb_fname->st,
884 state->smb_fname->twrp,
886 if (tevent_req_nomem(smb_path, req)) {
890 state->dosmode = fdos_mode(state->smb_fname->fsp);
891 tevent_req_done(req);
895 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
897 struct dos_mode_at_state *state =
899 struct dos_mode_at_state);
902 if (tevent_req_is_nterror(req, &status)) {
903 tevent_req_received(req);
907 *dosmode = state->dosmode;
908 tevent_req_received(req);
912 /*******************************************************************
913 chmod a file - but preserve some bits.
914 If "store dos attributes" is also set it will store the create time
915 from the stat struct in smb_fname (in NTTIME format) in the EA
917 ********************************************************************/
919 int file_set_dosmode(connection_struct *conn,
920 struct smb_filename *smb_fname,
922 struct smb_filename *parent_dir,
928 int ret = -1, lret = -1;
929 files_struct *fsp = NULL;
930 bool need_close = false;
933 if (!CAN_WRITE(conn)) {
938 dosmode &= SAMBA_ATTRIBUTES_MASK;
940 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
941 dosmode, smb_fname_str_dbg(smb_fname)));
943 unixmode = smb_fname->st.st_ex_mode;
945 get_acl_group_bits(conn, smb_fname,
946 &smb_fname->st.st_ex_mode);
948 if (S_ISDIR(smb_fname->st.st_ex_mode))
949 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
951 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
953 /* Store the DOS attributes in an EA by preference. */
954 status = SMB_VFS_SET_DOS_ATTRIBUTES(conn, smb_fname, dosmode);
955 if (NT_STATUS_IS_OK(status)) {
957 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
958 FILE_NOTIFY_CHANGE_ATTRIBUTES,
959 smb_fname->base_name);
961 smb_fname->st.st_ex_mode = unixmode;
965 * Only fall back to using UNIX modes if
966 * we get NOT_IMPLEMENTED.
968 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
969 errno = map_errno_from_nt_status(status);
974 /* Fall back to UNIX modes. */
975 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
977 /* preserve the file type bits */
980 /* preserve the s bits */
981 mask |= (S_ISUID | S_ISGID);
983 /* preserve the t bit */
988 /* possibly preserve the x bits */
989 if (!MAP_ARCHIVE(conn))
991 if (!MAP_SYSTEM(conn))
993 if (!MAP_HIDDEN(conn))
996 unixmode |= (smb_fname->st.st_ex_mode & mask);
998 /* if we previously had any r bits set then leave them alone */
999 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
1000 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
1004 /* if we previously had any w bits set then leave them alone
1005 whilst adding in the new w bits, if the new mode is not rdonly */
1006 if (!IS_DOS_READONLY(dosmode)) {
1007 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
1011 * From the chmod 2 man page:
1013 * "If the calling process is not privileged, and the group of the file
1014 * does not match the effective group ID of the process or one of its
1015 * supplementary group IDs, the S_ISGID bit will be turned off, but
1016 * this will not cause an error to be returned."
1018 * Simply refuse to do the chmod in this case.
1021 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
1022 geteuid() != sec_initial_uid() &&
1023 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
1024 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1025 "set for directory %s\n",
1026 smb_fname_str_dbg(smb_fname)));
1031 ret = SMB_VFS_CHMOD(conn, smb_fname, unixmode);
1033 if(!newfile || (lret != -1)) {
1034 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1035 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1036 smb_fname->base_name);
1038 smb_fname->st.st_ex_mode = unixmode;
1042 if((errno != EPERM) && (errno != EACCES))
1045 if(!lp_dos_filemode(SNUM(conn)))
1048 /* We want DOS semantics, ie allow non owner with write permission to change the
1049 bits on a file. Just like file_ntimes below.
1052 if (!can_write_to_file(conn,
1061 * We need to get an open file handle to do the
1062 * metadata operation under root.
1065 status = get_file_handle_for_metadata(conn,
1069 if (!NT_STATUS_IS_OK(status)) {
1070 errno = map_errno_from_nt_status(status);
1075 ret = SMB_VFS_FCHMOD(fsp, unixmode);
1078 close_file(NULL, fsp, NORMAL_CLOSE);
1081 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1082 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1083 smb_fname->base_name);
1086 smb_fname->st.st_ex_mode = unixmode;
1093 NTSTATUS file_set_sparse(connection_struct *conn,
1097 const struct loadparm_substitution *lp_sub =
1098 loadparm_s3_global_substitution();
1099 uint32_t old_dosmode;
1100 uint32_t new_dosmode;
1103 if (!CAN_WRITE(conn)) {
1104 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1105 "on readonly share[%s]\n",
1106 smb_fname_str_dbg(fsp->fsp_name),
1108 lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
1109 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1113 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1114 * following access flags are granted.
1116 if ((fsp->access_mask & (FILE_WRITE_DATA
1117 | FILE_WRITE_ATTRIBUTES
1118 | SEC_FILE_APPEND_DATA)) == 0) {
1119 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1120 "access_mask[0x%08X] - access denied\n",
1121 smb_fname_str_dbg(fsp->fsp_name),
1124 return NT_STATUS_ACCESS_DENIED;
1127 if (fsp->fsp_flags.is_directory) {
1128 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1129 (sparse ? "set" : "clear"),
1130 smb_fname_str_dbg(fsp->fsp_name)));
1131 return NT_STATUS_INVALID_PARAMETER;
1134 if (IS_IPC(conn) || IS_PRINT(conn)) {
1135 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1136 (sparse ? "set" : "clear")));
1137 return NT_STATUS_INVALID_PARAMETER;
1140 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1141 sparse, smb_fname_str_dbg(fsp->fsp_name)));
1143 if (!lp_store_dos_attributes(SNUM(conn))) {
1144 return NT_STATUS_INVALID_DEVICE_REQUEST;
1147 status = vfs_stat_fsp(fsp);
1148 if (!NT_STATUS_IS_OK(status)) {
1152 old_dosmode = fdos_mode(fsp);
1154 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1155 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1156 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1157 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1159 return NT_STATUS_OK;
1162 /* Store the DOS attributes in an EA. */
1163 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1164 if (!NT_STATUS_IS_OK(status)) {
1168 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1169 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1170 fsp->fsp_name->base_name);
1172 fsp->fsp_flags.is_sparse = sparse;
1174 return NT_STATUS_OK;
1177 /*******************************************************************
1178 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1180 *******************************************************************/
1182 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
1183 struct smb_file_time *ft)
1189 DEBUG(6, ("file_ntime: actime: %s",
1190 time_to_asc(convert_timespec_to_time_t(ft->atime))));
1191 DEBUG(6, ("file_ntime: modtime: %s",
1192 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
1193 DEBUG(6, ("file_ntime: ctime: %s",
1194 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
1195 DEBUG(6, ("file_ntime: createtime: %s",
1196 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
1198 /* Don't update the time on read-only shares */
1199 /* We need this as set_filetime (which can be called on
1200 close and other paths) can end up calling this function
1201 without the NEED_WRITE protection. Found by :
1202 Leo Weppelman <leo@wau.mis.ah.nl>
1205 if (!CAN_WRITE(conn)) {
1209 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
1213 if((errno != EPERM) && (errno != EACCES)) {
1217 if(!lp_dos_filetimes(SNUM(conn))) {
1221 /* We have permission (given by the Samba admin) to
1222 break POSIX semantics and allow a user to change
1223 the time on a file they don't own but can write to
1227 /* Check if we have write access. */
1228 if (can_write_to_file(conn,
1232 /* We are allowed to become root and change the filetime. */
1234 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1241 /******************************************************************
1242 Force a "sticky" write time on a pathname. This will always be
1243 returned on all future write time queries and set on close.
1244 ******************************************************************/
1246 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1248 if (is_omit_timespec(&mtime)) {
1252 if (!set_sticky_write_time(fileid, mtime)) {
1259 /******************************************************************
1260 Force a "sticky" write time on an fsp. This will always be
1261 returned on all future write time queries and set on close.
1262 ******************************************************************/
1264 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1266 if (is_omit_timespec(&mtime)) {
1270 fsp->fsp_flags.write_time_forced = true;
1271 TALLOC_FREE(fsp->update_write_time_event);
1273 return set_sticky_write_time_path(fsp->file_id, mtime);
1276 /******************************************************************
1277 Set a create time EA.
1278 ******************************************************************/
1280 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1281 const struct smb_filename *psmb_fname,
1282 struct timespec create_time)
1284 struct smb_filename *smb_fname;
1288 if (!lp_store_dos_attributes(SNUM(conn))) {
1289 return NT_STATUS_OK;
1292 smb_fname = synthetic_smb_fname(talloc_tos(),
1293 psmb_fname->base_name,
1299 if (smb_fname == NULL) {
1300 return NT_STATUS_NO_MEMORY;
1303 dosmode = fdos_mode(psmb_fname->fsp);
1305 smb_fname->st.st_ex_btime = create_time;
1307 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1309 return map_nt_error_from_unix(errno);
1312 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1313 smb_fname_str_dbg(smb_fname)));
1315 return NT_STATUS_OK;
1318 /******************************************************************
1319 Return a create time.
1320 ******************************************************************/
1322 struct timespec get_create_timespec(connection_struct *conn,
1323 struct files_struct *fsp,
1324 const struct smb_filename *smb_fname)
1326 return smb_fname->st.st_ex_btime;
1329 /******************************************************************
1330 Return a change time (may look at EA in future).
1331 ******************************************************************/
1333 struct timespec get_change_timespec(connection_struct *conn,
1334 struct files_struct *fsp,
1335 const struct smb_filename *smb_fname)
1337 return smb_fname->st.st_ex_mtime;
1340 /****************************************************************************
1341 Get a real open file handle we can do meta-data operations on. As it's
1342 going to be used under root access only on meta-data we should look for
1343 any existing open file handle first, and use that in preference (also to
1344 avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1345 ****************************************************************************/
1347 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1348 const struct smb_filename *smb_fname,
1349 files_struct **ret_fsp,
1354 struct file_id file_id;
1355 struct smb_filename *smb_fname_cp = NULL;
1357 *need_close = false;
1359 if (!VALID_STAT(smb_fname->st)) {
1360 return NT_STATUS_INVALID_PARAMETER;
1363 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1365 for(fsp = file_find_di_first(conn->sconn, file_id, true);
1367 fsp = file_find_di_next(fsp, true)) {
1368 if (fsp_get_io_fd(fsp) != -1) {
1370 return NT_STATUS_OK;
1374 smb_fname_cp = cp_smb_filename(talloc_tos(),
1376 if (smb_fname_cp == NULL) {
1377 return NT_STATUS_NO_MEMORY;
1380 status = openat_pathref_fsp(conn->cwd_fsp, smb_fname_cp);
1381 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
1382 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1384 if (!NT_STATUS_IS_OK(status)) {
1385 TALLOC_FREE(smb_fname_cp);
1389 /* Opens an INTERNAL_OPEN_ONLY write handle. */
1390 status = SMB_VFS_CREATE_FILE(
1393 smb_fname_cp, /* fname */
1394 FILE_WRITE_ATTRIBUTES, /* access_mask */
1395 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1397 FILE_OPEN, /* create_disposition*/
1398 0, /* create_options */
1399 0, /* file_attributes */
1400 INTERNAL_OPEN_ONLY, /* oplock_request */
1402 0, /* allocation_size */
1403 0, /* private_flags */
1406 ret_fsp, /* result */
1408 NULL, NULL); /* create context */
1410 TALLOC_FREE(smb_fname_cp);
1412 if (NT_STATUS_IS_OK(status)) {