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"
31 #include "fake_file.h"
33 static void dos_mode_debug_print(const char *func, uint32_t mode)
37 if (DEBUGLEVEL < DBGLVL_INFO) {
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,
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",
78 (unsigned int)(mode & 0x3f) ));
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.
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
96 Then apply create mask,
99 Base permission for directories:
100 dos directory is represented in unix by unix's dir bit and the exec bit
102 Then apply create mask,
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
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, };
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);
130 DBG_ERR("fstat failed [%s]: %s\n",
131 smb_fname_str_dbg(parent_dirfsp->fsp_name),
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));
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);
150 /* Inherit mode of parent directory. */
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));
162 if ((dosmode & FILE_ATTRIBUTE_ARCHIVE) &&
163 lp_map_archive(SNUM(conn))) {
167 if ((dosmode & FILE_ATTRIBUTE_SYSTEM) &&
168 lp_map_system(SNUM(conn))) {
172 if ((dosmode & FILE_ATTRIBUTE_HIDDEN) &&
173 lp_map_hidden(SNUM(conn))) {
178 /* Inherit 666 component of parent directory mode */
179 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
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);
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)
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;
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);
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,
256 struct xattr_DOSATTRIB dosattrib;
257 enum ndr_err_code ndr_err;
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) {
276 dosattr = dosattrib.info.compatinfoFFFF.attrib;
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,
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(
295 dosattr = dosattrib.info.oldinfo2.attrib;
296 /* Don't know what flags to check for this case. */
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,
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(
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;
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));
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);
365 NTSTATUS fget_ea_dos_attribute(struct files_struct *fsp,
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
380 sizeret = SMB_VFS_FGETXATTR(fsp,
381 SAMBA_XATTR_DOS_ATTRIB,
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 set_effective_capability(DAC_OVERRIDE_CAPABILITY);
391 sizeret = SMB_VFS_FGETXATTR(fsp,
392 SAMBA_XATTR_DOS_ATTRIB,
395 drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
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)) {
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,
424 struct xattr_DOSATTRIB dosattrib = { .version = 0, };
425 enum ndr_err_code ndr_err;
426 DATA_BLOB blob = { .data = NULL, };
427 struct timespec btime;
430 if (!lp_store_dos_attributes(SNUM(conn))) {
431 return NT_STATUS_NOT_IMPLEMENTED;
434 if (smb_fname->fsp == NULL) {
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);
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,
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 set_effective_capability(DAC_OVERRIDE_CAPABILITY);
511 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
512 SAMBA_XATTR_DOS_ATTRIB,
513 blob.data, blob.length, 0);
514 drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
516 status = NT_STATUS_OK;
518 if (!NT_STATUS_IS_OK(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)));
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, '/');
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;
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,
574 const struct stat_ex *st)
578 DEBUG(8, ("dos_mode_msdfs: %s\n", name));
580 if (!VALID_STAT(*st)) {
584 result = dos_mode_from_name(conn, name, result);
585 result |= dos_mode_from_sbuf(conn, st, NULL);
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);
604 * check whether a file or directory is flagged as compressed.
606 static NTSTATUS dos_mode_check_compressed(struct files_struct *fsp,
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)) {
618 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
619 *is_compressed = true;
621 *is_compressed = false;
626 static uint32_t dos_mode_post(uint32_t dosmode,
627 struct files_struct *fsp,
630 struct smb_filename *smb_fname = 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);
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)
687 NTSTATUS status = NT_STATUS_OK;
689 DBG_DEBUG("%s\n", fsp_str_dbg(fsp));
691 if (fsp->fake_file_handle != NULL) {
692 return dosmode_from_fake_filehandle(fsp->fake_file_handle);
695 if (!VALID_STAT(fsp->fsp_name->st)) {
699 if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) {
700 return FILE_ATTRIBUTE_NORMAL;
703 if (fsp->fsp_name->st.cached_dos_attributes != FILE_ATTRIBUTE_INVALID) {
704 return fsp->fsp_name->st.cached_dos_attributes;
707 /* Get the DOS attributes via the VFS if we can */
708 status = SMB_VFS_FGET_DOS_ATTRIBUTES(fsp->conn,
711 if (!NT_STATUS_IS_OK(status)) {
713 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
715 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
716 result |= dos_mode_from_sbuf(fsp->conn,
722 fsp->fsp_name->st.cached_dos_attributes = dos_mode_post(result, fsp, __func__);
723 return fsp->fsp_name->st.cached_dos_attributes;
726 struct dos_mode_at_state {
727 files_struct *dir_fsp;
728 struct smb_filename *smb_fname;
732 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
734 struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
735 struct tevent_context *ev,
736 files_struct *dir_fsp,
737 struct smb_filename *smb_fname)
739 struct tevent_req *req = NULL;
740 struct dos_mode_at_state *state = NULL;
741 struct tevent_req *subreq = NULL;
743 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
745 req = tevent_req_create(mem_ctx, &state,
746 struct dos_mode_at_state);
751 *state = (struct dos_mode_at_state) {
753 .smb_fname = smb_fname,
756 if (!VALID_STAT(smb_fname->st)) {
757 tevent_req_done(req);
758 return tevent_req_post(req, ev);
761 if (smb_fname->fsp == NULL) {
762 if (ISDOTDOT(smb_fname->base_name)) {
764 * smb_fname->fsp is explicitly closed
765 * for ".." to prevent meta-data leakage.
767 state->dosmode = FILE_ATTRIBUTE_DIRECTORY;
770 * This is a symlink in POSIX context.
771 * FIXME ? Should we move to returning
772 * FILE_ATTRIBUTE_REPARSE_POINT here ?
774 state->dosmode = FILE_ATTRIBUTE_NORMAL;
776 tevent_req_done(req);
777 return tevent_req_post(req, ev);
780 subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
784 if (tevent_req_nomem(subreq, req)) {
785 return tevent_req_post(req, ev);
787 tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
792 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
794 struct tevent_req *req =
795 tevent_req_callback_data(subreq,
797 struct dos_mode_at_state *state =
799 struct dos_mode_at_state);
800 struct vfs_aio_state aio_state;
805 * Make sure we run as the user again
807 ok = change_to_user_and_service_by_fsp(state->dir_fsp);
810 status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
814 if (!NT_STATUS_IS_OK(status)) {
816 * Both the sync dos_mode() as well as the async
817 * dos_mode_at_[send|recv] have no real error return, the only
818 * unhandled error is when the stat info in smb_fname is not
819 * valid (cf the checks in dos_mode() and dos_mode_at_send().
821 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
822 * dos_mode_post() which also does the mapping of a last resort
823 * from S_IFMT(st_mode).
825 * Only if we get NT_STATUS_NOT_IMPLEMENTED or
826 * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
827 * fallback to sync processing.
829 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) &&
830 !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED))
833 * state->dosmode should still be 0, but reset
837 status = NT_STATUS_OK;
840 if (NT_STATUS_IS_OK(status)) {
841 state->dosmode = dos_mode_post(state->dosmode,
842 state->smb_fname->fsp,
844 tevent_req_done(req);
849 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
852 state->dosmode = fdos_mode(state->smb_fname->fsp);
853 tevent_req_done(req);
857 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
859 struct dos_mode_at_state *state =
861 struct dos_mode_at_state);
864 if (tevent_req_is_nterror(req, &status)) {
865 tevent_req_received(req);
869 *dosmode = state->dosmode;
870 tevent_req_received(req);
874 /*******************************************************************
875 chmod a file - but preserve some bits.
876 If "store dos attributes" is also set it will store the create time
877 from the stat struct in smb_fname (in NTTIME format) in the EA
879 ********************************************************************/
881 int file_set_dosmode(connection_struct *conn,
882 struct smb_filename *smb_fname,
884 struct smb_filename *parent_dir,
893 if (!CAN_WRITE(conn)) {
898 if (S_ISLNK(smb_fname->st.st_ex_mode)) {
899 /* A symlink in POSIX context, ignore */
903 if ((S_ISDIR(smb_fname->st.st_ex_mode)) &&
904 (dosmode & FILE_ATTRIBUTE_TEMPORARY))
910 dosmode &= SAMBA_ATTRIBUTES_MASK;
912 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
913 dosmode, smb_fname_str_dbg(smb_fname)));
915 if (smb_fname->fsp == NULL) {
920 if (smb_fname->fsp->posix_flags & FSP_POSIX_FLAGS_OPEN &&
921 !lp_store_dos_attributes(SNUM(conn)))
926 unixmode = smb_fname->st.st_ex_mode;
928 get_acl_group_bits(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;
933 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
935 /* Store the DOS attributes in an EA by preference. */
936 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn,
937 metadata_fsp(smb_fname->fsp),
939 if (NT_STATUS_IS_OK(status)) {
940 smb_fname->st.cached_dos_attributes = dosmode;
946 * Only fall back to using UNIX modes if
947 * we get NOT_IMPLEMENTED.
949 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
950 errno = map_errno_from_nt_status(status);
954 /* Fall back to UNIX modes. */
955 unixmode = unix_mode(
959 parent_dir != NULL ? parent_dir->fsp : NULL);
961 /* preserve the file type bits */
964 /* preserve the s bits */
965 mask |= (S_ISUID | S_ISGID);
967 /* preserve the t bit */
972 /* possibly preserve the x bits */
973 if (!MAP_ARCHIVE(conn))
975 if (!MAP_SYSTEM(conn))
977 if (!MAP_HIDDEN(conn))
980 unixmode |= (smb_fname->st.st_ex_mode & mask);
982 /* if we previously had any r bits set then leave them alone */
983 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
984 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
988 /* if we previously had any w bits set then leave them alone
989 whilst adding in the new w bits, if the new mode is not rdonly */
990 if (!(dosmode & FILE_ATTRIBUTE_READONLY)) {
991 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
995 * From the chmod 2 man page:
997 * "If the calling process is not privileged, and the group of the file
998 * does not match the effective group ID of the process or one of its
999 * supplementary group IDs, the S_ISGID bit will be turned off, but
1000 * this will not cause an error to be returned."
1002 * Simply refuse to do the chmod in this case.
1005 if (S_ISDIR(smb_fname->st.st_ex_mode) &&
1006 (unixmode & S_ISGID) &&
1007 geteuid() != sec_initial_uid() &&
1008 !current_user_in_group(conn, smb_fname->st.st_ex_gid))
1010 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1011 "set for directory %s\n",
1012 smb_fname_str_dbg(smb_fname)));
1017 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1022 if((errno != EPERM) && (errno != EACCES))
1025 if(!lp_dos_filemode(SNUM(conn)))
1028 /* We want DOS semantics, ie allow non owner with write permission to change the
1029 bits on a file. Just like file_ntimes below.
1032 if (!can_write_to_fsp(smb_fname->fsp))
1039 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1044 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1045 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1046 smb_fname->base_name);
1049 smb_fname->st.st_ex_mode = unixmode;
1056 NTSTATUS file_set_sparse(connection_struct *conn,
1060 const struct loadparm_substitution *lp_sub =
1061 loadparm_s3_global_substitution();
1062 uint32_t old_dosmode;
1063 uint32_t new_dosmode;
1066 if (!CAN_WRITE(conn)) {
1067 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1068 "on readonly share[%s]\n",
1069 smb_fname_str_dbg(fsp->fsp_name),
1071 lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
1072 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1076 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1077 * following access flags are granted.
1079 status = check_any_access_fsp(fsp,
1081 | FILE_WRITE_ATTRIBUTES
1082 | SEC_FILE_APPEND_DATA);
1083 if (!NT_STATUS_IS_OK(status)) {
1084 DBG_DEBUG("fname[%s] set[%u] "
1085 "access_mask[0x%08X] - access denied\n",
1086 smb_fname_str_dbg(fsp->fsp_name),
1092 if (fsp->fsp_flags.is_directory) {
1093 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1094 (sparse ? "set" : "clear"),
1095 smb_fname_str_dbg(fsp->fsp_name)));
1096 return NT_STATUS_INVALID_PARAMETER;
1099 if (IS_IPC(conn) || IS_PRINT(conn)) {
1100 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1101 (sparse ? "set" : "clear")));
1102 return NT_STATUS_INVALID_PARAMETER;
1105 if (fsp_is_alternate_stream(fsp)) {
1107 * MS-FSA 2.1.1.5 IsSparse
1109 * This is a per stream attribute, but our backends don't
1110 * support it a consistent way, therefore just pretend
1111 * success and ignore the request.
1113 DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
1114 "[%s]\n", fsp_str_dbg(fsp));
1115 return NT_STATUS_OK;
1118 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1119 sparse, smb_fname_str_dbg(fsp->fsp_name)));
1121 if (!lp_store_dos_attributes(SNUM(conn))) {
1122 return NT_STATUS_INVALID_DEVICE_REQUEST;
1125 status = vfs_stat_fsp(fsp);
1126 if (!NT_STATUS_IS_OK(status)) {
1130 old_dosmode = fdos_mode(fsp);
1132 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1133 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1134 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1135 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1137 return NT_STATUS_OK;
1140 /* Store the DOS attributes in an EA. */
1141 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1142 if (!NT_STATUS_IS_OK(status)) {
1146 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1147 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1148 fsp->fsp_name->base_name);
1150 fsp->fsp_name->st.cached_dos_attributes = new_dosmode;
1151 fsp->fsp_flags.is_sparse = sparse;
1153 return NT_STATUS_OK;
1156 /*******************************************************************
1157 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1159 *******************************************************************/
1161 int file_ntimes(connection_struct *conn,
1163 struct smb_file_time *ft)
1169 DBG_INFO("actime: %s",
1170 time_to_asc(convert_timespec_to_time_t(ft->atime)));
1171 DBG_INFO("modtime: %s",
1172 time_to_asc(convert_timespec_to_time_t(ft->mtime)));
1173 DBG_INFO("ctime: %s",
1174 time_to_asc(convert_timespec_to_time_t(ft->ctime)));
1175 DBG_INFO("createtime: %s",
1176 time_to_asc(convert_timespec_to_time_t(ft->create_time)));
1178 /* Don't update the time on read-only shares */
1179 /* We need this as set_filetime (which can be called on
1180 close and other paths) can end up calling this function
1181 without the NEED_WRITE protection. Found by :
1182 Leo Weppelman <leo@wau.mis.ah.nl>
1185 if (!CAN_WRITE(conn)) {
1189 if (SMB_VFS_FNTIMES(fsp, ft) == 0) {
1193 if((errno != EPERM) && (errno != EACCES)) {
1197 if(!lp_dos_filetimes(SNUM(conn))) {
1201 /* We have permission (given by the Samba admin) to
1202 break POSIX semantics and allow a user to change
1203 the time on a file they don't own but can write to
1207 /* Check if we have write access. */
1208 if (can_write_to_fsp(fsp)) {
1209 /* We are allowed to become root and change the filetime. */
1211 ret = SMB_VFS_FNTIMES(fsp, ft);
1218 /******************************************************************
1219 Force a "sticky" write time on a pathname. This will always be
1220 returned on all future write time queries and set on close.
1221 ******************************************************************/
1223 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1225 if (is_omit_timespec(&mtime)) {
1229 if (!set_sticky_write_time(fileid, mtime)) {
1236 /******************************************************************
1237 Force a "sticky" write time on an fsp. This will always be
1238 returned on all future write time queries and set on close.
1239 ******************************************************************/
1241 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1243 if (is_omit_timespec(&mtime)) {
1247 fsp->fsp_flags.write_time_forced = true;
1248 TALLOC_FREE(fsp->update_write_time_event);
1250 return set_sticky_write_time_path(fsp->file_id, mtime);
1253 /******************************************************************
1254 Set a create time EA.
1255 ******************************************************************/
1257 NTSTATUS set_create_timespec_ea(struct files_struct *fsp,
1258 struct timespec create_time)
1263 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
1264 return NT_STATUS_OK;
1267 dosmode = fdos_mode(fsp);
1269 fsp->fsp_name->st.st_ex_btime = create_time;
1270 ret = file_set_dosmode(fsp->conn, fsp->fsp_name, dosmode, NULL, false);
1272 return map_nt_error_from_unix(errno);
1275 DBG_DEBUG("wrote create time EA for file %s\n",
1276 smb_fname_str_dbg(fsp->fsp_name));
1278 return NT_STATUS_OK;
1281 /******************************************************************
1282 Return a create time.
1283 ******************************************************************/
1285 struct timespec get_create_timespec(connection_struct *conn,
1286 struct files_struct *fsp,
1287 const struct smb_filename *smb_fname)
1290 struct files_struct *meta_fsp = metadata_fsp(fsp);
1291 return meta_fsp->fsp_name->st.st_ex_btime;
1293 return smb_fname->st.st_ex_btime;
1296 /******************************************************************
1297 Return a change time (may look at EA in future).
1298 ******************************************************************/
1300 struct timespec get_change_timespec(connection_struct *conn,
1301 struct files_struct *fsp,
1302 const struct smb_filename *smb_fname)
1304 return smb_fname->st.st_ex_mtime;