smbd: Print reparse_point in dos_mode_debug_print
[samba4-gss.git] / source3 / smbd / dosmode.c
blobdcb76012dad5cef99c00b2cda2600ab45afab947
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%x): \"%s\"\n", func, (unsigned)mode,
72 modestr);
75 static uint32_t filter_mode_by_protocol(enum protocol_types protocol,
76 uint32_t mode)
78 if (protocol <= PROTOCOL_LANMAN2) {
79 DEBUG(10,("filter_mode_by_protocol: "
80 "filtering result 0x%x to 0x%x\n",
81 (unsigned int)mode,
82 (unsigned int)(mode & 0x3f) ));
83 mode &= 0x3f;
85 return mode;
88 /****************************************************************************
89 Change a dos mode to a unix mode.
90 Base permission for files:
91 if creating file and inheriting (i.e. parent_dir != NULL)
92 apply read/write bits from parent directory.
93 else
94 everybody gets read bit set
95 dos readonly is represented in unix by removing everyone's write bit
96 dos archive is represented in unix by the user's execute bit
97 dos system is represented in unix by the group's execute bit
98 dos hidden is represented in unix by the other's execute bit
99 if !inheriting {
100 Then apply create mask,
101 then add force bits.
103 Base permission for directories:
104 dos directory is represented in unix by unix's dir bit and the exec bit
105 if !inheriting {
106 Then apply create mask,
107 then add force bits.
109 ****************************************************************************/
111 mode_t unix_mode(connection_struct *conn, int dosmode,
112 const struct smb_filename *smb_fname,
113 struct files_struct *parent_dirfsp)
115 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
116 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
117 * inheriting. */
119 if ((dosmode & FILE_ATTRIBUTE_READONLY) &&
120 !lp_store_dos_attributes(SNUM(conn))) {
121 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
124 if ((parent_dirfsp != NULL) && lp_inherit_permissions(SNUM(conn))) {
125 struct stat_ex sbuf = { .st_ex_nlink = 0, };
126 int ret;
128 DBG_DEBUG("[%s] inheriting from [%s]\n",
129 smb_fname_str_dbg(smb_fname),
130 smb_fname_str_dbg(parent_dirfsp->fsp_name));
132 ret = SMB_VFS_FSTAT(parent_dirfsp, &sbuf);
133 if (ret != 0) {
134 DBG_ERR("fstat failed [%s]: %s\n",
135 smb_fname_str_dbg(parent_dirfsp->fsp_name),
136 strerror(errno));
137 return(0); /* *** shouldn't happen! *** */
140 /* Save for later - but explicitly remove setuid bit for safety. */
141 dir_mode = sbuf.st_ex_mode & ~S_ISUID;
142 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
143 smb_fname_str_dbg(smb_fname), (int)dir_mode));
144 /* Clear "result" */
145 result = 0;
148 if (dosmode & FILE_ATTRIBUTE_DIRECTORY) {
149 /* We never make directories read only for the owner as under DOS a user
150 can always create a file in a read-only directory. */
151 result |= (S_IFDIR | S_IWUSR);
153 if (dir_mode) {
154 /* Inherit mode of parent directory. */
155 result |= dir_mode;
156 } else {
157 /* Provisionally add all 'x' bits */
158 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
160 /* Apply directory mask */
161 result &= lp_directory_mask(SNUM(conn));
162 /* Add in force bits */
163 result |= lp_force_directory_mode(SNUM(conn));
165 } else {
166 if ((dosmode & FILE_ATTRIBUTE_ARCHIVE) &&
167 lp_map_archive(SNUM(conn))) {
168 result |= S_IXUSR;
171 if ((dosmode & FILE_ATTRIBUTE_SYSTEM) &&
172 lp_map_system(SNUM(conn))) {
173 result |= S_IXGRP;
176 if ((dosmode & FILE_ATTRIBUTE_HIDDEN) &&
177 lp_map_hidden(SNUM(conn))) {
178 result |= S_IXOTH;
181 if (dir_mode) {
182 /* Inherit 666 component of parent directory mode */
183 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
184 } else {
185 /* Apply mode mask */
186 result &= lp_create_mask(SNUM(conn));
187 /* Add in force bits */
188 result |= lp_force_create_mode(SNUM(conn));
192 DBG_INFO("unix_mode(%s) returning 0%o\n",
193 smb_fname_str_dbg(smb_fname), (int)result);
195 return(result);
198 /****************************************************************************
199 Change a unix mode to a dos mode.
200 ****************************************************************************/
202 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
203 const struct stat_ex *st,
204 struct files_struct *fsp)
206 int result = 0;
207 enum mapreadonly_options ro_opts =
208 (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
210 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
211 /* if we can find out if a file is immutable we should report it r/o */
212 if (st->st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
213 result |= FILE_ATTRIBUTE_READONLY;
215 #endif
216 if (ro_opts == MAP_READONLY_YES) {
217 /* Original Samba method - map inverse of user "w" bit. */
218 if ((st->st_ex_mode & S_IWUSR) == 0) {
219 result |= FILE_ATTRIBUTE_READONLY;
222 if (ro_opts == MAP_READONLY_PERMISSIONS) {
223 /* smb_fname->fsp can be NULL for an MS-DFS link. */
224 /* Check actual permissions for read-only. */
225 if ((fsp != NULL) && !can_write_to_fsp(fsp)) {
226 result |= FILE_ATTRIBUTE_READONLY;
230 if (MAP_ARCHIVE(conn) && ((st->st_ex_mode & S_IXUSR) != 0)) {
231 result |= FILE_ATTRIBUTE_ARCHIVE;
234 if (MAP_SYSTEM(conn) && ((st->st_ex_mode & S_IXGRP) != 0)) {
235 result |= FILE_ATTRIBUTE_SYSTEM;
238 if (MAP_HIDDEN(conn) && ((st->st_ex_mode & S_IXOTH) != 0)) {
239 result |= FILE_ATTRIBUTE_HIDDEN;
242 if (S_ISDIR(st->st_ex_mode)) {
243 result = FILE_ATTRIBUTE_DIRECTORY |
244 (result & FILE_ATTRIBUTE_READONLY);
247 dos_mode_debug_print(__func__, result);
249 return result;
252 /****************************************************************************
253 Get DOS attributes from an EA.
254 This can also pull the create time into the stat struct inside smb_fname.
255 ****************************************************************************/
257 NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
258 DATA_BLOB blob,
259 uint32_t *pattr)
261 struct xattr_DOSATTRIB dosattrib;
262 enum ndr_err_code ndr_err;
263 uint32_t dosattr;
265 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
266 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
268 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
269 DBG_WARNING("bad ndr decode "
270 "from EA on file %s: Error = %s\n",
271 smb_fname_str_dbg(smb_fname),
272 ndr_errstr(ndr_err));
273 return ndr_map_error2ntstatus(ndr_err);
276 DBG_DEBUG("%s attr = %s\n",
277 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex);
279 switch (dosattrib.version) {
280 case 0xFFFF:
281 dosattr = dosattrib.info.compatinfoFFFF.attrib;
282 break;
283 case 1:
284 dosattr = dosattrib.info.info1.attrib;
285 if (!null_nttime(dosattrib.info.info1.create_time)) {
286 struct timespec create_time =
287 nt_time_to_unix_timespec(
288 dosattrib.info.info1.create_time);
290 update_stat_ex_create_time(&smb_fname->st,
291 create_time);
293 DBG_DEBUG("file %s case 1 set btime %s",
294 smb_fname_str_dbg(smb_fname),
295 time_to_asc(convert_timespec_to_time_t(
296 create_time)));
298 break;
299 case 2:
300 dosattr = dosattrib.info.oldinfo2.attrib;
301 /* Don't know what flags to check for this case. */
302 break;
303 case 3:
304 dosattr = dosattrib.info.info3.attrib;
305 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
306 !null_nttime(dosattrib.info.info3.create_time)) {
307 struct timespec create_time =
308 nt_time_to_full_timespec(
309 dosattrib.info.info3.create_time);
311 update_stat_ex_create_time(&smb_fname->st,
312 create_time);
314 DBG_DEBUG("file %s case 3 set btime %s",
315 smb_fname_str_dbg(smb_fname),
316 time_to_asc(convert_timespec_to_time_t(
317 create_time)));
319 break;
320 case 4:
321 case 5:
323 uint32_t info_valid_flags;
324 NTTIME info_create_time;
326 if (dosattrib.version == 4) {
327 info_valid_flags = dosattrib.info.info4.valid_flags;
328 info_create_time = dosattrib.info.info4.create_time;
329 dosattr = dosattrib.info.info4.attrib;
330 } else {
331 info_valid_flags = dosattrib.info.info5.valid_flags;
332 info_create_time = dosattrib.info.info5.create_time;
333 dosattr = dosattrib.info.info5.attrib;
336 if ((info_valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
337 !null_nttime(info_create_time))
339 struct timespec creat_time;
341 creat_time = nt_time_to_full_timespec(info_create_time);
342 update_stat_ex_create_time(&smb_fname->st, creat_time);
344 DBG_DEBUG("file [%s] creation time [%s]\n",
345 smb_fname_str_dbg(smb_fname),
346 nt_time_string(talloc_tos(), info_create_time));
349 break;
351 default:
352 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
353 smb_fname_str_dbg(smb_fname), blob.data);
354 /* Should this be INTERNAL_ERROR? */
355 return NT_STATUS_INVALID_PARAMETER;
358 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
359 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
363 * _SPARSE and _REPARSE_POINT are valid on get but not on
364 * set. Both are created via special fcntls.
367 dosattr &= (SAMBA_ATTRIBUTES_MASK|
368 FILE_ATTRIBUTE_SPARSE|
369 FILE_ATTRIBUTE_REPARSE_POINT);
371 *pattr |= dosattr;
373 dos_mode_debug_print(__func__, *pattr);
375 return NT_STATUS_OK;
378 NTSTATUS fget_ea_dos_attribute(struct files_struct *fsp,
379 uint32_t *pattr)
381 DATA_BLOB blob;
382 ssize_t sizeret;
383 fstring attrstr;
384 NTSTATUS status;
386 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
387 return NT_STATUS_NOT_IMPLEMENTED;
390 /* Don't reset pattr to zero as we may already have filename-based attributes we
391 need to preserve. */
393 sizeret = SMB_VFS_FGETXATTR(fsp,
394 SAMBA_XATTR_DOS_ATTRIB,
395 attrstr,
396 sizeof(attrstr));
397 if (sizeret == -1 && ( errno == EPERM || errno == EACCES )) {
398 /* we may also retrieve dos attribs for unreadable files, this
399 is why we'll retry as root. We don't use root in the first
400 run because in cases like NFS, root might have even less
401 rights than the real user
403 become_root();
404 sizeret = SMB_VFS_FGETXATTR(fsp,
405 SAMBA_XATTR_DOS_ATTRIB,
406 attrstr,
407 sizeof(attrstr));
408 unbecome_root();
410 if (sizeret == -1) {
411 DBG_INFO("Cannot get attribute "
412 "from EA on file %s: Error = %s\n",
413 fsp_str_dbg(fsp), strerror(errno));
414 return map_nt_error_from_unix(errno);
417 blob.data = (uint8_t *)attrstr;
418 blob.length = sizeret;
420 status = parse_dos_attribute_blob(fsp->fsp_name, blob, pattr);
421 if (!NT_STATUS_IS_OK(status)) {
422 return status;
425 return NT_STATUS_OK;
428 /****************************************************************************
429 Set DOS attributes in an EA.
430 Also sets the create time.
431 ****************************************************************************/
433 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
434 struct smb_filename *smb_fname,
435 uint32_t dosmode)
437 struct xattr_DOSATTRIB dosattrib = { .version = 0, };
438 enum ndr_err_code ndr_err;
439 DATA_BLOB blob = { .data = NULL, };
440 struct timespec btime;
441 int ret;
443 if (!lp_store_dos_attributes(SNUM(conn))) {
444 return NT_STATUS_NOT_IMPLEMENTED;
447 if (smb_fname->fsp == NULL) {
448 /* symlink */
449 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
452 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
453 * vfs_default via DMAPI if that is enabled.
455 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
457 dosattrib.version = 5;
458 dosattrib.info.info5.valid_flags = XATTR_DOSINFO_ATTRIB |
459 XATTR_DOSINFO_CREATE_TIME;
460 dosattrib.info.info5.attrib = dosmode;
461 dosattrib.info.info5.create_time = full_timespec_to_nt_time(
462 &smb_fname->st.st_ex_btime);
464 DBG_DEBUG("set attribute 0x%" PRIx32 ", btime = %s on file %s\n",
465 dosmode,
466 time_to_asc(convert_timespec_to_time_t(
467 smb_fname->st.st_ex_btime)),
468 smb_fname_str_dbg(smb_fname));
470 ndr_err = ndr_push_struct_blob(
471 &blob, talloc_tos(), &dosattrib,
472 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
474 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
475 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
476 ndr_errstr(ndr_err)));
477 return ndr_map_error2ntstatus(ndr_err);
480 if (blob.data == NULL || blob.length == 0) {
481 /* Should this be INTERNAL_ERROR? */
482 return NT_STATUS_INVALID_PARAMETER;
485 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
486 SAMBA_XATTR_DOS_ATTRIB,
487 blob.data, blob.length, 0);
488 if (ret != 0) {
489 NTSTATUS status = NT_STATUS_OK;
490 bool set_dosmode_ok = false;
492 if ((errno != EPERM) && (errno != EACCES)) {
493 DBG_INFO("Cannot set "
494 "attribute EA on file %s: Error = %s\n",
495 smb_fname_str_dbg(smb_fname), strerror(errno));
496 return map_nt_error_from_unix(errno);
499 /* We want DOS semantics, ie allow non owner with write permission to change the
500 bits on a file. Just like file_ntimes below.
503 /* Check if we have write access. */
504 if (!CAN_WRITE(conn)) {
505 return NT_STATUS_ACCESS_DENIED;
508 status = smbd_check_access_rights_fsp(conn->cwd_fsp,
509 smb_fname->fsp,
510 false,
511 FILE_WRITE_ATTRIBUTES);
512 if (NT_STATUS_IS_OK(status)) {
513 set_dosmode_ok = true;
516 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
517 set_dosmode_ok = can_write_to_fsp(smb_fname->fsp);
520 if (!set_dosmode_ok) {
521 return NT_STATUS_ACCESS_DENIED;
524 become_root();
525 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
526 SAMBA_XATTR_DOS_ATTRIB,
527 blob.data, blob.length, 0);
528 if (ret == 0) {
529 status = NT_STATUS_OK;
531 unbecome_root();
532 if (!NT_STATUS_IS_OK(status)) {
533 return status;
538 * We correctly stored the create time.
539 * We *always* set XATTR_DOSINFO_CREATE_TIME,
540 * so now it can no longer be considered
541 * calculated. Make sure to use the value rounded
542 * to NTTIME granularity we've stored in the xattr.
544 btime = nt_time_to_full_timespec(dosattrib.info.info5.create_time);
545 update_stat_ex_create_time(&smb_fname->st, btime);
547 DBG_DEBUG("set EA 0x%" PRIx32 " on file %s\n",
548 dosmode,
549 smb_fname_str_dbg(smb_fname));
550 return NT_STATUS_OK;
553 static uint32_t
554 dos_mode_from_name(connection_struct *conn, const char *name, uint32_t dosmode)
556 const char *p = NULL;
557 uint32_t result = dosmode;
559 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
560 lp_hide_dot_files(SNUM(conn)))
562 p = strrchr_m(name, '/');
563 if (p) {
564 p++;
565 } else {
566 p = name;
569 /* Only . and .. are not hidden. */
570 if ((p[0] == '.') && !(ISDOT(p) || ISDOTDOT(p))) {
571 result |= FILE_ATTRIBUTE_HIDDEN;
575 if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, name)) {
576 result |= FILE_ATTRIBUTE_HIDDEN;
579 return result;
582 /****************************************************************************
583 Change a unix mode to a dos mode for an ms dfs link.
584 ****************************************************************************/
586 uint32_t dos_mode_msdfs(connection_struct *conn,
587 const char *name,
588 const struct stat_ex *st)
590 uint32_t result = 0;
592 DEBUG(8, ("dos_mode_msdfs: %s\n", name));
594 if (!VALID_STAT(*st)) {
595 return 0;
598 result = dos_mode_from_name(conn, name, result);
599 result |= dos_mode_from_sbuf(conn, st, NULL);
601 if (result == 0) {
602 result = FILE_ATTRIBUTE_NORMAL;
605 result = filter_mode_by_protocol(conn_protocol(conn->sconn), result);
608 * Add in that it is a reparse point
610 result |= FILE_ATTRIBUTE_REPARSE_POINT;
612 dos_mode_debug_print(__func__, result);
614 return(result);
618 * check whether a file or directory is flagged as compressed.
620 static NTSTATUS dos_mode_check_compressed(struct files_struct *fsp,
621 bool *is_compressed)
623 NTSTATUS status;
624 uint16_t compression_fmt;
626 status = SMB_VFS_FGET_COMPRESSION(
627 fsp->conn, talloc_tos(), fsp, &compression_fmt);
628 if (!NT_STATUS_IS_OK(status)) {
629 return status;
632 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
633 *is_compressed = true;
634 } else {
635 *is_compressed = false;
637 return NT_STATUS_OK;
640 static uint32_t dos_mode_post(uint32_t dosmode,
641 struct files_struct *fsp,
642 const char *func)
644 struct smb_filename *smb_fname = NULL;
645 NTSTATUS status;
647 if (fsp != NULL) {
648 smb_fname = fsp->fsp_name;
650 SMB_ASSERT(smb_fname != NULL);
653 * According to MS-FSA a stream name does not have
654 * separate DOS attribute metadata, so we must return
655 * the DOS attribute from the base filename. With one caveat,
656 * a non-default stream name can never be a directory.
658 * As this is common to all streams data stores, we handle
659 * it here instead of inside all stream VFS modules.
661 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
664 if (is_named_stream(smb_fname)) {
665 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
666 dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
669 if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
670 bool compressed = false;
672 status = dos_mode_check_compressed(fsp, &compressed);
673 if (NT_STATUS_IS_OK(status) && compressed) {
674 dosmode |= FILE_ATTRIBUTE_COMPRESSED;
678 dosmode |= dos_mode_from_name(fsp->conn, smb_fname->base_name, dosmode);
680 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
681 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
682 } else if (dosmode == 0) {
683 dosmode = FILE_ATTRIBUTE_NORMAL;
686 dosmode = filter_mode_by_protocol(conn_protocol(fsp->conn->sconn),
687 dosmode);
689 dos_mode_debug_print(func, dosmode);
690 return dosmode;
693 /****************************************************************************
694 Change a unix mode to a dos mode.
695 May also read the create timespec into the stat struct in smb_fname
696 if "store dos attributes" is true.
697 ****************************************************************************/
699 uint32_t fdos_mode(struct files_struct *fsp)
701 uint32_t result = 0;
702 NTSTATUS status = NT_STATUS_OK;
704 DBG_DEBUG("%s\n", fsp_str_dbg(fsp));
706 if (fsp->fake_file_handle != NULL) {
707 return dosmode_from_fake_filehandle(fsp->fake_file_handle);
710 if (!VALID_STAT(fsp->fsp_name->st)) {
711 return 0;
714 if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) {
715 return FILE_ATTRIBUTE_NORMAL;
718 if (fsp->fsp_name->st.cached_dos_attributes != FILE_ATTRIBUTE_INVALID) {
719 return fsp->fsp_name->st.cached_dos_attributes;
722 /* Get the DOS attributes via the VFS if we can */
723 status = SMB_VFS_FGET_DOS_ATTRIBUTES(fsp->conn,
724 metadata_fsp(fsp),
725 &result);
727 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
728 result |= dos_mode_from_sbuf(fsp->conn,
729 &fsp->fsp_name->st,
730 fsp);
733 fsp->fsp_name->st.cached_dos_attributes = dos_mode_post(result, fsp, __func__);
734 return fsp->fsp_name->st.cached_dos_attributes;
737 struct dos_mode_at_state {
738 files_struct *dir_fsp;
739 struct smb_filename *smb_fname;
740 uint32_t dosmode;
743 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
745 struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
746 struct tevent_context *ev,
747 files_struct *dir_fsp,
748 struct smb_filename *smb_fname)
750 struct tevent_req *req = NULL;
751 struct dos_mode_at_state *state = NULL;
752 struct tevent_req *subreq = NULL;
754 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
756 req = tevent_req_create(mem_ctx, &state,
757 struct dos_mode_at_state);
758 if (req == NULL) {
759 return NULL;
762 *state = (struct dos_mode_at_state) {
763 .dir_fsp = dir_fsp,
764 .smb_fname = smb_fname,
767 if (!VALID_STAT(smb_fname->st)) {
768 tevent_req_done(req);
769 return tevent_req_post(req, ev);
772 if (smb_fname->fsp == NULL) {
773 if (ISDOTDOT(smb_fname->base_name)) {
775 * smb_fname->fsp is explicitly closed
776 * for ".." to prevent meta-data leakage.
778 state->dosmode = FILE_ATTRIBUTE_DIRECTORY;
779 } else {
781 * This is a symlink in POSIX context.
782 * FIXME ? Should we move to returning
783 * FILE_ATTRIBUTE_REPARSE_POINT here ?
785 state->dosmode = FILE_ATTRIBUTE_NORMAL;
787 tevent_req_done(req);
788 return tevent_req_post(req, ev);
791 subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
793 dir_fsp,
794 smb_fname);
795 if (tevent_req_nomem(subreq, req)) {
796 return tevent_req_post(req, ev);
798 tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
800 return req;
803 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
805 struct tevent_req *req =
806 tevent_req_callback_data(subreq,
807 struct tevent_req);
808 struct dos_mode_at_state *state =
809 tevent_req_data(req,
810 struct dos_mode_at_state);
811 struct vfs_aio_state aio_state;
812 NTSTATUS status;
813 bool ok;
816 * Make sure we run as the user again
818 ok = change_to_user_and_service_by_fsp(state->dir_fsp);
819 SMB_ASSERT(ok);
821 status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
822 &aio_state,
823 &state->dosmode);
824 TALLOC_FREE(subreq);
825 if (!NT_STATUS_IS_OK(status)) {
827 * Both the sync dos_mode() as well as the async
828 * dos_mode_at_[send|recv] have no real error return, the only
829 * unhandled error is when the stat info in smb_fname is not
830 * valid (cf the checks in dos_mode() and dos_mode_at_send().
832 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
833 * dos_mode_post() which also does the mapping of a last resort
834 * from S_IFMT(st_mode).
836 * Only if we get NT_STATUS_NOT_IMPLEMENTED or
837 * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
838 * fallback to sync processing.
840 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) &&
841 !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED))
844 * state->dosmode should still be 0, but reset
845 * it to be sure.
847 state->dosmode = 0;
848 status = NT_STATUS_OK;
851 if (NT_STATUS_IS_OK(status)) {
852 state->dosmode = dos_mode_post(state->dosmode,
853 state->smb_fname->fsp,
854 __func__);
855 tevent_req_done(req);
856 return;
860 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
863 state->dosmode = fdos_mode(state->smb_fname->fsp);
864 tevent_req_done(req);
865 return;
868 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
870 struct dos_mode_at_state *state =
871 tevent_req_data(req,
872 struct dos_mode_at_state);
873 NTSTATUS status;
875 if (tevent_req_is_nterror(req, &status)) {
876 tevent_req_received(req);
877 return status;
880 *dosmode = state->dosmode;
881 tevent_req_received(req);
882 return NT_STATUS_OK;
885 /*******************************************************************
886 chmod a file - but preserve some bits.
887 If "store dos attributes" is also set it will store the create time
888 from the stat struct in smb_fname (in NTTIME format) in the EA
889 attribute also.
890 ********************************************************************/
892 int file_set_dosmode(connection_struct *conn,
893 struct smb_filename *smb_fname,
894 uint32_t dosmode,
895 struct smb_filename *parent_dir,
896 bool newfile)
898 int mask=0;
899 mode_t tmp;
900 mode_t unixmode;
901 int ret = -1;
902 NTSTATUS status;
904 if (!CAN_WRITE(conn)) {
905 errno = EROFS;
906 return -1;
909 if (S_ISLNK(smb_fname->st.st_ex_mode)) {
910 /* A symlink in POSIX context, ignore */
911 return 0;
914 if ((S_ISDIR(smb_fname->st.st_ex_mode)) &&
915 (dosmode & FILE_ATTRIBUTE_TEMPORARY))
917 errno = EINVAL;
918 return -1;
921 dosmode &= SAMBA_ATTRIBUTES_MASK;
923 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
924 dosmode, smb_fname_str_dbg(smb_fname)));
926 if (smb_fname->fsp == NULL) {
927 errno = ENOENT;
928 return -1;
931 if ((smb_fname->fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) &&
932 !lp_store_dos_attributes(SNUM(conn)))
934 return 0;
937 unixmode = smb_fname->st.st_ex_mode;
939 get_acl_group_bits(conn, smb_fname->fsp, &smb_fname->st.st_ex_mode);
941 if (S_ISDIR(smb_fname->st.st_ex_mode))
942 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
943 else
944 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
946 /* Store the DOS attributes in an EA by preference. */
947 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn,
948 metadata_fsp(smb_fname->fsp),
949 dosmode);
950 if (NT_STATUS_IS_OK(status)) {
951 smb_fname->st.cached_dos_attributes = dosmode;
952 ret = 0;
953 goto done;
957 * Only fall back to using UNIX modes if
958 * we get NOT_IMPLEMENTED.
960 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
961 errno = map_errno_from_nt_status(status);
962 return -1;
965 /* Fall back to UNIX modes. */
966 unixmode = unix_mode(
967 conn,
968 dosmode,
969 smb_fname,
970 parent_dir != NULL ? parent_dir->fsp : NULL);
972 /* preserve the file type bits */
973 mask |= S_IFMT;
975 /* preserve the s bits */
976 mask |= (S_ISUID | S_ISGID);
978 /* preserve the t bit */
979 #ifdef S_ISVTX
980 mask |= S_ISVTX;
981 #endif
983 /* possibly preserve the x bits */
984 if (!MAP_ARCHIVE(conn))
985 mask |= S_IXUSR;
986 if (!MAP_SYSTEM(conn))
987 mask |= S_IXGRP;
988 if (!MAP_HIDDEN(conn))
989 mask |= S_IXOTH;
991 unixmode |= (smb_fname->st.st_ex_mode & mask);
993 /* if we previously had any r bits set then leave them alone */
994 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
995 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
996 unixmode |= tmp;
999 /* if we previously had any w bits set then leave them alone
1000 whilst adding in the new w bits, if the new mode is not rdonly */
1001 if (!(dosmode & FILE_ATTRIBUTE_READONLY)) {
1002 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
1006 * From the chmod 2 man page:
1008 * "If the calling process is not privileged, and the group of the file
1009 * does not match the effective group ID of the process or one of its
1010 * supplementary group IDs, the S_ISGID bit will be turned off, but
1011 * this will not cause an error to be returned."
1013 * Simply refuse to do the chmod in this case.
1016 if (S_ISDIR(smb_fname->st.st_ex_mode) &&
1017 (unixmode & S_ISGID) &&
1018 geteuid() != sec_initial_uid() &&
1019 !current_user_in_group(conn, smb_fname->st.st_ex_gid))
1021 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1022 "set for directory %s\n",
1023 smb_fname_str_dbg(smb_fname)));
1024 errno = EPERM;
1025 return -1;
1028 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1029 if (ret == 0) {
1030 goto done;
1033 if((errno != EPERM) && (errno != EACCES))
1034 return -1;
1036 if(!lp_dos_filemode(SNUM(conn)))
1037 return -1;
1039 /* We want DOS semantics, ie allow non owner with write permission to change the
1040 bits on a file. Just like file_ntimes below.
1043 if (!can_write_to_fsp(smb_fname->fsp))
1045 errno = EACCES;
1046 return -1;
1049 become_root();
1050 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1051 unbecome_root();
1053 done:
1054 if (!newfile) {
1055 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1056 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1057 smb_fname->base_name);
1059 if (ret == 0) {
1060 smb_fname->st.st_ex_mode = unixmode;
1063 return( ret );
1067 NTSTATUS file_set_sparse(connection_struct *conn,
1068 files_struct *fsp,
1069 bool sparse)
1071 const struct loadparm_substitution *lp_sub =
1072 loadparm_s3_global_substitution();
1073 uint32_t old_dosmode;
1074 uint32_t new_dosmode;
1075 NTSTATUS status;
1077 if (!CAN_WRITE(conn)) {
1078 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1079 "on readonly share[%s]\n",
1080 smb_fname_str_dbg(fsp->fsp_name),
1081 sparse,
1082 lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
1083 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1087 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1088 * following access flags are granted.
1090 status = check_any_access_fsp(fsp,
1091 FILE_WRITE_DATA
1092 | FILE_WRITE_ATTRIBUTES
1093 | SEC_FILE_APPEND_DATA);
1094 if (!NT_STATUS_IS_OK(status)) {
1095 DBG_DEBUG("fname[%s] set[%u] "
1096 "access_mask[0x%08X] - access denied\n",
1097 smb_fname_str_dbg(fsp->fsp_name),
1098 sparse,
1099 fsp->access_mask);
1100 return status;
1103 if (fsp->fsp_flags.is_directory) {
1104 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1105 (sparse ? "set" : "clear"),
1106 smb_fname_str_dbg(fsp->fsp_name)));
1107 return NT_STATUS_INVALID_PARAMETER;
1110 if (IS_IPC(conn) || IS_PRINT(conn)) {
1111 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1112 (sparse ? "set" : "clear")));
1113 return NT_STATUS_INVALID_PARAMETER;
1116 if (fsp_is_alternate_stream(fsp)) {
1118 * MS-FSA 2.1.1.5 IsSparse
1120 * This is a per stream attribute, but our backends don't
1121 * support it a consistent way, therefore just pretend
1122 * success and ignore the request.
1124 DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
1125 "[%s]\n", fsp_str_dbg(fsp));
1126 return NT_STATUS_OK;
1129 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1130 sparse, smb_fname_str_dbg(fsp->fsp_name)));
1132 if (!lp_store_dos_attributes(SNUM(conn))) {
1133 return NT_STATUS_INVALID_DEVICE_REQUEST;
1136 status = vfs_stat_fsp(fsp);
1137 if (!NT_STATUS_IS_OK(status)) {
1138 return status;
1141 old_dosmode = fdos_mode(fsp);
1143 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1144 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1145 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1146 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1147 } else {
1148 return NT_STATUS_OK;
1151 /* Store the DOS attributes in an EA. */
1152 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1153 if (!NT_STATUS_IS_OK(status)) {
1154 return status;
1157 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1158 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1159 fsp->fsp_name->base_name);
1161 fsp->fsp_name->st.cached_dos_attributes = new_dosmode;
1162 fsp->fsp_flags.is_sparse = sparse;
1164 return NT_STATUS_OK;
1167 /*******************************************************************
1168 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1169 than POSIX.
1170 *******************************************************************/
1172 int file_ntimes(connection_struct *conn,
1173 files_struct *fsp,
1174 struct smb_file_time *ft)
1176 int ret = -1;
1178 errno = 0;
1180 DBG_INFO("actime: %s",
1181 time_to_asc(convert_timespec_to_time_t(ft->atime)));
1182 DBG_INFO("modtime: %s",
1183 time_to_asc(convert_timespec_to_time_t(ft->mtime)));
1184 DBG_INFO("ctime: %s",
1185 time_to_asc(convert_timespec_to_time_t(ft->ctime)));
1186 DBG_INFO("createtime: %s",
1187 time_to_asc(convert_timespec_to_time_t(ft->create_time)));
1189 /* Don't update the time on read-only shares */
1190 /* We need this as set_filetime (which can be called on
1191 close and other paths) can end up calling this function
1192 without the NEED_WRITE protection. Found by :
1193 Leo Weppelman <leo@wau.mis.ah.nl>
1196 if (!CAN_WRITE(conn)) {
1197 return 0;
1200 if (SMB_VFS_FNTIMES(fsp, ft) == 0) {
1201 ret = 0;
1202 goto done;
1205 if((errno != EPERM) && (errno != EACCES)) {
1206 return -1;
1209 if(!lp_dos_filetimes(SNUM(conn))) {
1210 return -1;
1213 /* We have permission (given by the Samba admin) to
1214 break POSIX semantics and allow a user to change
1215 the time on a file they don't own but can write to
1216 (as DOS does).
1219 /* Check if we have write access. */
1220 if (can_write_to_fsp(fsp)) {
1221 /* We are allowed to become root and change the filetime. */
1222 become_root();
1223 ret = SMB_VFS_FNTIMES(fsp, ft);
1224 unbecome_root();
1227 done:
1228 if (ret == 0) {
1229 copy_stat_ex_timestamps(&fsp->fsp_name->st, ft);
1232 return ret;
1235 /******************************************************************
1236 Force a "sticky" write time on a pathname. This will always be
1237 returned on all future write time queries and set on close.
1238 ******************************************************************/
1240 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1242 if (is_omit_timespec(&mtime)) {
1243 return true;
1246 if (!set_sticky_write_time(fileid, mtime)) {
1247 return false;
1250 return true;
1253 /******************************************************************
1254 Force a "sticky" write time on an fsp. This will always be
1255 returned on all future write time queries and set on close.
1256 ******************************************************************/
1258 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1260 if (is_omit_timespec(&mtime)) {
1261 return true;
1264 fsp->fsp_flags.write_time_forced = true;
1265 TALLOC_FREE(fsp->update_write_time_event);
1267 return set_sticky_write_time_path(fsp->file_id, mtime);
1270 /******************************************************************
1271 Set a create time EA.
1272 ******************************************************************/
1274 NTSTATUS set_create_timespec_ea(struct files_struct *fsp,
1275 struct timespec create_time)
1277 uint32_t dosmode;
1278 int ret;
1280 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
1281 return NT_STATUS_OK;
1284 dosmode = fdos_mode(fsp);
1286 fsp->fsp_name->st.st_ex_btime = create_time;
1287 ret = file_set_dosmode(fsp->conn, fsp->fsp_name, dosmode, NULL, false);
1288 if (ret == -1) {
1289 return map_nt_error_from_unix(errno);
1292 DBG_DEBUG("wrote create time EA for file %s\n",
1293 smb_fname_str_dbg(fsp->fsp_name));
1295 return NT_STATUS_OK;
1298 /******************************************************************
1299 Return a create time.
1300 ******************************************************************/
1302 struct timespec get_create_timespec(connection_struct *conn,
1303 struct files_struct *fsp,
1304 const struct smb_filename *smb_fname)
1306 if (fsp != NULL) {
1307 struct files_struct *meta_fsp = metadata_fsp(fsp);
1308 return meta_fsp->fsp_name->st.st_ex_btime;
1310 return smb_fname->st.st_ex_btime;
1313 /******************************************************************
1314 Return a change time (may look at EA in future).
1315 ******************************************************************/
1317 struct timespec get_change_timespec(connection_struct *conn,
1318 struct files_struct *fsp,
1319 const struct smb_filename *smb_fname)
1321 return smb_fname->st.st_ex_mtime;