libsmb: Add reparse_data_buffer_marshall()
[Samba.git] / source3 / smbd / dosmode.c
blobdec1a22edd0a630dfbb901ae9de509a16fe1779b
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 (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
116 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
119 if ((parent_dirfsp != NULL) && lp_inherit_permissions(SNUM(conn))) {
120 struct stat_ex sbuf = { .st_ex_nlink = 0, };
121 int ret;
123 DBG_DEBUG("[%s] inheriting from [%s]\n",
124 smb_fname_str_dbg(smb_fname),
125 smb_fname_str_dbg(parent_dirfsp->fsp_name));
127 ret = SMB_VFS_FSTAT(parent_dirfsp, &sbuf);
128 if (ret != 0) {
129 DBG_ERR("fstat failed [%s]: %s\n",
130 smb_fname_str_dbg(parent_dirfsp->fsp_name),
131 strerror(errno));
132 return(0); /* *** shouldn't happen! *** */
135 /* Save for later - but explicitly remove setuid bit for safety. */
136 dir_mode = sbuf.st_ex_mode & ~S_ISUID;
137 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
138 smb_fname_str_dbg(smb_fname), (int)dir_mode));
139 /* Clear "result" */
140 result = 0;
143 if (IS_DOS_DIR(dosmode)) {
144 /* We never make directories read only for the owner as under DOS a user
145 can always create a file in a read-only directory. */
146 result |= (S_IFDIR | S_IWUSR);
148 if (dir_mode) {
149 /* Inherit mode of parent directory. */
150 result |= dir_mode;
151 } else {
152 /* Provisionally add all 'x' bits */
153 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
155 /* Apply directory mask */
156 result &= lp_directory_mask(SNUM(conn));
157 /* Add in force bits */
158 result |= lp_force_directory_mode(SNUM(conn));
160 } else {
161 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
162 result |= S_IXUSR;
164 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
165 result |= S_IXGRP;
167 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
168 result |= S_IXOTH;
170 if (dir_mode) {
171 /* Inherit 666 component of parent directory mode */
172 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
173 } else {
174 /* Apply mode mask */
175 result &= lp_create_mask(SNUM(conn));
176 /* Add in force bits */
177 result |= lp_force_create_mode(SNUM(conn));
181 DBG_INFO("unix_mode(%s) returning 0%o\n",
182 smb_fname_str_dbg(smb_fname), (int)result);
184 return(result);
187 /****************************************************************************
188 Change a unix mode to a dos mode.
189 ****************************************************************************/
191 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
192 const struct stat_ex *st,
193 struct files_struct *fsp)
195 int result = 0;
196 enum mapreadonly_options ro_opts =
197 (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
199 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
200 /* if we can find out if a file is immutable we should report it r/o */
201 if (st->st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
202 result |= FILE_ATTRIBUTE_READONLY;
204 #endif
205 if (ro_opts == MAP_READONLY_YES) {
206 /* Original Samba method - map inverse of user "w" bit. */
207 if ((st->st_ex_mode & S_IWUSR) == 0) {
208 result |= FILE_ATTRIBUTE_READONLY;
210 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
211 /* smb_fname->fsp can be NULL for an MS-DFS link. */
212 /* Check actual permissions for read-only. */
213 if ((fsp != NULL) && !can_write_to_fsp(fsp)) {
214 result |= FILE_ATTRIBUTE_READONLY;
216 } /* Else never set the readonly bit. */
218 if (MAP_ARCHIVE(conn) && ((st->st_ex_mode & S_IXUSR) != 0)) {
219 result |= FILE_ATTRIBUTE_ARCHIVE;
222 if (MAP_SYSTEM(conn) && ((st->st_ex_mode & S_IXGRP) != 0)) {
223 result |= FILE_ATTRIBUTE_SYSTEM;
226 if (MAP_HIDDEN(conn) && ((st->st_ex_mode & S_IXOTH) != 0)) {
227 result |= FILE_ATTRIBUTE_HIDDEN;
230 if (S_ISDIR(st->st_ex_mode)) {
231 result = FILE_ATTRIBUTE_DIRECTORY |
232 (result & FILE_ATTRIBUTE_READONLY);
235 dos_mode_debug_print(__func__, result);
237 return result;
240 /****************************************************************************
241 Get DOS attributes from an EA.
242 This can also pull the create time into the stat struct inside smb_fname.
243 ****************************************************************************/
245 NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
246 DATA_BLOB blob,
247 uint32_t *pattr)
249 struct xattr_DOSATTRIB dosattrib;
250 enum ndr_err_code ndr_err;
251 uint32_t dosattr;
253 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
254 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
256 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
257 DBG_WARNING("bad ndr decode "
258 "from EA on file %s: Error = %s\n",
259 smb_fname_str_dbg(smb_fname),
260 ndr_errstr(ndr_err));
261 return ndr_map_error2ntstatus(ndr_err);
264 DBG_DEBUG("%s attr = %s\n",
265 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex);
267 switch (dosattrib.version) {
268 case 0xFFFF:
269 dosattr = dosattrib.info.compatinfoFFFF.attrib;
270 break;
271 case 1:
272 dosattr = dosattrib.info.info1.attrib;
273 if (!null_nttime(dosattrib.info.info1.create_time)) {
274 struct timespec create_time =
275 nt_time_to_unix_timespec(
276 dosattrib.info.info1.create_time);
278 update_stat_ex_create_time(&smb_fname->st,
279 create_time);
281 DBG_DEBUG("file %s case 1 set btime %s",
282 smb_fname_str_dbg(smb_fname),
283 time_to_asc(convert_timespec_to_time_t(
284 create_time)));
286 break;
287 case 2:
288 dosattr = dosattrib.info.oldinfo2.attrib;
289 /* Don't know what flags to check for this case. */
290 break;
291 case 3:
292 dosattr = dosattrib.info.info3.attrib;
293 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
294 !null_nttime(dosattrib.info.info3.create_time)) {
295 struct timespec create_time =
296 nt_time_to_full_timespec(
297 dosattrib.info.info3.create_time);
299 update_stat_ex_create_time(&smb_fname->st,
300 create_time);
302 DBG_DEBUG("file %s case 3 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 4:
309 case 5:
311 uint32_t info_valid_flags;
312 NTTIME info_create_time;
314 if (dosattrib.version == 4) {
315 info_valid_flags = dosattrib.info.info4.valid_flags;
316 info_create_time = dosattrib.info.info4.create_time;
317 dosattr = dosattrib.info.info4.attrib;
318 } else {
319 info_valid_flags = dosattrib.info.info5.valid_flags;
320 info_create_time = dosattrib.info.info5.create_time;
321 dosattr = dosattrib.info.info5.attrib;
324 if ((info_valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
325 !null_nttime(info_create_time))
327 struct timespec creat_time;
329 creat_time = nt_time_to_full_timespec(info_create_time);
330 update_stat_ex_create_time(&smb_fname->st, creat_time);
332 DBG_DEBUG("file [%s] creation time [%s]\n",
333 smb_fname_str_dbg(smb_fname),
334 nt_time_string(talloc_tos(), info_create_time));
337 break;
339 default:
340 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
341 smb_fname_str_dbg(smb_fname), blob.data);
342 /* Should this be INTERNAL_ERROR? */
343 return NT_STATUS_INVALID_PARAMETER;
346 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
347 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
350 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
351 *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
353 dos_mode_debug_print(__func__, *pattr);
355 return NT_STATUS_OK;
358 NTSTATUS fget_ea_dos_attribute(struct files_struct *fsp,
359 uint32_t *pattr)
361 DATA_BLOB blob;
362 ssize_t sizeret;
363 fstring attrstr;
364 NTSTATUS status;
366 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
367 return NT_STATUS_NOT_IMPLEMENTED;
370 /* Don't reset pattr to zero as we may already have filename-based attributes we
371 need to preserve. */
373 sizeret = SMB_VFS_FGETXATTR(fsp,
374 SAMBA_XATTR_DOS_ATTRIB,
375 attrstr,
376 sizeof(attrstr));
377 if (sizeret == -1 && ( errno == EPERM || errno == EACCES )) {
378 /* we may also retrieve dos attribs for unreadable files, this
379 is why we'll retry as root. We don't use root in the first
380 run because in cases like NFS, root might have even less
381 rights than the real user
383 become_root();
384 sizeret = SMB_VFS_FGETXATTR(fsp,
385 SAMBA_XATTR_DOS_ATTRIB,
386 attrstr,
387 sizeof(attrstr));
388 unbecome_root();
390 if (sizeret == -1) {
391 DBG_INFO("Cannot get attribute "
392 "from EA on file %s: Error = %s\n",
393 fsp_str_dbg(fsp), strerror(errno));
394 return map_nt_error_from_unix(errno);
397 blob.data = (uint8_t *)attrstr;
398 blob.length = sizeret;
400 status = parse_dos_attribute_blob(fsp->fsp_name, blob, pattr);
401 if (!NT_STATUS_IS_OK(status)) {
402 return status;
405 return NT_STATUS_OK;
408 /****************************************************************************
409 Set DOS attributes in an EA.
410 Also sets the create time.
411 ****************************************************************************/
413 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
414 struct smb_filename *smb_fname,
415 uint32_t dosmode)
417 struct xattr_DOSATTRIB dosattrib = { .version = 0, };
418 enum ndr_err_code ndr_err;
419 DATA_BLOB blob = { .data = NULL, };
420 struct timespec btime;
421 int ret;
423 if (!lp_store_dos_attributes(SNUM(conn))) {
424 return NT_STATUS_NOT_IMPLEMENTED;
427 if (smb_fname->fsp == NULL) {
428 /* symlink */
429 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
432 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
433 * vfs_default via DMAPI if that is enabled.
435 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
437 dosattrib.version = 5;
438 dosattrib.info.info5.valid_flags = XATTR_DOSINFO_ATTRIB |
439 XATTR_DOSINFO_CREATE_TIME;
440 dosattrib.info.info5.attrib = dosmode;
441 dosattrib.info.info5.create_time = full_timespec_to_nt_time(
442 &smb_fname->st.st_ex_btime);
444 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
445 (unsigned int)dosmode,
446 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
447 smb_fname_str_dbg(smb_fname) ));
449 ndr_err = ndr_push_struct_blob(
450 &blob, talloc_tos(), &dosattrib,
451 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
453 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
454 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
455 ndr_errstr(ndr_err)));
456 return ndr_map_error2ntstatus(ndr_err);
459 if (blob.data == NULL || blob.length == 0) {
460 /* Should this be INTERNAL_ERROR? */
461 return NT_STATUS_INVALID_PARAMETER;
464 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
465 SAMBA_XATTR_DOS_ATTRIB,
466 blob.data, blob.length, 0);
467 if (ret != 0) {
468 NTSTATUS status = NT_STATUS_OK;
469 bool set_dosmode_ok = false;
471 if ((errno != EPERM) && (errno != EACCES)) {
472 DBG_INFO("Cannot set "
473 "attribute EA on file %s: Error = %s\n",
474 smb_fname_str_dbg(smb_fname), strerror(errno));
475 return map_nt_error_from_unix(errno);
478 /* We want DOS semantics, ie allow non owner with write permission to change the
479 bits on a file. Just like file_ntimes below.
482 /* Check if we have write access. */
483 if (!CAN_WRITE(conn)) {
484 return NT_STATUS_ACCESS_DENIED;
487 status = smbd_check_access_rights_fsp(conn->cwd_fsp,
488 smb_fname->fsp,
489 false,
490 FILE_WRITE_ATTRIBUTES);
491 if (NT_STATUS_IS_OK(status)) {
492 set_dosmode_ok = true;
495 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
496 set_dosmode_ok = can_write_to_fsp(smb_fname->fsp);
499 if (!set_dosmode_ok) {
500 return NT_STATUS_ACCESS_DENIED;
503 become_root();
504 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
505 SAMBA_XATTR_DOS_ATTRIB,
506 blob.data, blob.length, 0);
507 if (ret == 0) {
508 status = NT_STATUS_OK;
510 unbecome_root();
511 if (!NT_STATUS_IS_OK(status)) {
512 return status;
517 * We correctly stored the create time.
518 * We *always* set XATTR_DOSINFO_CREATE_TIME,
519 * so now it can no longer be considered
520 * calculated. Make sure to use the value rounded
521 * to NTTIME granularity we've stored in the xattr.
523 btime = nt_time_to_full_timespec(dosattrib.info.info5.create_time);
524 update_stat_ex_create_time(&smb_fname->st, btime);
526 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
527 (unsigned int)dosmode,
528 smb_fname_str_dbg(smb_fname)));
529 return NT_STATUS_OK;
532 static uint32_t
533 dos_mode_from_name(connection_struct *conn, const char *name, uint32_t dosmode)
535 const char *p = NULL;
536 uint32_t result = dosmode;
538 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
539 lp_hide_dot_files(SNUM(conn)))
541 p = strrchr_m(name, '/');
542 if (p) {
543 p++;
544 } else {
545 p = name;
548 /* Only . and .. are not hidden. */
549 if ((p[0] == '.') && !(ISDOT(p) || ISDOTDOT(p))) {
550 result |= FILE_ATTRIBUTE_HIDDEN;
554 if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, name)) {
555 result |= FILE_ATTRIBUTE_HIDDEN;
558 return result;
561 /****************************************************************************
562 Change a unix mode to a dos mode for an ms dfs link.
563 ****************************************************************************/
565 uint32_t dos_mode_msdfs(connection_struct *conn,
566 const char *name,
567 const struct stat_ex *st)
569 uint32_t result = 0;
571 DEBUG(8, ("dos_mode_msdfs: %s\n", name));
573 if (!VALID_STAT(*st)) {
574 return 0;
577 result = dos_mode_from_name(conn, name, result);
578 result |= dos_mode_from_sbuf(conn, st, NULL);
580 if (result == 0) {
581 result = FILE_ATTRIBUTE_NORMAL;
584 result = filter_mode_by_protocol(result);
587 * Add in that it is a reparse point
589 result |= FILE_ATTRIBUTE_REPARSE_POINT;
591 dos_mode_debug_print(__func__, result);
593 return(result);
597 * check whether a file or directory is flagged as compressed.
599 static NTSTATUS dos_mode_check_compressed(struct files_struct *fsp,
600 bool *is_compressed)
602 NTSTATUS status;
603 uint16_t compression_fmt;
605 status = SMB_VFS_FGET_COMPRESSION(
606 fsp->conn, talloc_tos(), fsp, &compression_fmt);
607 if (!NT_STATUS_IS_OK(status)) {
608 return status;
611 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
612 *is_compressed = true;
613 } else {
614 *is_compressed = false;
616 return NT_STATUS_OK;
619 static uint32_t dos_mode_post(uint32_t dosmode,
620 struct files_struct *fsp,
621 const char *func)
623 struct smb_filename *smb_fname = NULL;
624 NTSTATUS status;
626 if (fsp != NULL) {
627 smb_fname = fsp->fsp_name;
629 SMB_ASSERT(smb_fname != NULL);
632 * According to MS-FSA a stream name does not have
633 * separate DOS attribute metadata, so we must return
634 * the DOS attribute from the base filename. With one caveat,
635 * a non-default stream name can never be a directory.
637 * As this is common to all streams data stores, we handle
638 * it here instead of inside all stream VFS modules.
640 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
643 if (is_named_stream(smb_fname)) {
644 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
645 dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
648 if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
649 bool compressed = false;
651 status = dos_mode_check_compressed(fsp, &compressed);
652 if (NT_STATUS_IS_OK(status) && compressed) {
653 dosmode |= FILE_ATTRIBUTE_COMPRESSED;
657 dosmode |= dos_mode_from_name(fsp->conn, smb_fname->base_name, dosmode);
659 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
660 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
661 } else if (dosmode == 0) {
662 dosmode = FILE_ATTRIBUTE_NORMAL;
665 dosmode = filter_mode_by_protocol(dosmode);
667 dos_mode_debug_print(func, dosmode);
668 return dosmode;
671 /****************************************************************************
672 Change a unix mode to a dos mode.
673 May also read the create timespec into the stat struct in smb_fname
674 if "store dos attributes" is true.
675 ****************************************************************************/
677 uint32_t fdos_mode(struct files_struct *fsp)
679 uint32_t result = 0;
680 NTSTATUS status = NT_STATUS_OK;
682 if (fsp == NULL) {
684 * The pathological case where a callers does
685 * fdos_mode(smb_fname->fsp) passing a pathref fsp. But as
686 * smb_fname points at a symlink in POSIX context smb_fname->fsp
687 * is NULL.
689 return FILE_ATTRIBUTE_NORMAL;
692 DBG_DEBUG("%s\n", fsp_str_dbg(fsp));
694 if (fsp->fake_file_handle != NULL) {
695 return dosmode_from_fake_filehandle(fsp->fake_file_handle);
698 if (!VALID_STAT(fsp->fsp_name->st)) {
699 return 0;
702 if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) {
703 return FILE_ATTRIBUTE_NORMAL;
706 if (fsp->fsp_name->st.cached_dos_attributes != FILE_ATTRIBUTES_INVALID) {
707 return fsp->fsp_name->st.cached_dos_attributes;
710 /* Get the DOS attributes via the VFS if we can */
711 status = vfs_fget_dos_attributes(fsp, &result);
712 if (!NT_STATUS_IS_OK(status)) {
714 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
716 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
717 result |= dos_mode_from_sbuf(fsp->conn,
718 &fsp->fsp_name->st,
719 fsp);
723 fsp->fsp_name->st.cached_dos_attributes = dos_mode_post(result, fsp, __func__);
724 return fsp->fsp_name->st.cached_dos_attributes;
727 struct dos_mode_at_state {
728 files_struct *dir_fsp;
729 struct smb_filename *smb_fname;
730 uint32_t dosmode;
733 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
735 struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
736 struct tevent_context *ev,
737 files_struct *dir_fsp,
738 struct smb_filename *smb_fname)
740 struct tevent_req *req = NULL;
741 struct dos_mode_at_state *state = NULL;
742 struct tevent_req *subreq = NULL;
744 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
746 req = tevent_req_create(mem_ctx, &state,
747 struct dos_mode_at_state);
748 if (req == NULL) {
749 return NULL;
752 *state = (struct dos_mode_at_state) {
753 .dir_fsp = dir_fsp,
754 .smb_fname = smb_fname,
757 if (!VALID_STAT(smb_fname->st)) {
758 tevent_req_done(req);
759 return tevent_req_post(req, ev);
762 if (smb_fname->fsp == NULL) {
763 if (ISDOTDOT(smb_fname->base_name)) {
765 * smb_fname->fsp is explicitly closed
766 * for ".." to prevent meta-data leakage.
768 state->dosmode = FILE_ATTRIBUTE_DIRECTORY;
769 } else {
771 * This is a symlink in POSIX context.
772 * FIXME ? Should we move to returning
773 * FILE_ATTRIBUTE_REPARSE_POINT here ?
775 state->dosmode = FILE_ATTRIBUTE_NORMAL;
777 tevent_req_done(req);
778 return tevent_req_post(req, ev);
781 subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
783 dir_fsp,
784 smb_fname);
785 if (tevent_req_nomem(subreq, req)) {
786 return tevent_req_post(req, ev);
788 tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
790 return req;
793 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
795 struct tevent_req *req =
796 tevent_req_callback_data(subreq,
797 struct tevent_req);
798 struct dos_mode_at_state *state =
799 tevent_req_data(req,
800 struct dos_mode_at_state);
801 struct vfs_aio_state aio_state;
802 NTSTATUS status;
803 bool ok;
806 * Make sure we run as the user again
808 ok = change_to_user_and_service_by_fsp(state->dir_fsp);
809 SMB_ASSERT(ok);
811 status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
812 &aio_state,
813 &state->dosmode);
814 TALLOC_FREE(subreq);
815 if (!NT_STATUS_IS_OK(status)) {
817 * Both the sync dos_mode() as well as the async
818 * dos_mode_at_[send|recv] have no real error return, the only
819 * unhandled error is when the stat info in smb_fname is not
820 * valid (cf the checks in dos_mode() and dos_mode_at_send().
822 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
823 * dos_mode_post() which also does the mapping of a last resort
824 * from S_IFMT(st_mode).
826 * Only if we get NT_STATUS_NOT_IMPLEMENTED or
827 * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
828 * fallback to sync processing.
830 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) &&
831 !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED))
834 * state->dosmode should still be 0, but reset
835 * it to be sure.
837 state->dosmode = 0;
838 status = NT_STATUS_OK;
841 if (NT_STATUS_IS_OK(status)) {
842 state->dosmode = dos_mode_post(state->dosmode,
843 state->smb_fname->fsp,
844 __func__);
845 tevent_req_done(req);
846 return;
850 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
853 state->dosmode = fdos_mode(state->smb_fname->fsp);
854 tevent_req_done(req);
855 return;
858 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
860 struct dos_mode_at_state *state =
861 tevent_req_data(req,
862 struct dos_mode_at_state);
863 NTSTATUS status;
865 if (tevent_req_is_nterror(req, &status)) {
866 tevent_req_received(req);
867 return status;
870 *dosmode = state->dosmode;
871 tevent_req_received(req);
872 return NT_STATUS_OK;
875 /*******************************************************************
876 chmod a file - but preserve some bits.
877 If "store dos attributes" is also set it will store the create time
878 from the stat struct in smb_fname (in NTTIME format) in the EA
879 attribute also.
880 ********************************************************************/
882 int file_set_dosmode(connection_struct *conn,
883 struct smb_filename *smb_fname,
884 uint32_t dosmode,
885 struct smb_filename *parent_dir,
886 bool newfile)
888 int mask=0;
889 mode_t tmp;
890 mode_t unixmode;
891 int ret = -1;
892 NTSTATUS status;
894 if (!CAN_WRITE(conn)) {
895 errno = EROFS;
896 return -1;
899 if ((S_ISDIR(smb_fname->st.st_ex_mode)) &&
900 (dosmode & FILE_ATTRIBUTE_TEMPORARY))
902 errno = EINVAL;
903 return -1;
906 dosmode &= SAMBA_ATTRIBUTES_MASK;
908 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
909 dosmode, smb_fname_str_dbg(smb_fname)));
911 unixmode = smb_fname->st.st_ex_mode;
913 if (smb_fname->fsp != NULL) {
914 get_acl_group_bits(
915 conn, smb_fname->fsp, &smb_fname->st.st_ex_mode);
918 if (S_ISDIR(smb_fname->st.st_ex_mode))
919 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
920 else
921 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
923 if (smb_fname->fsp != NULL) {
924 /* Store the DOS attributes in an EA by preference. */
925 status = SMB_VFS_FSET_DOS_ATTRIBUTES(
926 conn, metadata_fsp(smb_fname->fsp), dosmode);
927 } else {
928 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
931 if (NT_STATUS_IS_OK(status)) {
932 smb_fname->st.cached_dos_attributes = dosmode;
933 ret = 0;
934 goto done;
938 * Only fall back to using UNIX modes if
939 * we get NOT_IMPLEMENTED.
941 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
942 errno = map_errno_from_nt_status(status);
943 return -1;
946 /* Fall back to UNIX modes. */
947 unixmode = unix_mode(
948 conn,
949 dosmode,
950 smb_fname,
951 parent_dir != NULL ? parent_dir->fsp : NULL);
953 /* preserve the file type bits */
954 mask |= S_IFMT;
956 /* preserve the s bits */
957 mask |= (S_ISUID | S_ISGID);
959 /* preserve the t bit */
960 #ifdef S_ISVTX
961 mask |= S_ISVTX;
962 #endif
964 /* possibly preserve the x bits */
965 if (!MAP_ARCHIVE(conn))
966 mask |= S_IXUSR;
967 if (!MAP_SYSTEM(conn))
968 mask |= S_IXGRP;
969 if (!MAP_HIDDEN(conn))
970 mask |= S_IXOTH;
972 unixmode |= (smb_fname->st.st_ex_mode & mask);
974 /* if we previously had any r bits set then leave them alone */
975 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
976 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
977 unixmode |= tmp;
980 /* if we previously had any w bits set then leave them alone
981 whilst adding in the new w bits, if the new mode is not rdonly */
982 if (!IS_DOS_READONLY(dosmode)) {
983 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
987 * From the chmod 2 man page:
989 * "If the calling process is not privileged, and the group of the file
990 * does not match the effective group ID of the process or one of its
991 * supplementary group IDs, the S_ISGID bit will be turned off, but
992 * this will not cause an error to be returned."
994 * Simply refuse to do the chmod in this case.
997 if (S_ISDIR(smb_fname->st.st_ex_mode) &&
998 (unixmode & S_ISGID) &&
999 geteuid() != sec_initial_uid() &&
1000 !current_user_in_group(conn, smb_fname->st.st_ex_gid))
1002 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1003 "set for directory %s\n",
1004 smb_fname_str_dbg(smb_fname)));
1005 errno = EPERM;
1006 return -1;
1009 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1010 if (ret == 0) {
1011 goto done;
1014 if((errno != EPERM) && (errno != EACCES))
1015 return -1;
1017 if(!lp_dos_filemode(SNUM(conn)))
1018 return -1;
1020 /* We want DOS semantics, ie allow non owner with write permission to change the
1021 bits on a file. Just like file_ntimes below.
1024 if (!can_write_to_fsp(smb_fname->fsp))
1026 errno = EACCES;
1027 return -1;
1030 become_root();
1031 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1032 unbecome_root();
1034 done:
1035 if (!newfile) {
1036 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1037 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1038 smb_fname->base_name);
1040 if (ret == 0) {
1041 smb_fname->st.st_ex_mode = unixmode;
1044 return( ret );
1048 NTSTATUS file_set_sparse(connection_struct *conn,
1049 files_struct *fsp,
1050 bool sparse)
1052 const struct loadparm_substitution *lp_sub =
1053 loadparm_s3_global_substitution();
1054 uint32_t old_dosmode;
1055 uint32_t new_dosmode;
1056 NTSTATUS status;
1058 if (!CAN_WRITE(conn)) {
1059 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1060 "on readonly share[%s]\n",
1061 smb_fname_str_dbg(fsp->fsp_name),
1062 sparse,
1063 lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
1064 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1068 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1069 * following access flags are granted.
1071 if ((fsp->access_mask & (FILE_WRITE_DATA
1072 | FILE_WRITE_ATTRIBUTES
1073 | SEC_FILE_APPEND_DATA)) == 0) {
1074 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1075 "access_mask[0x%08X] - access denied\n",
1076 smb_fname_str_dbg(fsp->fsp_name),
1077 sparse,
1078 fsp->access_mask));
1079 return NT_STATUS_ACCESS_DENIED;
1082 if (fsp->fsp_flags.is_directory) {
1083 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1084 (sparse ? "set" : "clear"),
1085 smb_fname_str_dbg(fsp->fsp_name)));
1086 return NT_STATUS_INVALID_PARAMETER;
1089 if (IS_IPC(conn) || IS_PRINT(conn)) {
1090 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1091 (sparse ? "set" : "clear")));
1092 return NT_STATUS_INVALID_PARAMETER;
1095 if (fsp_is_alternate_stream(fsp)) {
1097 * MS-FSA 2.1.1.5 IsSparse
1099 * This is a per stream attribute, but our backends don't
1100 * support it a consistent way, therefore just pretend
1101 * success and ignore the request.
1103 DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
1104 "[%s]\n", fsp_str_dbg(fsp));
1105 return NT_STATUS_OK;
1108 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1109 sparse, smb_fname_str_dbg(fsp->fsp_name)));
1111 if (!lp_store_dos_attributes(SNUM(conn))) {
1112 return NT_STATUS_INVALID_DEVICE_REQUEST;
1115 status = vfs_stat_fsp(fsp);
1116 if (!NT_STATUS_IS_OK(status)) {
1117 return status;
1120 old_dosmode = fdos_mode(fsp);
1122 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1123 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1124 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1125 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1126 } else {
1127 return NT_STATUS_OK;
1130 /* Store the DOS attributes in an EA. */
1131 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1132 if (!NT_STATUS_IS_OK(status)) {
1133 return status;
1136 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1137 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1138 fsp->fsp_name->base_name);
1140 fsp->fsp_name->st.cached_dos_attributes = new_dosmode;
1141 fsp->fsp_flags.is_sparse = sparse;
1143 return NT_STATUS_OK;
1146 /*******************************************************************
1147 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1148 than POSIX.
1149 *******************************************************************/
1151 int file_ntimes(connection_struct *conn,
1152 files_struct *fsp,
1153 struct smb_file_time *ft)
1155 int ret = -1;
1157 errno = 0;
1159 DBG_INFO("actime: %s",
1160 time_to_asc(convert_timespec_to_time_t(ft->atime)));
1161 DBG_INFO("modtime: %s",
1162 time_to_asc(convert_timespec_to_time_t(ft->mtime)));
1163 DBG_INFO("ctime: %s",
1164 time_to_asc(convert_timespec_to_time_t(ft->ctime)));
1165 DBG_INFO("createtime: %s",
1166 time_to_asc(convert_timespec_to_time_t(ft->create_time)));
1168 /* Don't update the time on read-only shares */
1169 /* We need this as set_filetime (which can be called on
1170 close and other paths) can end up calling this function
1171 without the NEED_WRITE protection. Found by :
1172 Leo Weppelman <leo@wau.mis.ah.nl>
1175 if (!CAN_WRITE(conn)) {
1176 return 0;
1179 if (SMB_VFS_FNTIMES(fsp, ft) == 0) {
1180 return 0;
1183 if((errno != EPERM) && (errno != EACCES)) {
1184 return -1;
1187 if(!lp_dos_filetimes(SNUM(conn))) {
1188 return -1;
1191 /* We have permission (given by the Samba admin) to
1192 break POSIX semantics and allow a user to change
1193 the time on a file they don't own but can write to
1194 (as DOS does).
1197 /* Check if we have write access. */
1198 if (can_write_to_fsp(fsp)) {
1199 /* We are allowed to become root and change the filetime. */
1200 become_root();
1201 ret = SMB_VFS_FNTIMES(fsp, ft);
1202 unbecome_root();
1205 return ret;
1208 /******************************************************************
1209 Force a "sticky" write time on a pathname. This will always be
1210 returned on all future write time queries and set on close.
1211 ******************************************************************/
1213 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1215 if (is_omit_timespec(&mtime)) {
1216 return true;
1219 if (!set_sticky_write_time(fileid, mtime)) {
1220 return false;
1223 return true;
1226 /******************************************************************
1227 Force a "sticky" write time on an fsp. This will always be
1228 returned on all future write time queries and set on close.
1229 ******************************************************************/
1231 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1233 if (is_omit_timespec(&mtime)) {
1234 return true;
1237 fsp->fsp_flags.write_time_forced = true;
1238 TALLOC_FREE(fsp->update_write_time_event);
1240 return set_sticky_write_time_path(fsp->file_id, mtime);
1243 /******************************************************************
1244 Set a create time EA.
1245 ******************************************************************/
1247 NTSTATUS set_create_timespec_ea(struct files_struct *fsp,
1248 struct timespec create_time)
1250 uint32_t dosmode;
1251 int ret;
1253 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
1254 return NT_STATUS_OK;
1257 dosmode = fdos_mode(fsp);
1259 fsp->fsp_name->st.st_ex_btime = create_time;
1260 ret = file_set_dosmode(fsp->conn, fsp->fsp_name, dosmode, NULL, false);
1261 if (ret == -1) {
1262 return map_nt_error_from_unix(errno);
1265 DBG_DEBUG("wrote create time EA for file %s\n",
1266 smb_fname_str_dbg(fsp->fsp_name));
1268 return NT_STATUS_OK;
1271 /******************************************************************
1272 Return a create time.
1273 ******************************************************************/
1275 struct timespec get_create_timespec(connection_struct *conn,
1276 struct files_struct *fsp,
1277 const struct smb_filename *smb_fname)
1279 return smb_fname->st.st_ex_btime;
1282 /******************************************************************
1283 Return a change time (may look at EA in future).
1284 ******************************************************************/
1286 struct timespec get_change_timespec(connection_struct *conn,
1287 struct files_struct *fsp,
1288 const struct smb_filename *smb_fname)
1290 return smb_fname->st.st_ex_mtime;