smbd: use check_any_access_fsp() for all access checks
[Samba.git] / source3 / smbd / dosmode.c
blob1472af7d0591ad891f9e4b4f881061e6a72c39fa
1 /*
2 Unix SMB/CIFS implementation.
3 dos mode handling functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) James Peach 2006
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "includes.h"
22 #include "globals.h"
23 #include "system/filesys.h"
24 #include "librpc/gen_ndr/ndr_xattr.h"
25 #include "librpc/gen_ndr/ioctl.h"
26 #include "../libcli/security/security.h"
27 #include "smbd/smbd.h"
28 #include "lib/param/loadparm.h"
29 #include "lib/util/tevent_ntstatus.h"
30 #include "lib/util/string_wrappers.h"
31 #include "fake_file.h"
33 static void dos_mode_debug_print(const char *func, uint32_t mode)
35 fstring modestr;
37 if (DEBUGLEVEL < DBGLVL_INFO) {
38 return;
41 modestr[0] = '\0';
43 if (mode & FILE_ATTRIBUTE_HIDDEN) {
44 fstrcat(modestr, "h");
46 if (mode & FILE_ATTRIBUTE_READONLY) {
47 fstrcat(modestr, "r");
49 if (mode & FILE_ATTRIBUTE_SYSTEM) {
50 fstrcat(modestr, "s");
52 if (mode & FILE_ATTRIBUTE_DIRECTORY) {
53 fstrcat(modestr, "d");
55 if (mode & FILE_ATTRIBUTE_ARCHIVE) {
56 fstrcat(modestr, "a");
58 if (mode & FILE_ATTRIBUTE_SPARSE) {
59 fstrcat(modestr, "[sparse]");
61 if (mode & FILE_ATTRIBUTE_OFFLINE) {
62 fstrcat(modestr, "[offline]");
64 if (mode & FILE_ATTRIBUTE_COMPRESSED) {
65 fstrcat(modestr, "[compressed]");
68 DBG_INFO("%s returning (0x%x): \"%s\"\n", func, (unsigned)mode,
69 modestr);
72 static uint32_t filter_mode_by_protocol(uint32_t mode)
74 if (get_Protocol() <= PROTOCOL_LANMAN2) {
75 DEBUG(10,("filter_mode_by_protocol: "
76 "filtering result 0x%x to 0x%x\n",
77 (unsigned int)mode,
78 (unsigned int)(mode & 0x3f) ));
79 mode &= 0x3f;
81 return mode;
84 /****************************************************************************
85 Change a dos mode to a unix mode.
86 Base permission for files:
87 if creating file and inheriting (i.e. parent_dir != NULL)
88 apply read/write bits from parent directory.
89 else
90 everybody gets read bit set
91 dos readonly is represented in unix by removing everyone's write bit
92 dos archive is represented in unix by the user's execute bit
93 dos system is represented in unix by the group's execute bit
94 dos hidden is represented in unix by the other's execute bit
95 if !inheriting {
96 Then apply create mask,
97 then add force bits.
99 Base permission for directories:
100 dos directory is represented in unix by unix's dir bit and the exec bit
101 if !inheriting {
102 Then apply create mask,
103 then add force bits.
105 ****************************************************************************/
107 mode_t unix_mode(connection_struct *conn, int dosmode,
108 const struct smb_filename *smb_fname,
109 struct files_struct *parent_dirfsp)
111 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
112 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
113 * inheriting. */
115 if ((dosmode & FILE_ATTRIBUTE_READONLY) &&
116 !lp_store_dos_attributes(SNUM(conn))) {
117 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
120 if ((parent_dirfsp != NULL) && lp_inherit_permissions(SNUM(conn))) {
121 struct stat_ex sbuf = { .st_ex_nlink = 0, };
122 int ret;
124 DBG_DEBUG("[%s] inheriting from [%s]\n",
125 smb_fname_str_dbg(smb_fname),
126 smb_fname_str_dbg(parent_dirfsp->fsp_name));
128 ret = SMB_VFS_FSTAT(parent_dirfsp, &sbuf);
129 if (ret != 0) {
130 DBG_ERR("fstat failed [%s]: %s\n",
131 smb_fname_str_dbg(parent_dirfsp->fsp_name),
132 strerror(errno));
133 return(0); /* *** shouldn't happen! *** */
136 /* Save for later - but explicitly remove setuid bit for safety. */
137 dir_mode = sbuf.st_ex_mode & ~S_ISUID;
138 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
139 smb_fname_str_dbg(smb_fname), (int)dir_mode));
140 /* Clear "result" */
141 result = 0;
144 if (dosmode & FILE_ATTRIBUTE_DIRECTORY) {
145 /* We never make directories read only for the owner as under DOS a user
146 can always create a file in a read-only directory. */
147 result |= (S_IFDIR | S_IWUSR);
149 if (dir_mode) {
150 /* Inherit mode of parent directory. */
151 result |= dir_mode;
152 } else {
153 /* Provisionally add all 'x' bits */
154 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
156 /* Apply directory mask */
157 result &= lp_directory_mask(SNUM(conn));
158 /* Add in force bits */
159 result |= lp_force_directory_mode(SNUM(conn));
161 } else {
162 if ((dosmode & FILE_ATTRIBUTE_ARCHIVE) &&
163 lp_map_archive(SNUM(conn))) {
164 result |= S_IXUSR;
167 if ((dosmode & FILE_ATTRIBUTE_SYSTEM) &&
168 lp_map_system(SNUM(conn))) {
169 result |= S_IXGRP;
172 if ((dosmode & FILE_ATTRIBUTE_HIDDEN) &&
173 lp_map_hidden(SNUM(conn))) {
174 result |= S_IXOTH;
177 if (dir_mode) {
178 /* Inherit 666 component of parent directory mode */
179 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
180 } else {
181 /* Apply mode mask */
182 result &= lp_create_mask(SNUM(conn));
183 /* Add in force bits */
184 result |= lp_force_create_mode(SNUM(conn));
188 DBG_INFO("unix_mode(%s) returning 0%o\n",
189 smb_fname_str_dbg(smb_fname), (int)result);
191 return(result);
194 /****************************************************************************
195 Change a unix mode to a dos mode.
196 ****************************************************************************/
198 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
199 const struct stat_ex *st,
200 struct files_struct *fsp)
202 int result = 0;
203 enum mapreadonly_options ro_opts =
204 (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
206 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
207 /* if we can find out if a file is immutable we should report it r/o */
208 if (st->st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
209 result |= FILE_ATTRIBUTE_READONLY;
211 #endif
212 if (ro_opts == MAP_READONLY_YES) {
213 /* Original Samba method - map inverse of user "w" bit. */
214 if ((st->st_ex_mode & S_IWUSR) == 0) {
215 result |= FILE_ATTRIBUTE_READONLY;
217 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
218 /* smb_fname->fsp can be NULL for an MS-DFS link. */
219 /* Check actual permissions for read-only. */
220 if ((fsp != NULL) && !can_write_to_fsp(fsp)) {
221 result |= FILE_ATTRIBUTE_READONLY;
223 } /* Else never set the readonly bit. */
225 if (MAP_ARCHIVE(conn) && ((st->st_ex_mode & S_IXUSR) != 0)) {
226 result |= FILE_ATTRIBUTE_ARCHIVE;
229 if (MAP_SYSTEM(conn) && ((st->st_ex_mode & S_IXGRP) != 0)) {
230 result |= FILE_ATTRIBUTE_SYSTEM;
233 if (MAP_HIDDEN(conn) && ((st->st_ex_mode & S_IXOTH) != 0)) {
234 result |= FILE_ATTRIBUTE_HIDDEN;
237 if (S_ISDIR(st->st_ex_mode)) {
238 result = FILE_ATTRIBUTE_DIRECTORY |
239 (result & FILE_ATTRIBUTE_READONLY);
242 dos_mode_debug_print(__func__, result);
244 return result;
247 /****************************************************************************
248 Get DOS attributes from an EA.
249 This can also pull the create time into the stat struct inside smb_fname.
250 ****************************************************************************/
252 NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
253 DATA_BLOB blob,
254 uint32_t *pattr)
256 struct xattr_DOSATTRIB dosattrib;
257 enum ndr_err_code ndr_err;
258 uint32_t dosattr;
260 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
261 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
263 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
264 DBG_WARNING("bad ndr decode "
265 "from EA on file %s: Error = %s\n",
266 smb_fname_str_dbg(smb_fname),
267 ndr_errstr(ndr_err));
268 return ndr_map_error2ntstatus(ndr_err);
271 DBG_DEBUG("%s attr = %s\n",
272 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex);
274 switch (dosattrib.version) {
275 case 0xFFFF:
276 dosattr = dosattrib.info.compatinfoFFFF.attrib;
277 break;
278 case 1:
279 dosattr = dosattrib.info.info1.attrib;
280 if (!null_nttime(dosattrib.info.info1.create_time)) {
281 struct timespec create_time =
282 nt_time_to_unix_timespec(
283 dosattrib.info.info1.create_time);
285 update_stat_ex_create_time(&smb_fname->st,
286 create_time);
288 DBG_DEBUG("file %s case 1 set btime %s",
289 smb_fname_str_dbg(smb_fname),
290 time_to_asc(convert_timespec_to_time_t(
291 create_time)));
293 break;
294 case 2:
295 dosattr = dosattrib.info.oldinfo2.attrib;
296 /* Don't know what flags to check for this case. */
297 break;
298 case 3:
299 dosattr = dosattrib.info.info3.attrib;
300 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
301 !null_nttime(dosattrib.info.info3.create_time)) {
302 struct timespec create_time =
303 nt_time_to_full_timespec(
304 dosattrib.info.info3.create_time);
306 update_stat_ex_create_time(&smb_fname->st,
307 create_time);
309 DBG_DEBUG("file %s case 3 set btime %s",
310 smb_fname_str_dbg(smb_fname),
311 time_to_asc(convert_timespec_to_time_t(
312 create_time)));
314 break;
315 case 4:
316 case 5:
318 uint32_t info_valid_flags;
319 NTTIME info_create_time;
321 if (dosattrib.version == 4) {
322 info_valid_flags = dosattrib.info.info4.valid_flags;
323 info_create_time = dosattrib.info.info4.create_time;
324 dosattr = dosattrib.info.info4.attrib;
325 } else {
326 info_valid_flags = dosattrib.info.info5.valid_flags;
327 info_create_time = dosattrib.info.info5.create_time;
328 dosattr = dosattrib.info.info5.attrib;
331 if ((info_valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
332 !null_nttime(info_create_time))
334 struct timespec creat_time;
336 creat_time = nt_time_to_full_timespec(info_create_time);
337 update_stat_ex_create_time(&smb_fname->st, creat_time);
339 DBG_DEBUG("file [%s] creation time [%s]\n",
340 smb_fname_str_dbg(smb_fname),
341 nt_time_string(talloc_tos(), info_create_time));
344 break;
346 default:
347 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
348 smb_fname_str_dbg(smb_fname), blob.data);
349 /* Should this be INTERNAL_ERROR? */
350 return NT_STATUS_INVALID_PARAMETER;
353 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
354 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
357 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
358 *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
360 dos_mode_debug_print(__func__, *pattr);
362 return NT_STATUS_OK;
365 NTSTATUS fget_ea_dos_attribute(struct files_struct *fsp,
366 uint32_t *pattr)
368 DATA_BLOB blob;
369 ssize_t sizeret;
370 fstring attrstr;
371 NTSTATUS status;
373 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
374 return NT_STATUS_NOT_IMPLEMENTED;
377 /* Don't reset pattr to zero as we may already have filename-based attributes we
378 need to preserve. */
380 sizeret = SMB_VFS_FGETXATTR(fsp,
381 SAMBA_XATTR_DOS_ATTRIB,
382 attrstr,
383 sizeof(attrstr));
384 if (sizeret == -1 && ( errno == EPERM || errno == EACCES )) {
385 /* we may also retrieve dos attribs for unreadable files, this
386 is why we'll retry as root. We don't use root in the first
387 run because in cases like NFS, root might have even less
388 rights than the real user
390 set_effective_capability(DAC_OVERRIDE_CAPABILITY);
391 sizeret = SMB_VFS_FGETXATTR(fsp,
392 SAMBA_XATTR_DOS_ATTRIB,
393 attrstr,
394 sizeof(attrstr));
395 drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
397 if (sizeret == -1) {
398 DBG_INFO("Cannot get attribute "
399 "from EA on file %s: Error = %s\n",
400 fsp_str_dbg(fsp), strerror(errno));
401 return map_nt_error_from_unix(errno);
404 blob.data = (uint8_t *)attrstr;
405 blob.length = sizeret;
407 status = parse_dos_attribute_blob(fsp->fsp_name, blob, pattr);
408 if (!NT_STATUS_IS_OK(status)) {
409 return status;
412 return NT_STATUS_OK;
415 /****************************************************************************
416 Set DOS attributes in an EA.
417 Also sets the create time.
418 ****************************************************************************/
420 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
421 struct smb_filename *smb_fname,
422 uint32_t dosmode)
424 struct xattr_DOSATTRIB dosattrib = { .version = 0, };
425 enum ndr_err_code ndr_err;
426 DATA_BLOB blob = { .data = NULL, };
427 struct timespec btime;
428 int ret;
430 if (!lp_store_dos_attributes(SNUM(conn))) {
431 return NT_STATUS_NOT_IMPLEMENTED;
434 if (smb_fname->fsp == NULL) {
435 /* symlink */
436 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
439 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
440 * vfs_default via DMAPI if that is enabled.
442 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
444 dosattrib.version = 5;
445 dosattrib.info.info5.valid_flags = XATTR_DOSINFO_ATTRIB |
446 XATTR_DOSINFO_CREATE_TIME;
447 dosattrib.info.info5.attrib = dosmode;
448 dosattrib.info.info5.create_time = full_timespec_to_nt_time(
449 &smb_fname->st.st_ex_btime);
451 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
452 (unsigned int)dosmode,
453 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
454 smb_fname_str_dbg(smb_fname) ));
456 ndr_err = ndr_push_struct_blob(
457 &blob, talloc_tos(), &dosattrib,
458 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
460 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
461 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
462 ndr_errstr(ndr_err)));
463 return ndr_map_error2ntstatus(ndr_err);
466 if (blob.data == NULL || blob.length == 0) {
467 /* Should this be INTERNAL_ERROR? */
468 return NT_STATUS_INVALID_PARAMETER;
471 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
472 SAMBA_XATTR_DOS_ATTRIB,
473 blob.data, blob.length, 0);
474 if (ret != 0) {
475 NTSTATUS status = NT_STATUS_OK;
476 bool set_dosmode_ok = false;
478 if ((errno != EPERM) && (errno != EACCES)) {
479 DBG_INFO("Cannot set "
480 "attribute EA on file %s: Error = %s\n",
481 smb_fname_str_dbg(smb_fname), strerror(errno));
482 return map_nt_error_from_unix(errno);
485 /* We want DOS semantics, ie allow non owner with write permission to change the
486 bits on a file. Just like file_ntimes below.
489 /* Check if we have write access. */
490 if (!CAN_WRITE(conn)) {
491 return NT_STATUS_ACCESS_DENIED;
494 status = smbd_check_access_rights_fsp(conn->cwd_fsp,
495 smb_fname->fsp,
496 false,
497 FILE_WRITE_ATTRIBUTES);
498 if (NT_STATUS_IS_OK(status)) {
499 set_dosmode_ok = true;
502 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
503 set_dosmode_ok = can_write_to_fsp(smb_fname->fsp);
506 if (!set_dosmode_ok) {
507 return NT_STATUS_ACCESS_DENIED;
510 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);
515 if (ret == 0) {
516 status = NT_STATUS_OK;
518 if (!NT_STATUS_IS_OK(status)) {
519 return status;
524 * We correctly stored the create time.
525 * We *always* set XATTR_DOSINFO_CREATE_TIME,
526 * so now it can no longer be considered
527 * calculated. Make sure to use the value rounded
528 * to NTTIME granularity we've stored in the xattr.
530 btime = nt_time_to_full_timespec(dosattrib.info.info5.create_time);
531 update_stat_ex_create_time(&smb_fname->st, btime);
533 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
534 (unsigned int)dosmode,
535 smb_fname_str_dbg(smb_fname)));
536 return NT_STATUS_OK;
539 static uint32_t
540 dos_mode_from_name(connection_struct *conn, const char *name, uint32_t dosmode)
542 const char *p = NULL;
543 uint32_t result = dosmode;
545 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
546 lp_hide_dot_files(SNUM(conn)))
548 p = strrchr_m(name, '/');
549 if (p) {
550 p++;
551 } else {
552 p = name;
555 /* Only . and .. are not hidden. */
556 if ((p[0] == '.') && !(ISDOT(p) || ISDOTDOT(p))) {
557 result |= FILE_ATTRIBUTE_HIDDEN;
561 if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, name)) {
562 result |= FILE_ATTRIBUTE_HIDDEN;
565 return result;
568 /****************************************************************************
569 Change a unix mode to a dos mode for an ms dfs link.
570 ****************************************************************************/
572 uint32_t dos_mode_msdfs(connection_struct *conn,
573 const char *name,
574 const struct stat_ex *st)
576 uint32_t result = 0;
578 DEBUG(8, ("dos_mode_msdfs: %s\n", name));
580 if (!VALID_STAT(*st)) {
581 return 0;
584 result = dos_mode_from_name(conn, name, result);
585 result |= dos_mode_from_sbuf(conn, st, NULL);
587 if (result == 0) {
588 result = FILE_ATTRIBUTE_NORMAL;
591 result = filter_mode_by_protocol(result);
594 * Add in that it is a reparse point
596 result |= FILE_ATTRIBUTE_REPARSE_POINT;
598 dos_mode_debug_print(__func__, result);
600 return(result);
604 * check whether a file or directory is flagged as compressed.
606 static NTSTATUS dos_mode_check_compressed(struct files_struct *fsp,
607 bool *is_compressed)
609 NTSTATUS status;
610 uint16_t compression_fmt;
612 status = SMB_VFS_FGET_COMPRESSION(
613 fsp->conn, talloc_tos(), fsp, &compression_fmt);
614 if (!NT_STATUS_IS_OK(status)) {
615 return status;
618 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
619 *is_compressed = true;
620 } else {
621 *is_compressed = false;
623 return NT_STATUS_OK;
626 static uint32_t dos_mode_post(uint32_t dosmode,
627 struct files_struct *fsp,
628 const char *func)
630 struct smb_filename *smb_fname = NULL;
631 NTSTATUS status;
633 if (fsp != NULL) {
634 smb_fname = fsp->fsp_name;
636 SMB_ASSERT(smb_fname != NULL);
639 * According to MS-FSA a stream name does not have
640 * separate DOS attribute metadata, so we must return
641 * the DOS attribute from the base filename. With one caveat,
642 * a non-default stream name can never be a directory.
644 * As this is common to all streams data stores, we handle
645 * it here instead of inside all stream VFS modules.
647 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
650 if (is_named_stream(smb_fname)) {
651 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
652 dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
655 if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
656 bool compressed = false;
658 status = dos_mode_check_compressed(fsp, &compressed);
659 if (NT_STATUS_IS_OK(status) && compressed) {
660 dosmode |= FILE_ATTRIBUTE_COMPRESSED;
664 dosmode |= dos_mode_from_name(fsp->conn, smb_fname->base_name, dosmode);
666 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
667 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
668 } else if (dosmode == 0) {
669 dosmode = FILE_ATTRIBUTE_NORMAL;
672 dosmode = filter_mode_by_protocol(dosmode);
674 dos_mode_debug_print(func, dosmode);
675 return dosmode;
678 /****************************************************************************
679 Change a unix mode to a dos mode.
680 May also read the create timespec into the stat struct in smb_fname
681 if "store dos attributes" is true.
682 ****************************************************************************/
684 uint32_t fdos_mode(struct files_struct *fsp)
686 uint32_t result = 0;
687 NTSTATUS status = NT_STATUS_OK;
689 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)) {
696 return 0;
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,
709 metadata_fsp(fsp),
710 &result);
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,
717 &fsp->fsp_name->st,
718 fsp);
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;
729 uint32_t dosmode;
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);
747 if (req == NULL) {
748 return NULL;
751 *state = (struct dos_mode_at_state) {
752 .dir_fsp = dir_fsp,
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;
768 } else {
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,
782 dir_fsp,
783 smb_fname);
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);
789 return 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,
796 struct tevent_req);
797 struct dos_mode_at_state *state =
798 tevent_req_data(req,
799 struct dos_mode_at_state);
800 struct vfs_aio_state aio_state;
801 NTSTATUS status;
802 bool ok;
805 * Make sure we run as the user again
807 ok = change_to_user_and_service_by_fsp(state->dir_fsp);
808 SMB_ASSERT(ok);
810 status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
811 &aio_state,
812 &state->dosmode);
813 TALLOC_FREE(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
834 * it to be sure.
836 state->dosmode = 0;
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,
843 __func__);
844 tevent_req_done(req);
845 return;
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);
854 return;
857 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
859 struct dos_mode_at_state *state =
860 tevent_req_data(req,
861 struct dos_mode_at_state);
862 NTSTATUS status;
864 if (tevent_req_is_nterror(req, &status)) {
865 tevent_req_received(req);
866 return status;
869 *dosmode = state->dosmode;
870 tevent_req_received(req);
871 return NT_STATUS_OK;
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
878 attribute also.
879 ********************************************************************/
881 int file_set_dosmode(connection_struct *conn,
882 struct smb_filename *smb_fname,
883 uint32_t dosmode,
884 struct smb_filename *parent_dir,
885 bool newfile)
887 int mask=0;
888 mode_t tmp;
889 mode_t unixmode;
890 int ret = -1;
891 NTSTATUS status;
893 if (!CAN_WRITE(conn)) {
894 errno = EROFS;
895 return -1;
898 if (S_ISLNK(smb_fname->st.st_ex_mode)) {
899 /* A symlink in POSIX context, ignore */
900 return 0;
903 if ((S_ISDIR(smb_fname->st.st_ex_mode)) &&
904 (dosmode & FILE_ATTRIBUTE_TEMPORARY))
906 errno = EINVAL;
907 return -1;
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) {
916 errno = ENOENT;
917 return -1;
920 if (smb_fname->fsp->posix_flags & FSP_POSIX_FLAGS_OPEN &&
921 !lp_store_dos_attributes(SNUM(conn)))
923 return 0;
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;
932 else
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),
938 dosmode);
939 if (NT_STATUS_IS_OK(status)) {
940 smb_fname->st.cached_dos_attributes = dosmode;
941 ret = 0;
942 goto done;
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);
951 return -1;
954 /* Fall back to UNIX modes. */
955 unixmode = unix_mode(
956 conn,
957 dosmode,
958 smb_fname,
959 parent_dir != NULL ? parent_dir->fsp : NULL);
961 /* preserve the file type bits */
962 mask |= S_IFMT;
964 /* preserve the s bits */
965 mask |= (S_ISUID | S_ISGID);
967 /* preserve the t bit */
968 #ifdef S_ISVTX
969 mask |= S_ISVTX;
970 #endif
972 /* possibly preserve the x bits */
973 if (!MAP_ARCHIVE(conn))
974 mask |= S_IXUSR;
975 if (!MAP_SYSTEM(conn))
976 mask |= S_IXGRP;
977 if (!MAP_HIDDEN(conn))
978 mask |= S_IXOTH;
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);
985 unixmode |= tmp;
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)));
1013 errno = EPERM;
1014 return -1;
1017 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1018 if (ret == 0) {
1019 goto done;
1022 if((errno != EPERM) && (errno != EACCES))
1023 return -1;
1025 if(!lp_dos_filemode(SNUM(conn)))
1026 return -1;
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))
1034 errno = EACCES;
1035 return -1;
1038 set_effective_capability(DAC_OVERRIDE_CAPABILITY);
1039 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1040 drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
1042 done:
1043 if (!newfile) {
1044 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1045 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1046 smb_fname->base_name);
1048 if (ret == 0) {
1049 smb_fname->st.st_ex_mode = unixmode;
1052 return( ret );
1056 NTSTATUS file_set_sparse(connection_struct *conn,
1057 files_struct *fsp,
1058 bool sparse)
1060 const struct loadparm_substitution *lp_sub =
1061 loadparm_s3_global_substitution();
1062 uint32_t old_dosmode;
1063 uint32_t new_dosmode;
1064 NTSTATUS status;
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),
1070 sparse,
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,
1080 FILE_WRITE_DATA
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),
1087 sparse,
1088 fsp->access_mask);
1089 return status;
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)) {
1127 return 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;
1136 } else {
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)) {
1143 return 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
1158 than POSIX.
1159 *******************************************************************/
1161 int file_ntimes(connection_struct *conn,
1162 files_struct *fsp,
1163 struct smb_file_time *ft)
1165 int ret = -1;
1167 errno = 0;
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)) {
1186 return 0;
1189 if (SMB_VFS_FNTIMES(fsp, ft) == 0) {
1190 return 0;
1193 if((errno != EPERM) && (errno != EACCES)) {
1194 return -1;
1197 if(!lp_dos_filetimes(SNUM(conn))) {
1198 return -1;
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
1204 (as DOS does).
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. */
1210 set_effective_capability(DAC_OVERRIDE_CAPABILITY);
1211 ret = SMB_VFS_FNTIMES(fsp, ft);
1212 drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
1215 return ret;
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)) {
1226 return true;
1229 if (!set_sticky_write_time(fileid, mtime)) {
1230 return false;
1233 return true;
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)) {
1244 return true;
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)
1260 uint32_t dosmode;
1261 int ret;
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);
1271 if (ret == -1) {
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)
1289 if (fsp != NULL) {
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;