smbd: Convert refuse_symlink_fsp() to bool
[samba4-gss.git] / source3 / smbd / dosmode.c
blobf39f8a7d15bf0372bc4e2a1ffa1ebd00144c54b3
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]");
67 if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
68 fstrcat(modestr, "[reparse_point]");
71 DBG_INFO("%s returning (0x%" PRIx32 "): \"%s\"\n",
72 func,
73 mode,
74 modestr);
77 static uint32_t filter_mode_by_protocol(enum protocol_types protocol,
78 uint32_t mode)
80 if (protocol <= PROTOCOL_LANMAN2) {
81 DEBUG(10,("filter_mode_by_protocol: "
82 "filtering result 0x%x to 0x%x\n",
83 (unsigned int)mode,
84 (unsigned int)(mode & 0x3f) ));
85 mode &= 0x3f;
87 return mode;
90 mode_t apply_conf_file_mask(struct connection_struct *conn, mode_t mode)
92 mode &= lp_create_mask(SNUM(conn));
93 mode |= lp_force_create_mode(SNUM(conn));
94 return mode;
97 mode_t apply_conf_dir_mask(struct connection_struct *conn, mode_t mode)
99 mode &= lp_directory_mask(SNUM(conn));
100 mode |= lp_force_directory_mode(SNUM(conn));
101 return mode;
104 /****************************************************************************
105 Change a dos mode to a unix mode.
106 Base permission for files:
107 if creating file and inheriting (i.e. parent_dir != NULL)
108 apply read/write bits from parent directory.
109 else
110 everybody gets read bit set
111 dos readonly is represented in unix by removing everyone's write bit
112 dos archive is represented in unix by the user's execute bit
113 dos system is represented in unix by the group's execute bit
114 dos hidden is represented in unix by the other's execute bit
115 if !inheriting {
116 Then apply create mask,
117 then add force bits.
119 Base permission for directories:
120 dos directory is represented in unix by unix's dir bit and the exec bit
121 if !inheriting {
122 Then apply create mask,
123 then add force bits.
125 ****************************************************************************/
127 mode_t unix_mode(connection_struct *conn, int dosmode,
128 const struct smb_filename *smb_fname,
129 struct files_struct *parent_dirfsp)
131 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
132 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
133 * inheriting. */
135 if ((dosmode & FILE_ATTRIBUTE_READONLY) &&
136 !lp_store_dos_attributes(SNUM(conn))) {
137 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
140 if ((parent_dirfsp != NULL) && lp_inherit_permissions(SNUM(conn))) {
141 struct stat_ex sbuf = { .st_ex_nlink = 0, };
142 int ret;
144 DBG_DEBUG("[%s] inheriting from [%s]\n",
145 smb_fname_str_dbg(smb_fname),
146 smb_fname_str_dbg(parent_dirfsp->fsp_name));
148 ret = SMB_VFS_FSTAT(parent_dirfsp, &sbuf);
149 if (ret != 0) {
150 DBG_ERR("fstat failed [%s]: %s\n",
151 smb_fname_str_dbg(parent_dirfsp->fsp_name),
152 strerror(errno));
153 return(0); /* *** shouldn't happen! *** */
156 /* Save for later - but explicitly remove setuid bit for safety. */
157 dir_mode = sbuf.st_ex_mode & ~S_ISUID;
158 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
159 smb_fname_str_dbg(smb_fname), (int)dir_mode));
160 /* Clear "result" */
161 result = 0;
164 if (dosmode & FILE_ATTRIBUTE_DIRECTORY) {
165 /* We never make directories read only for the owner as under DOS a user
166 can always create a file in a read-only directory. */
167 result |= (S_IFDIR | S_IWUSR);
169 if (dir_mode) {
170 /* Inherit mode of parent directory. */
171 result |= dir_mode;
172 } else {
173 /* Provisionally add all 'x' bits */
174 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
175 result = apply_conf_dir_mask(conn, result);
177 } else {
178 if ((dosmode & FILE_ATTRIBUTE_ARCHIVE) &&
179 lp_map_archive(SNUM(conn))) {
180 result |= S_IXUSR;
183 if ((dosmode & FILE_ATTRIBUTE_SYSTEM) &&
184 lp_map_system(SNUM(conn))) {
185 result |= S_IXGRP;
188 if ((dosmode & FILE_ATTRIBUTE_HIDDEN) &&
189 lp_map_hidden(SNUM(conn))) {
190 result |= S_IXOTH;
193 if (dir_mode) {
194 /* Inherit 666 component of parent directory mode */
195 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
196 } else {
197 result = apply_conf_file_mask(conn, result);
201 DBG_INFO("unix_mode(%s) returning 0%o\n",
202 smb_fname_str_dbg(smb_fname), (int)result);
204 return(result);
207 /****************************************************************************
208 Change a unix mode to a dos mode.
209 ****************************************************************************/
211 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
212 const struct stat_ex *st,
213 struct files_struct *fsp)
215 int result = 0;
216 enum mapreadonly_options ro_opts =
217 (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
219 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
220 /* if we can find out if a file is immutable we should report it r/o */
221 if (st->st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
222 result |= FILE_ATTRIBUTE_READONLY;
224 #endif
225 if (ro_opts == MAP_READONLY_YES) {
226 /* Original Samba method - map inverse of user "w" bit. */
227 if ((st->st_ex_mode & S_IWUSR) == 0) {
228 result |= FILE_ATTRIBUTE_READONLY;
231 if (ro_opts == MAP_READONLY_PERMISSIONS) {
232 /* smb_fname->fsp can be NULL for an MS-DFS link. */
233 /* Check actual permissions for read-only. */
234 if ((fsp != NULL) && !can_write_to_fsp(fsp)) {
235 result |= FILE_ATTRIBUTE_READONLY;
239 if (MAP_ARCHIVE(conn) && ((st->st_ex_mode & S_IXUSR) != 0)) {
240 result |= FILE_ATTRIBUTE_ARCHIVE;
243 if (MAP_SYSTEM(conn) && ((st->st_ex_mode & S_IXGRP) != 0)) {
244 result |= FILE_ATTRIBUTE_SYSTEM;
247 if (MAP_HIDDEN(conn) && ((st->st_ex_mode & S_IXOTH) != 0)) {
248 result |= FILE_ATTRIBUTE_HIDDEN;
251 if (S_ISDIR(st->st_ex_mode)) {
252 result = FILE_ATTRIBUTE_DIRECTORY |
253 (result & FILE_ATTRIBUTE_READONLY);
256 dos_mode_debug_print(__func__, result);
258 return result;
261 /****************************************************************************
262 Get DOS attributes from an EA.
263 This can also pull the create time into the stat struct inside smb_fname.
264 ****************************************************************************/
266 NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
267 DATA_BLOB blob,
268 uint32_t *pattr)
270 struct xattr_DOSATTRIB dosattrib;
271 enum ndr_err_code ndr_err;
272 uint32_t dosattr;
274 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
275 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
277 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
278 DBG_WARNING("bad ndr decode "
279 "from EA on file %s: Error = %s\n",
280 smb_fname_str_dbg(smb_fname),
281 ndr_errstr(ndr_err));
282 return ndr_map_error2ntstatus(ndr_err);
285 DBG_DEBUG("%s attr = %s\n",
286 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex);
288 switch (dosattrib.version) {
289 case 0xFFFF:
290 dosattr = dosattrib.info.compatinfoFFFF.attrib;
291 break;
292 case 1:
293 dosattr = dosattrib.info.info1.attrib;
294 if (!null_nttime(dosattrib.info.info1.create_time)) {
295 struct timespec create_time =
296 nt_time_to_unix_timespec(
297 dosattrib.info.info1.create_time);
299 update_stat_ex_create_time(&smb_fname->st,
300 create_time);
302 DBG_DEBUG("file %s case 1 set btime %s",
303 smb_fname_str_dbg(smb_fname),
304 time_to_asc(convert_timespec_to_time_t(
305 create_time)));
307 break;
308 case 2:
309 dosattr = dosattrib.info.oldinfo2.attrib;
310 /* Don't know what flags to check for this case. */
311 break;
312 case 3:
313 dosattr = dosattrib.info.info3.attrib;
314 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
315 !null_nttime(dosattrib.info.info3.create_time)) {
316 struct timespec create_time =
317 nt_time_to_full_timespec(
318 dosattrib.info.info3.create_time);
320 update_stat_ex_create_time(&smb_fname->st,
321 create_time);
323 DBG_DEBUG("file %s case 3 set btime %s",
324 smb_fname_str_dbg(smb_fname),
325 time_to_asc(convert_timespec_to_time_t(
326 create_time)));
328 break;
329 case 4:
330 case 5:
332 uint32_t info_valid_flags;
333 NTTIME info_create_time;
335 if (dosattrib.version == 4) {
336 info_valid_flags = dosattrib.info.info4.valid_flags;
337 info_create_time = dosattrib.info.info4.create_time;
338 dosattr = dosattrib.info.info4.attrib;
339 } else {
340 info_valid_flags = dosattrib.info.info5.valid_flags;
341 info_create_time = dosattrib.info.info5.create_time;
342 dosattr = dosattrib.info.info5.attrib;
345 if ((info_valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
346 !null_nttime(info_create_time))
348 struct timespec creat_time;
350 creat_time = nt_time_to_full_timespec(info_create_time);
351 update_stat_ex_create_time(&smb_fname->st, creat_time);
353 DBG_DEBUG("file [%s] creation time [%s]\n",
354 smb_fname_str_dbg(smb_fname),
355 nt_time_string(talloc_tos(), info_create_time));
358 break;
360 default:
361 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
362 smb_fname_str_dbg(smb_fname), blob.data);
363 /* Should this be INTERNAL_ERROR? */
364 return NT_STATUS_INVALID_PARAMETER;
367 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
368 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
372 * _SPARSE and _REPARSE_POINT are valid on get but not on
373 * set. Both are created via special fcntls.
376 dosattr &= (SAMBA_ATTRIBUTES_MASK|
377 FILE_ATTRIBUTE_SPARSE|
378 FILE_ATTRIBUTE_REPARSE_POINT);
380 *pattr |= dosattr;
382 dos_mode_debug_print(__func__, *pattr);
384 return NT_STATUS_OK;
387 NTSTATUS fget_ea_dos_attribute(struct files_struct *fsp,
388 uint32_t *pattr)
390 DATA_BLOB blob;
391 ssize_t sizeret;
392 fstring attrstr;
393 NTSTATUS status;
395 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
396 return NT_STATUS_NOT_IMPLEMENTED;
399 /* Don't reset pattr to zero as we may already have filename-based attributes we
400 need to preserve. */
402 sizeret = SMB_VFS_FGETXATTR(fsp,
403 SAMBA_XATTR_DOS_ATTRIB,
404 attrstr,
405 sizeof(attrstr));
406 if (sizeret == -1 && ( errno == EPERM || errno == EACCES )) {
407 /* we may also retrieve dos attribs for unreadable files, this
408 is why we'll retry as root. We don't use root in the first
409 run because in cases like NFS, root might have even less
410 rights than the real user
412 become_root();
413 sizeret = SMB_VFS_FGETXATTR(fsp,
414 SAMBA_XATTR_DOS_ATTRIB,
415 attrstr,
416 sizeof(attrstr));
417 unbecome_root();
419 if (sizeret == -1) {
420 DBG_INFO("Cannot get attribute "
421 "from EA on file %s: Error = %s\n",
422 fsp_str_dbg(fsp), strerror(errno));
423 return map_nt_error_from_unix(errno);
426 blob.data = (uint8_t *)attrstr;
427 blob.length = sizeret;
429 status = parse_dos_attribute_blob(fsp->fsp_name, blob, pattr);
430 if (!NT_STATUS_IS_OK(status)) {
431 return status;
434 return NT_STATUS_OK;
437 /****************************************************************************
438 Set DOS attributes in an EA.
439 Also sets the create time.
440 ****************************************************************************/
442 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
443 struct smb_filename *smb_fname,
444 uint32_t dosmode)
446 struct xattr_DOSATTRIB dosattrib = { .version = 0, };
447 enum ndr_err_code ndr_err;
448 DATA_BLOB blob = { .data = NULL, };
449 struct timespec btime;
450 int ret;
452 if (!lp_store_dos_attributes(SNUM(conn))) {
453 return NT_STATUS_NOT_IMPLEMENTED;
456 if (smb_fname->fsp == NULL) {
457 /* symlink */
458 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
461 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
462 * vfs_default via DMAPI if that is enabled.
464 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
466 dosattrib.version = 5;
467 dosattrib.info.info5.valid_flags = XATTR_DOSINFO_ATTRIB |
468 XATTR_DOSINFO_CREATE_TIME;
469 dosattrib.info.info5.attrib = dosmode;
470 dosattrib.info.info5.create_time = full_timespec_to_nt_time(
471 &smb_fname->st.st_ex_btime);
473 DBG_DEBUG("set attribute 0x%" PRIx32 ", btime = %s on file %s\n",
474 dosmode,
475 time_to_asc(convert_timespec_to_time_t(
476 smb_fname->st.st_ex_btime)),
477 smb_fname_str_dbg(smb_fname));
479 ndr_err = ndr_push_struct_blob(
480 &blob, talloc_tos(), &dosattrib,
481 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
483 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
484 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
485 ndr_errstr(ndr_err)));
486 return ndr_map_error2ntstatus(ndr_err);
489 if (blob.data == NULL || blob.length == 0) {
490 /* Should this be INTERNAL_ERROR? */
491 return NT_STATUS_INVALID_PARAMETER;
494 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
495 SAMBA_XATTR_DOS_ATTRIB,
496 blob.data, blob.length, 0);
497 if (ret != 0) {
498 NTSTATUS status = NT_STATUS_OK;
499 bool set_dosmode_ok = false;
501 if ((errno != EPERM) && (errno != EACCES)) {
502 DBG_INFO("Cannot set "
503 "attribute EA on file %s: Error = %s\n",
504 smb_fname_str_dbg(smb_fname), strerror(errno));
505 return map_nt_error_from_unix(errno);
508 /* We want DOS semantics, ie allow non owner with write permission to change the
509 bits on a file. Just like file_ntimes below.
512 /* Check if we have write access. */
513 if (!CAN_WRITE(conn)) {
514 return NT_STATUS_ACCESS_DENIED;
517 status = smbd_check_access_rights_fsp(conn->cwd_fsp,
518 smb_fname->fsp,
519 false,
520 FILE_WRITE_ATTRIBUTES);
521 if (NT_STATUS_IS_OK(status)) {
522 set_dosmode_ok = true;
525 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
526 set_dosmode_ok = can_write_to_fsp(smb_fname->fsp);
529 if (!set_dosmode_ok) {
530 return NT_STATUS_ACCESS_DENIED;
533 become_root();
534 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
535 SAMBA_XATTR_DOS_ATTRIB,
536 blob.data, blob.length, 0);
537 if (ret == 0) {
538 status = NT_STATUS_OK;
540 unbecome_root();
541 if (!NT_STATUS_IS_OK(status)) {
542 return status;
547 * We correctly stored the create time.
548 * We *always* set XATTR_DOSINFO_CREATE_TIME,
549 * so now it can no longer be considered
550 * calculated. Make sure to use the value rounded
551 * to NTTIME granularity we've stored in the xattr.
553 btime = nt_time_to_full_timespec(dosattrib.info.info5.create_time);
554 update_stat_ex_create_time(&smb_fname->st, btime);
556 DBG_DEBUG("set EA 0x%" PRIx32 " on file %s\n",
557 dosmode,
558 smb_fname_str_dbg(smb_fname));
559 return NT_STATUS_OK;
562 static uint32_t
563 dos_mode_from_name(connection_struct *conn, const char *name, uint32_t dosmode)
565 const char *p = NULL;
566 uint32_t result = dosmode;
568 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
569 lp_hide_dot_files(SNUM(conn)))
571 p = strrchr_m(name, '/');
572 if (p) {
573 p++;
574 } else {
575 p = name;
578 /* Only . and .. are not hidden. */
579 if ((p[0] == '.') && !(ISDOT(p) || ISDOTDOT(p))) {
580 result |= FILE_ATTRIBUTE_HIDDEN;
584 if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, name)) {
585 result |= FILE_ATTRIBUTE_HIDDEN;
588 return result;
591 /****************************************************************************
592 Change a unix mode to a dos mode for an ms dfs link.
593 ****************************************************************************/
595 uint32_t dos_mode_msdfs(connection_struct *conn,
596 const char *name,
597 const struct stat_ex *st)
599 uint32_t result = 0;
601 DEBUG(8, ("dos_mode_msdfs: %s\n", name));
603 if (!VALID_STAT(*st)) {
604 return 0;
607 result = dos_mode_from_name(conn, name, result);
608 result |= dos_mode_from_sbuf(conn, st, NULL);
610 if (result == 0) {
611 result = FILE_ATTRIBUTE_NORMAL;
614 result = filter_mode_by_protocol(conn_protocol(conn->sconn), result);
617 * Add in that it is a reparse point
619 result |= FILE_ATTRIBUTE_REPARSE_POINT;
621 dos_mode_debug_print(__func__, result);
623 return(result);
627 * check whether a file or directory is flagged as compressed.
629 static NTSTATUS dos_mode_check_compressed(struct files_struct *fsp,
630 bool *is_compressed)
632 NTSTATUS status;
633 uint16_t compression_fmt;
635 status = SMB_VFS_FGET_COMPRESSION(
636 fsp->conn, talloc_tos(), fsp, &compression_fmt);
637 if (!NT_STATUS_IS_OK(status)) {
638 return status;
641 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
642 *is_compressed = true;
643 } else {
644 *is_compressed = false;
646 return NT_STATUS_OK;
649 static uint32_t dos_mode_post(uint32_t dosmode,
650 struct files_struct *fsp,
651 const char *func)
653 struct smb_filename *smb_fname = NULL;
654 NTSTATUS status;
656 if (fsp != NULL) {
657 smb_fname = fsp->fsp_name;
659 SMB_ASSERT(smb_fname != NULL);
662 * According to MS-FSA a stream name does not have
663 * separate DOS attribute metadata, so we must return
664 * the DOS attribute from the base filename. With one caveat,
665 * a non-default stream name can never be a directory.
667 * As this is common to all streams data stores, we handle
668 * it here instead of inside all stream VFS modules.
670 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
673 if (is_named_stream(smb_fname)) {
674 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
675 dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
678 if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
679 bool compressed = false;
681 status = dos_mode_check_compressed(fsp, &compressed);
682 if (NT_STATUS_IS_OK(status) && compressed) {
683 dosmode |= FILE_ATTRIBUTE_COMPRESSED;
687 dosmode |= dos_mode_from_name(fsp->conn, smb_fname->base_name, dosmode);
689 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
690 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
691 } else if (dosmode == 0) {
692 dosmode = FILE_ATTRIBUTE_NORMAL;
695 dosmode = filter_mode_by_protocol(conn_protocol(fsp->conn->sconn),
696 dosmode);
698 dos_mode_debug_print(func, dosmode);
699 return dosmode;
702 /****************************************************************************
703 Change a unix mode to a dos mode.
704 May also read the create timespec into the stat struct in smb_fname
705 if "store dos attributes" is true.
706 ****************************************************************************/
708 uint32_t fdos_mode(struct files_struct *fsp)
710 uint32_t result = 0;
711 NTSTATUS status = NT_STATUS_OK;
713 DBG_DEBUG("%s\n", fsp_str_dbg(fsp));
715 if (fsp->fake_file_handle != NULL) {
716 return dosmode_from_fake_filehandle(fsp->fake_file_handle);
719 if (!VALID_STAT(fsp->fsp_name->st)) {
720 return 0;
723 switch (fsp->fsp_name->st.st_ex_mode & S_IFMT) {
724 case S_IFLNK:
725 return FILE_ATTRIBUTE_NORMAL;
726 break;
727 case S_IFIFO:
728 case S_IFSOCK:
729 case S_IFBLK:
730 case S_IFCHR:
731 return FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_REPARSE_POINT;
732 break;
733 default:
734 break;
737 if (fsp->fsp_name->st.cached_dos_attributes != FILE_ATTRIBUTE_INVALID) {
738 return fsp->fsp_name->st.cached_dos_attributes;
741 /* Get the DOS attributes via the VFS if we can */
742 status = SMB_VFS_FGET_DOS_ATTRIBUTES(fsp->conn,
743 metadata_fsp(fsp),
744 &result);
746 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
747 result |= dos_mode_from_sbuf(fsp->conn,
748 &fsp->fsp_name->st,
749 fsp);
752 fsp->fsp_name->st.cached_dos_attributes = dos_mode_post(result, fsp, __func__);
753 return fsp->fsp_name->st.cached_dos_attributes;
756 struct dos_mode_at_state {
757 files_struct *dir_fsp;
758 struct smb_filename *smb_fname;
759 uint32_t dosmode;
762 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
764 struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
765 struct tevent_context *ev,
766 files_struct *dir_fsp,
767 struct smb_filename *smb_fname)
769 struct tevent_req *req = NULL;
770 struct dos_mode_at_state *state = NULL;
771 struct tevent_req *subreq = NULL;
773 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
775 req = tevent_req_create(mem_ctx, &state,
776 struct dos_mode_at_state);
777 if (req == NULL) {
778 return NULL;
781 *state = (struct dos_mode_at_state) {
782 .dir_fsp = dir_fsp,
783 .smb_fname = smb_fname,
786 if (!VALID_STAT(smb_fname->st)) {
787 tevent_req_done(req);
788 return tevent_req_post(req, ev);
791 if (smb_fname->fsp == NULL) {
792 if (ISDOTDOT(smb_fname->base_name)) {
794 * smb_fname->fsp is explicitly closed
795 * for ".." to prevent meta-data leakage.
797 state->dosmode = FILE_ATTRIBUTE_DIRECTORY;
798 } else {
800 * This is a symlink in POSIX context.
801 * FIXME ? Should we move to returning
802 * FILE_ATTRIBUTE_REPARSE_POINT here ?
804 state->dosmode = FILE_ATTRIBUTE_NORMAL;
806 tevent_req_done(req);
807 return tevent_req_post(req, ev);
810 subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
812 dir_fsp,
813 smb_fname);
814 if (tevent_req_nomem(subreq, req)) {
815 return tevent_req_post(req, ev);
817 tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
819 return req;
822 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
824 struct tevent_req *req =
825 tevent_req_callback_data(subreq,
826 struct tevent_req);
827 struct dos_mode_at_state *state =
828 tevent_req_data(req,
829 struct dos_mode_at_state);
830 struct vfs_aio_state aio_state;
831 NTSTATUS status;
832 bool ok;
835 * Make sure we run as the user again
837 ok = change_to_user_and_service_by_fsp(state->dir_fsp);
838 SMB_ASSERT(ok);
840 status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
841 &aio_state,
842 &state->dosmode);
843 TALLOC_FREE(subreq);
844 if (!NT_STATUS_IS_OK(status)) {
846 * Both the sync dos_mode() as well as the async
847 * dos_mode_at_[send|recv] have no real error return, the only
848 * unhandled error is when the stat info in smb_fname is not
849 * valid (cf the checks in dos_mode() and dos_mode_at_send().
851 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
852 * dos_mode_post() which also does the mapping of a last resort
853 * from S_IFMT(st_mode).
855 * Only if we get NT_STATUS_NOT_IMPLEMENTED or
856 * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
857 * fallback to sync processing.
859 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) &&
860 !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED))
863 * state->dosmode should still be 0, but reset
864 * it to be sure.
866 state->dosmode = 0;
867 status = NT_STATUS_OK;
870 if (NT_STATUS_IS_OK(status)) {
871 state->dosmode = dos_mode_post(state->dosmode,
872 state->smb_fname->fsp,
873 __func__);
874 tevent_req_done(req);
875 return;
879 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
882 state->dosmode = fdos_mode(state->smb_fname->fsp);
883 tevent_req_done(req);
884 return;
887 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
889 struct dos_mode_at_state *state =
890 tevent_req_data(req,
891 struct dos_mode_at_state);
892 NTSTATUS status;
894 if (tevent_req_is_nterror(req, &status)) {
895 tevent_req_received(req);
896 return status;
899 *dosmode = state->dosmode;
900 tevent_req_received(req);
901 return NT_STATUS_OK;
904 /*******************************************************************
905 chmod a file - but preserve some bits.
906 If "store dos attributes" is also set it will store the create time
907 from the stat struct in smb_fname (in NTTIME format) in the EA
908 attribute also.
909 ********************************************************************/
911 int file_set_dosmode(connection_struct *conn,
912 struct smb_filename *smb_fname,
913 uint32_t dosmode,
914 struct smb_filename *parent_dir,
915 bool newfile)
917 int mask=0;
918 mode_t tmp;
919 mode_t unixmode;
920 int ret = -1;
921 NTSTATUS status;
923 if (!CAN_WRITE(conn)) {
924 errno = EROFS;
925 return -1;
928 if (S_ISLNK(smb_fname->st.st_ex_mode)) {
929 /* A symlink in POSIX context, ignore */
930 return 0;
933 if ((S_ISDIR(smb_fname->st.st_ex_mode)) &&
934 (dosmode & FILE_ATTRIBUTE_TEMPORARY))
936 errno = EINVAL;
937 return -1;
940 dosmode &= SAMBA_ATTRIBUTES_MASK;
942 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
943 dosmode, smb_fname_str_dbg(smb_fname)));
945 if (smb_fname->fsp == NULL) {
946 errno = ENOENT;
947 return -1;
950 if (smb_fname->fsp->fsp_flags.posix_open &&
951 !lp_store_dos_attributes(SNUM(conn)))
953 return 0;
956 unixmode = smb_fname->st.st_ex_mode;
958 get_acl_group_bits(conn, smb_fname->fsp, &smb_fname->st.st_ex_mode);
960 if (S_ISDIR(smb_fname->st.st_ex_mode))
961 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
962 else
963 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
965 /* Store the DOS attributes in an EA by preference. */
966 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn,
967 metadata_fsp(smb_fname->fsp),
968 dosmode);
969 if (NT_STATUS_IS_OK(status)) {
970 smb_fname->st.cached_dos_attributes = dosmode;
971 ret = 0;
972 goto done;
976 * Only fall back to using UNIX modes if
977 * we get NOT_IMPLEMENTED.
979 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
980 errno = map_errno_from_nt_status(status);
981 return -1;
984 /* Fall back to UNIX modes. */
985 unixmode = unix_mode(
986 conn,
987 dosmode,
988 smb_fname,
989 parent_dir != NULL ? parent_dir->fsp : NULL);
991 /* preserve the file type bits */
992 mask |= S_IFMT;
994 /* preserve the s bits */
995 mask |= (S_ISUID | S_ISGID);
997 /* preserve the t bit */
998 #ifdef S_ISVTX
999 mask |= S_ISVTX;
1000 #endif
1002 /* possibly preserve the x bits */
1003 if (!MAP_ARCHIVE(conn))
1004 mask |= S_IXUSR;
1005 if (!MAP_SYSTEM(conn))
1006 mask |= S_IXGRP;
1007 if (!MAP_HIDDEN(conn))
1008 mask |= S_IXOTH;
1010 unixmode |= (smb_fname->st.st_ex_mode & mask);
1012 /* if we previously had any r bits set then leave them alone */
1013 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
1014 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
1015 unixmode |= tmp;
1018 /* if we previously had any w bits set then leave them alone
1019 whilst adding in the new w bits, if the new mode is not rdonly */
1020 if (!(dosmode & FILE_ATTRIBUTE_READONLY)) {
1021 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
1025 * From the chmod 2 man page:
1027 * "If the calling process is not privileged, and the group of the file
1028 * does not match the effective group ID of the process or one of its
1029 * supplementary group IDs, the S_ISGID bit will be turned off, but
1030 * this will not cause an error to be returned."
1032 * Simply refuse to do the chmod in this case.
1035 if (S_ISDIR(smb_fname->st.st_ex_mode) &&
1036 (unixmode & S_ISGID) &&
1037 geteuid() != sec_initial_uid() &&
1038 !current_user_in_group(conn, smb_fname->st.st_ex_gid))
1040 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1041 "set for directory %s\n",
1042 smb_fname_str_dbg(smb_fname)));
1043 errno = EPERM;
1044 return -1;
1047 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1048 if (ret == 0) {
1049 goto done;
1052 if((errno != EPERM) && (errno != EACCES))
1053 return -1;
1055 if(!lp_dos_filemode(SNUM(conn)))
1056 return -1;
1058 /* We want DOS semantics, ie allow non owner with write permission to change the
1059 bits on a file. Just like file_ntimes below.
1062 if (!can_write_to_fsp(smb_fname->fsp))
1064 errno = EACCES;
1065 return -1;
1068 become_root();
1069 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1070 unbecome_root();
1072 done:
1073 if (!newfile) {
1074 notify_fname(conn,
1075 NOTIFY_ACTION_MODIFIED |
1076 NOTIFY_ACTION_DIRLEASE_BREAK,
1077 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1078 smb_fname,
1079 fsp_get_smb2_lease(smb_fname->fsp));
1081 if (ret == 0) {
1082 smb_fname->st.st_ex_mode = unixmode;
1085 return( ret );
1089 NTSTATUS file_set_sparse(connection_struct *conn,
1090 files_struct *fsp,
1091 bool sparse)
1093 const struct loadparm_substitution *lp_sub =
1094 loadparm_s3_global_substitution();
1095 uint32_t old_dosmode;
1096 uint32_t new_dosmode;
1097 NTSTATUS status;
1099 if (!CAN_WRITE(conn)) {
1100 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1101 "on readonly share[%s]\n",
1102 smb_fname_str_dbg(fsp->fsp_name),
1103 sparse,
1104 lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
1105 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1109 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1110 * following access flags are granted.
1112 status = check_any_access_fsp(fsp,
1113 FILE_WRITE_DATA
1114 | FILE_WRITE_ATTRIBUTES
1115 | SEC_FILE_APPEND_DATA);
1116 if (!NT_STATUS_IS_OK(status)) {
1117 DBG_DEBUG("fname[%s] set[%u] "
1118 "access_mask[0x%08X] - access denied\n",
1119 smb_fname_str_dbg(fsp->fsp_name),
1120 sparse,
1121 fsp->access_mask);
1122 return status;
1125 if (fsp->fsp_flags.is_directory) {
1126 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1127 (sparse ? "set" : "clear"),
1128 smb_fname_str_dbg(fsp->fsp_name)));
1129 return NT_STATUS_INVALID_PARAMETER;
1132 if (IS_IPC(conn) || IS_PRINT(conn)) {
1133 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1134 (sparse ? "set" : "clear")));
1135 return NT_STATUS_INVALID_PARAMETER;
1138 if (fsp_is_alternate_stream(fsp)) {
1140 * MS-FSA 2.1.1.5 IsSparse
1142 * This is a per stream attribute, but our backends don't
1143 * support it a consistent way, therefore just pretend
1144 * success and ignore the request.
1146 DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
1147 "[%s]\n", fsp_str_dbg(fsp));
1148 return NT_STATUS_OK;
1151 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1152 sparse, smb_fname_str_dbg(fsp->fsp_name)));
1154 if (!lp_store_dos_attributes(SNUM(conn))) {
1155 return NT_STATUS_INVALID_DEVICE_REQUEST;
1158 status = vfs_stat_fsp(fsp);
1159 if (!NT_STATUS_IS_OK(status)) {
1160 return status;
1163 old_dosmode = fdos_mode(fsp);
1165 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1166 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1167 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1168 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1169 } else {
1170 return NT_STATUS_OK;
1173 /* Store the DOS attributes in an EA. */
1174 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1175 if (!NT_STATUS_IS_OK(status)) {
1176 return status;
1179 notify_fname(conn,
1180 NOTIFY_ACTION_MODIFIED |
1181 NOTIFY_ACTION_DIRLEASE_BREAK,
1182 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1183 fsp->fsp_name,
1184 fsp_get_smb2_lease(fsp));
1186 fsp->fsp_name->st.cached_dos_attributes = new_dosmode;
1187 fsp->fsp_flags.is_sparse = sparse;
1189 return NT_STATUS_OK;
1192 /*******************************************************************
1193 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1194 than POSIX.
1195 *******************************************************************/
1197 int file_ntimes(connection_struct *conn,
1198 files_struct *fsp,
1199 struct smb_file_time *ft)
1201 int ret = -1;
1203 errno = 0;
1205 DBG_INFO("actime: %s",
1206 time_to_asc(convert_timespec_to_time_t(ft->atime)));
1207 DBG_INFO("modtime: %s",
1208 time_to_asc(convert_timespec_to_time_t(ft->mtime)));
1209 DBG_INFO("ctime: %s",
1210 time_to_asc(convert_timespec_to_time_t(ft->ctime)));
1211 DBG_INFO("createtime: %s",
1212 time_to_asc(convert_timespec_to_time_t(ft->create_time)));
1214 /* Don't update the time on read-only shares */
1215 /* We need this as set_filetime (which can be called on
1216 close and other paths) can end up calling this function
1217 without the NEED_WRITE protection. Found by :
1218 Leo Weppelman <leo@wau.mis.ah.nl>
1221 if (!CAN_WRITE(conn)) {
1222 return 0;
1225 if (SMB_VFS_FNTIMES(fsp, ft) == 0) {
1226 ret = 0;
1227 goto done;
1230 if((errno != EPERM) && (errno != EACCES)) {
1231 return -1;
1234 if(!lp_dos_filetimes(SNUM(conn))) {
1235 return -1;
1238 /* We have permission (given by the Samba admin) to
1239 break POSIX semantics and allow a user to change
1240 the time on a file they don't own but can write to
1241 (as DOS does).
1244 /* Check if we have write access. */
1245 if (can_write_to_fsp(fsp)) {
1246 /* We are allowed to become root and change the filetime. */
1247 become_root();
1248 ret = SMB_VFS_FNTIMES(fsp, ft);
1249 unbecome_root();
1252 done:
1253 if (ret == 0) {
1254 copy_stat_ex_timestamps(&fsp->fsp_name->st, ft);
1257 return ret;
1260 /******************************************************************
1261 Force a "sticky" write time on an fsp. This will always be
1262 returned on all future write time queries and set on close.
1263 ******************************************************************/
1265 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1267 bool ok;
1269 if (is_omit_timespec(&mtime)) {
1270 return true;
1273 fsp->fsp_flags.write_time_forced = true;
1274 TALLOC_FREE(fsp->update_write_time_event);
1276 ok = set_sticky_write_time(fsp->file_id, mtime);
1277 return ok;
1280 /******************************************************************
1281 Set a create time EA.
1282 ******************************************************************/
1284 NTSTATUS set_create_timespec_ea(struct files_struct *fsp,
1285 struct timespec create_time)
1287 uint32_t dosmode;
1288 int ret;
1290 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
1291 return NT_STATUS_OK;
1294 dosmode = fdos_mode(fsp);
1296 fsp->fsp_name->st.st_ex_btime = create_time;
1297 ret = file_set_dosmode(fsp->conn, fsp->fsp_name, dosmode, NULL, false);
1298 if (ret == -1) {
1299 return map_nt_error_from_unix(errno);
1302 DBG_DEBUG("wrote create time EA for file %s\n",
1303 smb_fname_str_dbg(fsp->fsp_name));
1305 return NT_STATUS_OK;
1308 /******************************************************************
1309 Return a create time.
1310 ******************************************************************/
1312 struct timespec get_create_timespec(connection_struct *conn,
1313 struct files_struct *fsp,
1314 const struct smb_filename *smb_fname)
1316 if (fsp != NULL) {
1317 struct files_struct *meta_fsp = metadata_fsp(fsp);
1318 return meta_fsp->fsp_name->st.st_ex_btime;
1320 return smb_fname->st.st_ex_btime;
1323 /******************************************************************
1324 Return a change time (may look at EA in future).
1325 ******************************************************************/
1327 struct timespec get_change_timespec(connection_struct *conn,
1328 struct files_struct *fsp,
1329 const struct smb_filename *smb_fname)
1331 return smb_fname->st.st_ex_mtime;