smbXsrv_session: Remove a "can't happen" NULL check
[Samba.git] / source3 / smbd / dosmode.c
blob674a13076e1d98d4ed557fd631e4a5186235e574
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(enum protocol_types protocol,
73 uint32_t mode)
75 if (protocol <= PROTOCOL_LANMAN2) {
76 DEBUG(10,("filter_mode_by_protocol: "
77 "filtering result 0x%x to 0x%x\n",
78 (unsigned int)mode,
79 (unsigned int)(mode & 0x3f) ));
80 mode &= 0x3f;
82 return mode;
85 /****************************************************************************
86 Change a dos mode to a unix mode.
87 Base permission for files:
88 if creating file and inheriting (i.e. parent_dir != NULL)
89 apply read/write bits from parent directory.
90 else
91 everybody gets read bit set
92 dos readonly is represented in unix by removing everyone's write bit
93 dos archive is represented in unix by the user's execute bit
94 dos system is represented in unix by the group's execute bit
95 dos hidden is represented in unix by the other's execute bit
96 if !inheriting {
97 Then apply create mask,
98 then add force bits.
100 Base permission for directories:
101 dos directory is represented in unix by unix's dir bit and the exec bit
102 if !inheriting {
103 Then apply create mask,
104 then add force bits.
106 ****************************************************************************/
108 mode_t unix_mode(connection_struct *conn, int dosmode,
109 const struct smb_filename *smb_fname,
110 struct files_struct *parent_dirfsp)
112 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
113 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
114 * inheriting. */
116 if ((dosmode & FILE_ATTRIBUTE_READONLY) &&
117 !lp_store_dos_attributes(SNUM(conn))) {
118 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
121 if ((parent_dirfsp != NULL) && lp_inherit_permissions(SNUM(conn))) {
122 struct stat_ex sbuf = { .st_ex_nlink = 0, };
123 int ret;
125 DBG_DEBUG("[%s] inheriting from [%s]\n",
126 smb_fname_str_dbg(smb_fname),
127 smb_fname_str_dbg(parent_dirfsp->fsp_name));
129 ret = SMB_VFS_FSTAT(parent_dirfsp, &sbuf);
130 if (ret != 0) {
131 DBG_ERR("fstat failed [%s]: %s\n",
132 smb_fname_str_dbg(parent_dirfsp->fsp_name),
133 strerror(errno));
134 return(0); /* *** shouldn't happen! *** */
137 /* Save for later - but explicitly remove setuid bit for safety. */
138 dir_mode = sbuf.st_ex_mode & ~S_ISUID;
139 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
140 smb_fname_str_dbg(smb_fname), (int)dir_mode));
141 /* Clear "result" */
142 result = 0;
145 if (dosmode & FILE_ATTRIBUTE_DIRECTORY) {
146 /* We never make directories read only for the owner as under DOS a user
147 can always create a file in a read-only directory. */
148 result |= (S_IFDIR | S_IWUSR);
150 if (dir_mode) {
151 /* Inherit mode of parent directory. */
152 result |= dir_mode;
153 } else {
154 /* Provisionally add all 'x' bits */
155 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
157 /* Apply directory mask */
158 result &= lp_directory_mask(SNUM(conn));
159 /* Add in force bits */
160 result |= lp_force_directory_mode(SNUM(conn));
162 } else {
163 if ((dosmode & FILE_ATTRIBUTE_ARCHIVE) &&
164 lp_map_archive(SNUM(conn))) {
165 result |= S_IXUSR;
168 if ((dosmode & FILE_ATTRIBUTE_SYSTEM) &&
169 lp_map_system(SNUM(conn))) {
170 result |= S_IXGRP;
173 if ((dosmode & FILE_ATTRIBUTE_HIDDEN) &&
174 lp_map_hidden(SNUM(conn))) {
175 result |= S_IXOTH;
178 if (dir_mode) {
179 /* Inherit 666 component of parent directory mode */
180 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
181 } else {
182 /* Apply mode mask */
183 result &= lp_create_mask(SNUM(conn));
184 /* Add in force bits */
185 result |= lp_force_create_mode(SNUM(conn));
189 DBG_INFO("unix_mode(%s) returning 0%o\n",
190 smb_fname_str_dbg(smb_fname), (int)result);
192 return(result);
195 /****************************************************************************
196 Change a unix mode to a dos mode.
197 ****************************************************************************/
199 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
200 const struct stat_ex *st,
201 struct files_struct *fsp)
203 int result = 0;
204 enum mapreadonly_options ro_opts =
205 (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
207 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
208 /* if we can find out if a file is immutable we should report it r/o */
209 if (st->st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
210 result |= FILE_ATTRIBUTE_READONLY;
212 #endif
213 if (ro_opts == MAP_READONLY_YES) {
214 /* Original Samba method - map inverse of user "w" bit. */
215 if ((st->st_ex_mode & S_IWUSR) == 0) {
216 result |= FILE_ATTRIBUTE_READONLY;
218 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
219 /* smb_fname->fsp can be NULL for an MS-DFS link. */
220 /* Check actual permissions for read-only. */
221 if ((fsp != NULL) && !can_write_to_fsp(fsp)) {
222 result |= FILE_ATTRIBUTE_READONLY;
224 } /* Else never set the readonly bit. */
226 if (MAP_ARCHIVE(conn) && ((st->st_ex_mode & S_IXUSR) != 0)) {
227 result |= FILE_ATTRIBUTE_ARCHIVE;
230 if (MAP_SYSTEM(conn) && ((st->st_ex_mode & S_IXGRP) != 0)) {
231 result |= FILE_ATTRIBUTE_SYSTEM;
234 if (MAP_HIDDEN(conn) && ((st->st_ex_mode & S_IXOTH) != 0)) {
235 result |= FILE_ATTRIBUTE_HIDDEN;
238 if (S_ISDIR(st->st_ex_mode)) {
239 result = FILE_ATTRIBUTE_DIRECTORY |
240 (result & FILE_ATTRIBUTE_READONLY);
243 dos_mode_debug_print(__func__, result);
245 return result;
248 /****************************************************************************
249 Get DOS attributes from an EA.
250 This can also pull the create time into the stat struct inside smb_fname.
251 ****************************************************************************/
253 NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
254 DATA_BLOB blob,
255 uint32_t *pattr)
257 struct xattr_DOSATTRIB dosattrib;
258 enum ndr_err_code ndr_err;
259 uint32_t dosattr;
261 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
262 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
264 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
265 DBG_WARNING("bad ndr decode "
266 "from EA on file %s: Error = %s\n",
267 smb_fname_str_dbg(smb_fname),
268 ndr_errstr(ndr_err));
269 return ndr_map_error2ntstatus(ndr_err);
272 DBG_DEBUG("%s attr = %s\n",
273 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex);
275 switch (dosattrib.version) {
276 case 0xFFFF:
277 dosattr = dosattrib.info.compatinfoFFFF.attrib;
278 break;
279 case 1:
280 dosattr = dosattrib.info.info1.attrib;
281 if (!null_nttime(dosattrib.info.info1.create_time)) {
282 struct timespec create_time =
283 nt_time_to_unix_timespec(
284 dosattrib.info.info1.create_time);
286 update_stat_ex_create_time(&smb_fname->st,
287 create_time);
289 DBG_DEBUG("file %s case 1 set btime %s",
290 smb_fname_str_dbg(smb_fname),
291 time_to_asc(convert_timespec_to_time_t(
292 create_time)));
294 break;
295 case 2:
296 dosattr = dosattrib.info.oldinfo2.attrib;
297 /* Don't know what flags to check for this case. */
298 break;
299 case 3:
300 dosattr = dosattrib.info.info3.attrib;
301 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
302 !null_nttime(dosattrib.info.info3.create_time)) {
303 struct timespec create_time =
304 nt_time_to_full_timespec(
305 dosattrib.info.info3.create_time);
307 update_stat_ex_create_time(&smb_fname->st,
308 create_time);
310 DBG_DEBUG("file %s case 3 set btime %s",
311 smb_fname_str_dbg(smb_fname),
312 time_to_asc(convert_timespec_to_time_t(
313 create_time)));
315 break;
316 case 4:
317 case 5:
319 uint32_t info_valid_flags;
320 NTTIME info_create_time;
322 if (dosattrib.version == 4) {
323 info_valid_flags = dosattrib.info.info4.valid_flags;
324 info_create_time = dosattrib.info.info4.create_time;
325 dosattr = dosattrib.info.info4.attrib;
326 } else {
327 info_valid_flags = dosattrib.info.info5.valid_flags;
328 info_create_time = dosattrib.info.info5.create_time;
329 dosattr = dosattrib.info.info5.attrib;
332 if ((info_valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
333 !null_nttime(info_create_time))
335 struct timespec creat_time;
337 creat_time = nt_time_to_full_timespec(info_create_time);
338 update_stat_ex_create_time(&smb_fname->st, creat_time);
340 DBG_DEBUG("file [%s] creation time [%s]\n",
341 smb_fname_str_dbg(smb_fname),
342 nt_time_string(talloc_tos(), info_create_time));
345 break;
347 default:
348 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
349 smb_fname_str_dbg(smb_fname), blob.data);
350 /* Should this be INTERNAL_ERROR? */
351 return NT_STATUS_INVALID_PARAMETER;
354 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
355 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
358 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
359 *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
361 dos_mode_debug_print(__func__, *pattr);
363 return NT_STATUS_OK;
366 NTSTATUS fget_ea_dos_attribute(struct files_struct *fsp,
367 uint32_t *pattr)
369 DATA_BLOB blob;
370 ssize_t sizeret;
371 fstring attrstr;
372 NTSTATUS status;
374 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
375 return NT_STATUS_NOT_IMPLEMENTED;
378 /* Don't reset pattr to zero as we may already have filename-based attributes we
379 need to preserve. */
381 sizeret = SMB_VFS_FGETXATTR(fsp,
382 SAMBA_XATTR_DOS_ATTRIB,
383 attrstr,
384 sizeof(attrstr));
385 if (sizeret == -1 && ( errno == EPERM || errno == EACCES )) {
386 /* we may also retrieve dos attribs for unreadable files, this
387 is why we'll retry as root. We don't use root in the first
388 run because in cases like NFS, root might have even less
389 rights than the real user
391 become_root();
392 sizeret = SMB_VFS_FGETXATTR(fsp,
393 SAMBA_XATTR_DOS_ATTRIB,
394 attrstr,
395 sizeof(attrstr));
396 unbecome_root();
398 if (sizeret == -1) {
399 DBG_INFO("Cannot get attribute "
400 "from EA on file %s: Error = %s\n",
401 fsp_str_dbg(fsp), strerror(errno));
402 return map_nt_error_from_unix(errno);
405 blob.data = (uint8_t *)attrstr;
406 blob.length = sizeret;
408 status = parse_dos_attribute_blob(fsp->fsp_name, blob, pattr);
409 if (!NT_STATUS_IS_OK(status)) {
410 return status;
413 return NT_STATUS_OK;
416 /****************************************************************************
417 Set DOS attributes in an EA.
418 Also sets the create time.
419 ****************************************************************************/
421 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
422 struct smb_filename *smb_fname,
423 uint32_t dosmode)
425 struct xattr_DOSATTRIB dosattrib = { .version = 0, };
426 enum ndr_err_code ndr_err;
427 DATA_BLOB blob = { .data = NULL, };
428 struct timespec btime;
429 int ret;
431 if (!lp_store_dos_attributes(SNUM(conn))) {
432 return NT_STATUS_NOT_IMPLEMENTED;
435 if (smb_fname->fsp == NULL) {
436 /* symlink */
437 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
440 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
441 * vfs_default via DMAPI if that is enabled.
443 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
445 dosattrib.version = 5;
446 dosattrib.info.info5.valid_flags = XATTR_DOSINFO_ATTRIB |
447 XATTR_DOSINFO_CREATE_TIME;
448 dosattrib.info.info5.attrib = dosmode;
449 dosattrib.info.info5.create_time = full_timespec_to_nt_time(
450 &smb_fname->st.st_ex_btime);
452 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
453 (unsigned int)dosmode,
454 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
455 smb_fname_str_dbg(smb_fname) ));
457 ndr_err = ndr_push_struct_blob(
458 &blob, talloc_tos(), &dosattrib,
459 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
461 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
462 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
463 ndr_errstr(ndr_err)));
464 return ndr_map_error2ntstatus(ndr_err);
467 if (blob.data == NULL || blob.length == 0) {
468 /* Should this be INTERNAL_ERROR? */
469 return NT_STATUS_INVALID_PARAMETER;
472 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
473 SAMBA_XATTR_DOS_ATTRIB,
474 blob.data, blob.length, 0);
475 if (ret != 0) {
476 NTSTATUS status = NT_STATUS_OK;
477 bool set_dosmode_ok = false;
479 if ((errno != EPERM) && (errno != EACCES)) {
480 DBG_INFO("Cannot set "
481 "attribute EA on file %s: Error = %s\n",
482 smb_fname_str_dbg(smb_fname), strerror(errno));
483 return map_nt_error_from_unix(errno);
486 /* We want DOS semantics, ie allow non owner with write permission to change the
487 bits on a file. Just like file_ntimes below.
490 /* Check if we have write access. */
491 if (!CAN_WRITE(conn)) {
492 return NT_STATUS_ACCESS_DENIED;
495 status = smbd_check_access_rights_fsp(conn->cwd_fsp,
496 smb_fname->fsp,
497 false,
498 FILE_WRITE_ATTRIBUTES);
499 if (NT_STATUS_IS_OK(status)) {
500 set_dosmode_ok = true;
503 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
504 set_dosmode_ok = can_write_to_fsp(smb_fname->fsp);
507 if (!set_dosmode_ok) {
508 return NT_STATUS_ACCESS_DENIED;
511 become_root();
512 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
513 SAMBA_XATTR_DOS_ATTRIB,
514 blob.data, blob.length, 0);
515 if (ret == 0) {
516 status = NT_STATUS_OK;
518 unbecome_root();
519 if (!NT_STATUS_IS_OK(status)) {
520 return status;
525 * We correctly stored the create time.
526 * We *always* set XATTR_DOSINFO_CREATE_TIME,
527 * so now it can no longer be considered
528 * calculated. Make sure to use the value rounded
529 * to NTTIME granularity we've stored in the xattr.
531 btime = nt_time_to_full_timespec(dosattrib.info.info5.create_time);
532 update_stat_ex_create_time(&smb_fname->st, btime);
534 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
535 (unsigned int)dosmode,
536 smb_fname_str_dbg(smb_fname)));
537 return NT_STATUS_OK;
540 static uint32_t
541 dos_mode_from_name(connection_struct *conn, const char *name, uint32_t dosmode)
543 const char *p = NULL;
544 uint32_t result = dosmode;
546 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
547 lp_hide_dot_files(SNUM(conn)))
549 p = strrchr_m(name, '/');
550 if (p) {
551 p++;
552 } else {
553 p = name;
556 /* Only . and .. are not hidden. */
557 if ((p[0] == '.') && !(ISDOT(p) || ISDOTDOT(p))) {
558 result |= FILE_ATTRIBUTE_HIDDEN;
562 if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, name)) {
563 result |= FILE_ATTRIBUTE_HIDDEN;
566 return result;
569 /****************************************************************************
570 Change a unix mode to a dos mode for an ms dfs link.
571 ****************************************************************************/
573 uint32_t dos_mode_msdfs(connection_struct *conn,
574 const char *name,
575 const struct stat_ex *st)
577 uint32_t result = 0;
579 DEBUG(8, ("dos_mode_msdfs: %s\n", name));
581 if (!VALID_STAT(*st)) {
582 return 0;
585 result = dos_mode_from_name(conn, name, result);
586 result |= dos_mode_from_sbuf(conn, st, NULL);
588 if (result == 0) {
589 result = FILE_ATTRIBUTE_NORMAL;
592 result = filter_mode_by_protocol(conn_protocol(conn->sconn), result);
595 * Add in that it is a reparse point
597 result |= FILE_ATTRIBUTE_REPARSE_POINT;
599 dos_mode_debug_print(__func__, result);
601 return(result);
605 * check whether a file or directory is flagged as compressed.
607 static NTSTATUS dos_mode_check_compressed(struct files_struct *fsp,
608 bool *is_compressed)
610 NTSTATUS status;
611 uint16_t compression_fmt;
613 status = SMB_VFS_FGET_COMPRESSION(
614 fsp->conn, talloc_tos(), fsp, &compression_fmt);
615 if (!NT_STATUS_IS_OK(status)) {
616 return status;
619 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
620 *is_compressed = true;
621 } else {
622 *is_compressed = false;
624 return NT_STATUS_OK;
627 static uint32_t dos_mode_post(uint32_t dosmode,
628 struct files_struct *fsp,
629 const char *func)
631 struct smb_filename *smb_fname = NULL;
632 NTSTATUS status;
634 if (fsp != NULL) {
635 smb_fname = fsp->fsp_name;
637 SMB_ASSERT(smb_fname != NULL);
640 * According to MS-FSA a stream name does not have
641 * separate DOS attribute metadata, so we must return
642 * the DOS attribute from the base filename. With one caveat,
643 * a non-default stream name can never be a directory.
645 * As this is common to all streams data stores, we handle
646 * it here instead of inside all stream VFS modules.
648 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
651 if (is_named_stream(smb_fname)) {
652 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
653 dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
656 if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
657 bool compressed = false;
659 status = dos_mode_check_compressed(fsp, &compressed);
660 if (NT_STATUS_IS_OK(status) && compressed) {
661 dosmode |= FILE_ATTRIBUTE_COMPRESSED;
665 dosmode |= dos_mode_from_name(fsp->conn, smb_fname->base_name, dosmode);
667 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
668 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
669 } else if (dosmode == 0) {
670 dosmode = FILE_ATTRIBUTE_NORMAL;
673 dosmode = filter_mode_by_protocol(conn_protocol(fsp->conn->sconn),
674 dosmode);
676 dos_mode_debug_print(func, dosmode);
677 return dosmode;
680 /****************************************************************************
681 Change a unix mode to a dos mode.
682 May also read the create timespec into the stat struct in smb_fname
683 if "store dos attributes" is true.
684 ****************************************************************************/
686 uint32_t fdos_mode(struct files_struct *fsp)
688 uint32_t result = 0;
689 NTSTATUS status = NT_STATUS_OK;
691 DBG_DEBUG("%s\n", fsp_str_dbg(fsp));
693 if (fsp->fake_file_handle != NULL) {
694 return dosmode_from_fake_filehandle(fsp->fake_file_handle);
697 if (!VALID_STAT(fsp->fsp_name->st)) {
698 return 0;
701 if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) {
702 return FILE_ATTRIBUTE_NORMAL;
705 if (fsp->fsp_name->st.cached_dos_attributes != FILE_ATTRIBUTE_INVALID) {
706 return fsp->fsp_name->st.cached_dos_attributes;
709 /* Get the DOS attributes via the VFS if we can */
710 status = SMB_VFS_FGET_DOS_ATTRIBUTES(fsp->conn,
711 metadata_fsp(fsp),
712 &result);
713 if (!NT_STATUS_IS_OK(status)) {
715 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
717 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
718 result |= dos_mode_from_sbuf(fsp->conn,
719 &fsp->fsp_name->st,
720 fsp);
724 fsp->fsp_name->st.cached_dos_attributes = dos_mode_post(result, fsp, __func__);
725 return fsp->fsp_name->st.cached_dos_attributes;
728 struct dos_mode_at_state {
729 files_struct *dir_fsp;
730 struct smb_filename *smb_fname;
731 uint32_t dosmode;
734 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
736 struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
737 struct tevent_context *ev,
738 files_struct *dir_fsp,
739 struct smb_filename *smb_fname)
741 struct tevent_req *req = NULL;
742 struct dos_mode_at_state *state = NULL;
743 struct tevent_req *subreq = NULL;
745 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
747 req = tevent_req_create(mem_ctx, &state,
748 struct dos_mode_at_state);
749 if (req == NULL) {
750 return NULL;
753 *state = (struct dos_mode_at_state) {
754 .dir_fsp = dir_fsp,
755 .smb_fname = smb_fname,
758 if (!VALID_STAT(smb_fname->st)) {
759 tevent_req_done(req);
760 return tevent_req_post(req, ev);
763 if (smb_fname->fsp == NULL) {
764 if (ISDOTDOT(smb_fname->base_name)) {
766 * smb_fname->fsp is explicitly closed
767 * for ".." to prevent meta-data leakage.
769 state->dosmode = FILE_ATTRIBUTE_DIRECTORY;
770 } else {
772 * This is a symlink in POSIX context.
773 * FIXME ? Should we move to returning
774 * FILE_ATTRIBUTE_REPARSE_POINT here ?
776 state->dosmode = FILE_ATTRIBUTE_NORMAL;
778 tevent_req_done(req);
779 return tevent_req_post(req, ev);
782 subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
784 dir_fsp,
785 smb_fname);
786 if (tevent_req_nomem(subreq, req)) {
787 return tevent_req_post(req, ev);
789 tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
791 return req;
794 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
796 struct tevent_req *req =
797 tevent_req_callback_data(subreq,
798 struct tevent_req);
799 struct dos_mode_at_state *state =
800 tevent_req_data(req,
801 struct dos_mode_at_state);
802 struct vfs_aio_state aio_state;
803 NTSTATUS status;
804 bool ok;
807 * Make sure we run as the user again
809 ok = change_to_user_and_service_by_fsp(state->dir_fsp);
810 SMB_ASSERT(ok);
812 status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
813 &aio_state,
814 &state->dosmode);
815 TALLOC_FREE(subreq);
816 if (!NT_STATUS_IS_OK(status)) {
818 * Both the sync dos_mode() as well as the async
819 * dos_mode_at_[send|recv] have no real error return, the only
820 * unhandled error is when the stat info in smb_fname is not
821 * valid (cf the checks in dos_mode() and dos_mode_at_send().
823 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
824 * dos_mode_post() which also does the mapping of a last resort
825 * from S_IFMT(st_mode).
827 * Only if we get NT_STATUS_NOT_IMPLEMENTED or
828 * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
829 * fallback to sync processing.
831 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) &&
832 !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED))
835 * state->dosmode should still be 0, but reset
836 * it to be sure.
838 state->dosmode = 0;
839 status = NT_STATUS_OK;
842 if (NT_STATUS_IS_OK(status)) {
843 state->dosmode = dos_mode_post(state->dosmode,
844 state->smb_fname->fsp,
845 __func__);
846 tevent_req_done(req);
847 return;
851 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
854 state->dosmode = fdos_mode(state->smb_fname->fsp);
855 tevent_req_done(req);
856 return;
859 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
861 struct dos_mode_at_state *state =
862 tevent_req_data(req,
863 struct dos_mode_at_state);
864 NTSTATUS status;
866 if (tevent_req_is_nterror(req, &status)) {
867 tevent_req_received(req);
868 return status;
871 *dosmode = state->dosmode;
872 tevent_req_received(req);
873 return NT_STATUS_OK;
876 /*******************************************************************
877 chmod a file - but preserve some bits.
878 If "store dos attributes" is also set it will store the create time
879 from the stat struct in smb_fname (in NTTIME format) in the EA
880 attribute also.
881 ********************************************************************/
883 int file_set_dosmode(connection_struct *conn,
884 struct smb_filename *smb_fname,
885 uint32_t dosmode,
886 struct smb_filename *parent_dir,
887 bool newfile)
889 int mask=0;
890 mode_t tmp;
891 mode_t unixmode;
892 int ret = -1;
893 NTSTATUS status;
895 if (!CAN_WRITE(conn)) {
896 errno = EROFS;
897 return -1;
900 if (S_ISLNK(smb_fname->st.st_ex_mode)) {
901 /* A symlink in POSIX context, ignore */
902 return 0;
905 if ((S_ISDIR(smb_fname->st.st_ex_mode)) &&
906 (dosmode & FILE_ATTRIBUTE_TEMPORARY))
908 errno = EINVAL;
909 return -1;
912 dosmode &= SAMBA_ATTRIBUTES_MASK;
914 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
915 dosmode, smb_fname_str_dbg(smb_fname)));
917 if (smb_fname->fsp == NULL) {
918 errno = ENOENT;
919 return -1;
922 if ((smb_fname->fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) &&
923 !lp_store_dos_attributes(SNUM(conn)))
925 return 0;
928 unixmode = smb_fname->st.st_ex_mode;
930 get_acl_group_bits(conn, smb_fname->fsp, &smb_fname->st.st_ex_mode);
932 if (S_ISDIR(smb_fname->st.st_ex_mode))
933 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
934 else
935 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
937 /* Store the DOS attributes in an EA by preference. */
938 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn,
939 metadata_fsp(smb_fname->fsp),
940 dosmode);
941 if (NT_STATUS_IS_OK(status)) {
942 smb_fname->st.cached_dos_attributes = dosmode;
943 ret = 0;
944 goto done;
948 * Only fall back to using UNIX modes if
949 * we get NOT_IMPLEMENTED.
951 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
952 errno = map_errno_from_nt_status(status);
953 return -1;
956 /* Fall back to UNIX modes. */
957 unixmode = unix_mode(
958 conn,
959 dosmode,
960 smb_fname,
961 parent_dir != NULL ? parent_dir->fsp : NULL);
963 /* preserve the file type bits */
964 mask |= S_IFMT;
966 /* preserve the s bits */
967 mask |= (S_ISUID | S_ISGID);
969 /* preserve the t bit */
970 #ifdef S_ISVTX
971 mask |= S_ISVTX;
972 #endif
974 /* possibly preserve the x bits */
975 if (!MAP_ARCHIVE(conn))
976 mask |= S_IXUSR;
977 if (!MAP_SYSTEM(conn))
978 mask |= S_IXGRP;
979 if (!MAP_HIDDEN(conn))
980 mask |= S_IXOTH;
982 unixmode |= (smb_fname->st.st_ex_mode & mask);
984 /* if we previously had any r bits set then leave them alone */
985 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
986 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
987 unixmode |= tmp;
990 /* if we previously had any w bits set then leave them alone
991 whilst adding in the new w bits, if the new mode is not rdonly */
992 if (!(dosmode & FILE_ATTRIBUTE_READONLY)) {
993 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
997 * From the chmod 2 man page:
999 * "If the calling process is not privileged, and the group of the file
1000 * does not match the effective group ID of the process or one of its
1001 * supplementary group IDs, the S_ISGID bit will be turned off, but
1002 * this will not cause an error to be returned."
1004 * Simply refuse to do the chmod in this case.
1007 if (S_ISDIR(smb_fname->st.st_ex_mode) &&
1008 (unixmode & S_ISGID) &&
1009 geteuid() != sec_initial_uid() &&
1010 !current_user_in_group(conn, smb_fname->st.st_ex_gid))
1012 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1013 "set for directory %s\n",
1014 smb_fname_str_dbg(smb_fname)));
1015 errno = EPERM;
1016 return -1;
1019 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1020 if (ret == 0) {
1021 goto done;
1024 if((errno != EPERM) && (errno != EACCES))
1025 return -1;
1027 if(!lp_dos_filemode(SNUM(conn)))
1028 return -1;
1030 /* We want DOS semantics, ie allow non owner with write permission to change the
1031 bits on a file. Just like file_ntimes below.
1034 if (!can_write_to_fsp(smb_fname->fsp))
1036 errno = EACCES;
1037 return -1;
1040 become_root();
1041 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1042 unbecome_root();
1044 done:
1045 if (!newfile) {
1046 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1047 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1048 smb_fname->base_name);
1050 if (ret == 0) {
1051 smb_fname->st.st_ex_mode = unixmode;
1054 return( ret );
1058 NTSTATUS file_set_sparse(connection_struct *conn,
1059 files_struct *fsp,
1060 bool sparse)
1062 const struct loadparm_substitution *lp_sub =
1063 loadparm_s3_global_substitution();
1064 uint32_t old_dosmode;
1065 uint32_t new_dosmode;
1066 NTSTATUS status;
1068 if (!CAN_WRITE(conn)) {
1069 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1070 "on readonly share[%s]\n",
1071 smb_fname_str_dbg(fsp->fsp_name),
1072 sparse,
1073 lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
1074 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1078 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1079 * following access flags are granted.
1081 status = check_any_access_fsp(fsp,
1082 FILE_WRITE_DATA
1083 | FILE_WRITE_ATTRIBUTES
1084 | SEC_FILE_APPEND_DATA);
1085 if (!NT_STATUS_IS_OK(status)) {
1086 DBG_DEBUG("fname[%s] set[%u] "
1087 "access_mask[0x%08X] - access denied\n",
1088 smb_fname_str_dbg(fsp->fsp_name),
1089 sparse,
1090 fsp->access_mask);
1091 return status;
1094 if (fsp->fsp_flags.is_directory) {
1095 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1096 (sparse ? "set" : "clear"),
1097 smb_fname_str_dbg(fsp->fsp_name)));
1098 return NT_STATUS_INVALID_PARAMETER;
1101 if (IS_IPC(conn) || IS_PRINT(conn)) {
1102 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1103 (sparse ? "set" : "clear")));
1104 return NT_STATUS_INVALID_PARAMETER;
1107 if (fsp_is_alternate_stream(fsp)) {
1109 * MS-FSA 2.1.1.5 IsSparse
1111 * This is a per stream attribute, but our backends don't
1112 * support it a consistent way, therefore just pretend
1113 * success and ignore the request.
1115 DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
1116 "[%s]\n", fsp_str_dbg(fsp));
1117 return NT_STATUS_OK;
1120 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1121 sparse, smb_fname_str_dbg(fsp->fsp_name)));
1123 if (!lp_store_dos_attributes(SNUM(conn))) {
1124 return NT_STATUS_INVALID_DEVICE_REQUEST;
1127 status = vfs_stat_fsp(fsp);
1128 if (!NT_STATUS_IS_OK(status)) {
1129 return status;
1132 old_dosmode = fdos_mode(fsp);
1134 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1135 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1136 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1137 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1138 } else {
1139 return NT_STATUS_OK;
1142 /* Store the DOS attributes in an EA. */
1143 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1144 if (!NT_STATUS_IS_OK(status)) {
1145 return status;
1148 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1149 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1150 fsp->fsp_name->base_name);
1152 fsp->fsp_name->st.cached_dos_attributes = new_dosmode;
1153 fsp->fsp_flags.is_sparse = sparse;
1155 return NT_STATUS_OK;
1158 /*******************************************************************
1159 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1160 than POSIX.
1161 *******************************************************************/
1163 int file_ntimes(connection_struct *conn,
1164 files_struct *fsp,
1165 struct smb_file_time *ft)
1167 int ret = -1;
1169 errno = 0;
1171 DBG_INFO("actime: %s",
1172 time_to_asc(convert_timespec_to_time_t(ft->atime)));
1173 DBG_INFO("modtime: %s",
1174 time_to_asc(convert_timespec_to_time_t(ft->mtime)));
1175 DBG_INFO("ctime: %s",
1176 time_to_asc(convert_timespec_to_time_t(ft->ctime)));
1177 DBG_INFO("createtime: %s",
1178 time_to_asc(convert_timespec_to_time_t(ft->create_time)));
1180 /* Don't update the time on read-only shares */
1181 /* We need this as set_filetime (which can be called on
1182 close and other paths) can end up calling this function
1183 without the NEED_WRITE protection. Found by :
1184 Leo Weppelman <leo@wau.mis.ah.nl>
1187 if (!CAN_WRITE(conn)) {
1188 return 0;
1191 if (SMB_VFS_FNTIMES(fsp, ft) == 0) {
1192 ret = 0;
1193 goto done;
1196 if((errno != EPERM) && (errno != EACCES)) {
1197 return -1;
1200 if(!lp_dos_filetimes(SNUM(conn))) {
1201 return -1;
1204 /* We have permission (given by the Samba admin) to
1205 break POSIX semantics and allow a user to change
1206 the time on a file they don't own but can write to
1207 (as DOS does).
1210 /* Check if we have write access. */
1211 if (can_write_to_fsp(fsp)) {
1212 /* We are allowed to become root and change the filetime. */
1213 become_root();
1214 ret = SMB_VFS_FNTIMES(fsp, ft);
1215 unbecome_root();
1218 done:
1219 if (ret == 0) {
1220 copy_stat_ex_timestamps(fsp, ft);
1223 return ret;
1226 /******************************************************************
1227 Force a "sticky" write time on a pathname. This will always be
1228 returned on all future write time queries and set on close.
1229 ******************************************************************/
1231 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1233 if (is_omit_timespec(&mtime)) {
1234 return true;
1237 if (!set_sticky_write_time(fileid, mtime)) {
1238 return false;
1241 return true;
1244 /******************************************************************
1245 Force a "sticky" write time on an fsp. This will always be
1246 returned on all future write time queries and set on close.
1247 ******************************************************************/
1249 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1251 if (is_omit_timespec(&mtime)) {
1252 return true;
1255 fsp->fsp_flags.write_time_forced = true;
1256 TALLOC_FREE(fsp->update_write_time_event);
1258 return set_sticky_write_time_path(fsp->file_id, mtime);
1261 /******************************************************************
1262 Set a create time EA.
1263 ******************************************************************/
1265 NTSTATUS set_create_timespec_ea(struct files_struct *fsp,
1266 struct timespec create_time)
1268 uint32_t dosmode;
1269 int ret;
1271 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
1272 return NT_STATUS_OK;
1275 dosmode = fdos_mode(fsp);
1277 fsp->fsp_name->st.st_ex_btime = create_time;
1278 ret = file_set_dosmode(fsp->conn, fsp->fsp_name, dosmode, NULL, false);
1279 if (ret == -1) {
1280 return map_nt_error_from_unix(errno);
1283 DBG_DEBUG("wrote create time EA for file %s\n",
1284 smb_fname_str_dbg(fsp->fsp_name));
1286 return NT_STATUS_OK;
1289 /******************************************************************
1290 Return a create time.
1291 ******************************************************************/
1293 struct timespec get_create_timespec(connection_struct *conn,
1294 struct files_struct *fsp,
1295 const struct smb_filename *smb_fname)
1297 if (fsp != NULL) {
1298 struct files_struct *meta_fsp = metadata_fsp(fsp);
1299 return meta_fsp->fsp_name->st.st_ex_btime;
1301 return smb_fname->st.st_ex_btime;
1304 /******************************************************************
1305 Return a change time (may look at EA in future).
1306 ******************************************************************/
1308 struct timespec get_change_timespec(connection_struct *conn,
1309 struct files_struct *fsp,
1310 const struct smb_filename *smb_fname)
1312 return smb_fname->st.st_ex_mtime;