auth/credentials: don't ignore "client use kerberos" and --use-kerberos for machine...
[Samba.git] / source3 / smbd / dosmode.c
blobfac226459cef2acc1b775af99600f7aae1e0a3d0
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;
359 * _SPARSE and _REPARSE_POINT are valid on get but not on
360 * set. Both are created via special fcntls.
363 dosattr &= (SAMBA_ATTRIBUTES_MASK|
364 FILE_ATTRIBUTE_SPARSE|
365 FILE_ATTRIBUTE_REPARSE_POINT);
367 *pattr |= dosattr;
369 dos_mode_debug_print(__func__, *pattr);
371 return NT_STATUS_OK;
374 NTSTATUS fget_ea_dos_attribute(struct files_struct *fsp,
375 uint32_t *pattr)
377 DATA_BLOB blob;
378 ssize_t sizeret;
379 fstring attrstr;
380 NTSTATUS status;
382 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
383 return NT_STATUS_NOT_IMPLEMENTED;
386 /* Don't reset pattr to zero as we may already have filename-based attributes we
387 need to preserve. */
389 sizeret = SMB_VFS_FGETXATTR(fsp,
390 SAMBA_XATTR_DOS_ATTRIB,
391 attrstr,
392 sizeof(attrstr));
393 if (sizeret == -1 && ( errno == EPERM || errno == EACCES )) {
394 /* we may also retrieve dos attribs for unreadable files, this
395 is why we'll retry as root. We don't use root in the first
396 run because in cases like NFS, root might have even less
397 rights than the real user
399 become_root();
400 sizeret = SMB_VFS_FGETXATTR(fsp,
401 SAMBA_XATTR_DOS_ATTRIB,
402 attrstr,
403 sizeof(attrstr));
404 unbecome_root();
406 if (sizeret == -1) {
407 DBG_INFO("Cannot get attribute "
408 "from EA on file %s: Error = %s\n",
409 fsp_str_dbg(fsp), strerror(errno));
410 return map_nt_error_from_unix(errno);
413 blob.data = (uint8_t *)attrstr;
414 blob.length = sizeret;
416 status = parse_dos_attribute_blob(fsp->fsp_name, blob, pattr);
417 if (!NT_STATUS_IS_OK(status)) {
418 return status;
421 return NT_STATUS_OK;
424 /****************************************************************************
425 Set DOS attributes in an EA.
426 Also sets the create time.
427 ****************************************************************************/
429 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
430 struct smb_filename *smb_fname,
431 uint32_t dosmode)
433 struct xattr_DOSATTRIB dosattrib = { .version = 0, };
434 enum ndr_err_code ndr_err;
435 DATA_BLOB blob = { .data = NULL, };
436 struct timespec btime;
437 int ret;
439 if (!lp_store_dos_attributes(SNUM(conn))) {
440 return NT_STATUS_NOT_IMPLEMENTED;
443 if (smb_fname->fsp == NULL) {
444 /* symlink */
445 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
448 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
449 * vfs_default via DMAPI if that is enabled.
451 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
453 dosattrib.version = 5;
454 dosattrib.info.info5.valid_flags = XATTR_DOSINFO_ATTRIB |
455 XATTR_DOSINFO_CREATE_TIME;
456 dosattrib.info.info5.attrib = dosmode;
457 dosattrib.info.info5.create_time = full_timespec_to_nt_time(
458 &smb_fname->st.st_ex_btime);
460 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
461 (unsigned int)dosmode,
462 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
463 smb_fname_str_dbg(smb_fname) ));
465 ndr_err = ndr_push_struct_blob(
466 &blob, talloc_tos(), &dosattrib,
467 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
469 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
470 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
471 ndr_errstr(ndr_err)));
472 return ndr_map_error2ntstatus(ndr_err);
475 if (blob.data == NULL || blob.length == 0) {
476 /* Should this be INTERNAL_ERROR? */
477 return NT_STATUS_INVALID_PARAMETER;
480 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
481 SAMBA_XATTR_DOS_ATTRIB,
482 blob.data, blob.length, 0);
483 if (ret != 0) {
484 NTSTATUS status = NT_STATUS_OK;
485 bool set_dosmode_ok = false;
487 if ((errno != EPERM) && (errno != EACCES)) {
488 DBG_INFO("Cannot set "
489 "attribute EA on file %s: Error = %s\n",
490 smb_fname_str_dbg(smb_fname), strerror(errno));
491 return map_nt_error_from_unix(errno);
494 /* We want DOS semantics, ie allow non owner with write permission to change the
495 bits on a file. Just like file_ntimes below.
498 /* Check if we have write access. */
499 if (!CAN_WRITE(conn)) {
500 return NT_STATUS_ACCESS_DENIED;
503 status = smbd_check_access_rights_fsp(conn->cwd_fsp,
504 smb_fname->fsp,
505 false,
506 FILE_WRITE_ATTRIBUTES);
507 if (NT_STATUS_IS_OK(status)) {
508 set_dosmode_ok = true;
511 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
512 set_dosmode_ok = can_write_to_fsp(smb_fname->fsp);
515 if (!set_dosmode_ok) {
516 return NT_STATUS_ACCESS_DENIED;
519 become_root();
520 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
521 SAMBA_XATTR_DOS_ATTRIB,
522 blob.data, blob.length, 0);
523 if (ret == 0) {
524 status = NT_STATUS_OK;
526 unbecome_root();
527 if (!NT_STATUS_IS_OK(status)) {
528 return status;
533 * We correctly stored the create time.
534 * We *always* set XATTR_DOSINFO_CREATE_TIME,
535 * so now it can no longer be considered
536 * calculated. Make sure to use the value rounded
537 * to NTTIME granularity we've stored in the xattr.
539 btime = nt_time_to_full_timespec(dosattrib.info.info5.create_time);
540 update_stat_ex_create_time(&smb_fname->st, btime);
542 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
543 (unsigned int)dosmode,
544 smb_fname_str_dbg(smb_fname)));
545 return NT_STATUS_OK;
548 static uint32_t
549 dos_mode_from_name(connection_struct *conn, const char *name, uint32_t dosmode)
551 const char *p = NULL;
552 uint32_t result = dosmode;
554 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
555 lp_hide_dot_files(SNUM(conn)))
557 p = strrchr_m(name, '/');
558 if (p) {
559 p++;
560 } else {
561 p = name;
564 /* Only . and .. are not hidden. */
565 if ((p[0] == '.') && !(ISDOT(p) || ISDOTDOT(p))) {
566 result |= FILE_ATTRIBUTE_HIDDEN;
570 if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, name)) {
571 result |= FILE_ATTRIBUTE_HIDDEN;
574 return result;
577 /****************************************************************************
578 Change a unix mode to a dos mode for an ms dfs link.
579 ****************************************************************************/
581 uint32_t dos_mode_msdfs(connection_struct *conn,
582 const char *name,
583 const struct stat_ex *st)
585 uint32_t result = 0;
587 DEBUG(8, ("dos_mode_msdfs: %s\n", name));
589 if (!VALID_STAT(*st)) {
590 return 0;
593 result = dos_mode_from_name(conn, name, result);
594 result |= dos_mode_from_sbuf(conn, st, NULL);
596 if (result == 0) {
597 result = FILE_ATTRIBUTE_NORMAL;
600 result = filter_mode_by_protocol(conn_protocol(conn->sconn), result);
603 * Add in that it is a reparse point
605 result |= FILE_ATTRIBUTE_REPARSE_POINT;
607 dos_mode_debug_print(__func__, result);
609 return(result);
613 * check whether a file or directory is flagged as compressed.
615 static NTSTATUS dos_mode_check_compressed(struct files_struct *fsp,
616 bool *is_compressed)
618 NTSTATUS status;
619 uint16_t compression_fmt;
621 status = SMB_VFS_FGET_COMPRESSION(
622 fsp->conn, talloc_tos(), fsp, &compression_fmt);
623 if (!NT_STATUS_IS_OK(status)) {
624 return status;
627 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
628 *is_compressed = true;
629 } else {
630 *is_compressed = false;
632 return NT_STATUS_OK;
635 static uint32_t dos_mode_post(uint32_t dosmode,
636 struct files_struct *fsp,
637 const char *func)
639 struct smb_filename *smb_fname = NULL;
640 NTSTATUS status;
642 if (fsp != NULL) {
643 smb_fname = fsp->fsp_name;
645 SMB_ASSERT(smb_fname != NULL);
648 * According to MS-FSA a stream name does not have
649 * separate DOS attribute metadata, so we must return
650 * the DOS attribute from the base filename. With one caveat,
651 * a non-default stream name can never be a directory.
653 * As this is common to all streams data stores, we handle
654 * it here instead of inside all stream VFS modules.
656 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
659 if (is_named_stream(smb_fname)) {
660 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
661 dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
664 if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
665 bool compressed = false;
667 status = dos_mode_check_compressed(fsp, &compressed);
668 if (NT_STATUS_IS_OK(status) && compressed) {
669 dosmode |= FILE_ATTRIBUTE_COMPRESSED;
673 dosmode |= dos_mode_from_name(fsp->conn, smb_fname->base_name, dosmode);
675 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
676 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
677 } else if (dosmode == 0) {
678 dosmode = FILE_ATTRIBUTE_NORMAL;
681 dosmode = filter_mode_by_protocol(conn_protocol(fsp->conn->sconn),
682 dosmode);
684 dos_mode_debug_print(func, dosmode);
685 return dosmode;
688 /****************************************************************************
689 Change a unix mode to a dos mode.
690 May also read the create timespec into the stat struct in smb_fname
691 if "store dos attributes" is true.
692 ****************************************************************************/
694 uint32_t fdos_mode(struct files_struct *fsp)
696 uint32_t result = 0;
697 NTSTATUS status = NT_STATUS_OK;
699 DBG_DEBUG("%s\n", fsp_str_dbg(fsp));
701 if (fsp->fake_file_handle != NULL) {
702 return dosmode_from_fake_filehandle(fsp->fake_file_handle);
705 if (!VALID_STAT(fsp->fsp_name->st)) {
706 return 0;
709 if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) {
710 return FILE_ATTRIBUTE_NORMAL;
713 if (fsp->fsp_name->st.cached_dos_attributes != FILE_ATTRIBUTE_INVALID) {
714 return fsp->fsp_name->st.cached_dos_attributes;
717 /* Get the DOS attributes via the VFS if we can */
718 status = SMB_VFS_FGET_DOS_ATTRIBUTES(fsp->conn,
719 metadata_fsp(fsp),
720 &result);
721 if (!NT_STATUS_IS_OK(status)) {
723 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
725 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
726 result |= dos_mode_from_sbuf(fsp->conn,
727 &fsp->fsp_name->st,
728 fsp);
732 fsp->fsp_name->st.cached_dos_attributes = dos_mode_post(result, fsp, __func__);
733 return fsp->fsp_name->st.cached_dos_attributes;
736 struct dos_mode_at_state {
737 files_struct *dir_fsp;
738 struct smb_filename *smb_fname;
739 uint32_t dosmode;
742 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
744 struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
745 struct tevent_context *ev,
746 files_struct *dir_fsp,
747 struct smb_filename *smb_fname)
749 struct tevent_req *req = NULL;
750 struct dos_mode_at_state *state = NULL;
751 struct tevent_req *subreq = NULL;
753 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
755 req = tevent_req_create(mem_ctx, &state,
756 struct dos_mode_at_state);
757 if (req == NULL) {
758 return NULL;
761 *state = (struct dos_mode_at_state) {
762 .dir_fsp = dir_fsp,
763 .smb_fname = smb_fname,
766 if (!VALID_STAT(smb_fname->st)) {
767 tevent_req_done(req);
768 return tevent_req_post(req, ev);
771 if (smb_fname->fsp == NULL) {
772 if (ISDOTDOT(smb_fname->base_name)) {
774 * smb_fname->fsp is explicitly closed
775 * for ".." to prevent meta-data leakage.
777 state->dosmode = FILE_ATTRIBUTE_DIRECTORY;
778 } else {
780 * This is a symlink in POSIX context.
781 * FIXME ? Should we move to returning
782 * FILE_ATTRIBUTE_REPARSE_POINT here ?
784 state->dosmode = FILE_ATTRIBUTE_NORMAL;
786 tevent_req_done(req);
787 return tevent_req_post(req, ev);
790 subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
792 dir_fsp,
793 smb_fname);
794 if (tevent_req_nomem(subreq, req)) {
795 return tevent_req_post(req, ev);
797 tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
799 return req;
802 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
804 struct tevent_req *req =
805 tevent_req_callback_data(subreq,
806 struct tevent_req);
807 struct dos_mode_at_state *state =
808 tevent_req_data(req,
809 struct dos_mode_at_state);
810 struct vfs_aio_state aio_state;
811 NTSTATUS status;
812 bool ok;
815 * Make sure we run as the user again
817 ok = change_to_user_and_service_by_fsp(state->dir_fsp);
818 SMB_ASSERT(ok);
820 status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
821 &aio_state,
822 &state->dosmode);
823 TALLOC_FREE(subreq);
824 if (!NT_STATUS_IS_OK(status)) {
826 * Both the sync dos_mode() as well as the async
827 * dos_mode_at_[send|recv] have no real error return, the only
828 * unhandled error is when the stat info in smb_fname is not
829 * valid (cf the checks in dos_mode() and dos_mode_at_send().
831 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
832 * dos_mode_post() which also does the mapping of a last resort
833 * from S_IFMT(st_mode).
835 * Only if we get NT_STATUS_NOT_IMPLEMENTED or
836 * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
837 * fallback to sync processing.
839 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) &&
840 !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED))
843 * state->dosmode should still be 0, but reset
844 * it to be sure.
846 state->dosmode = 0;
847 status = NT_STATUS_OK;
850 if (NT_STATUS_IS_OK(status)) {
851 state->dosmode = dos_mode_post(state->dosmode,
852 state->smb_fname->fsp,
853 __func__);
854 tevent_req_done(req);
855 return;
859 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
862 state->dosmode = fdos_mode(state->smb_fname->fsp);
863 tevent_req_done(req);
864 return;
867 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
869 struct dos_mode_at_state *state =
870 tevent_req_data(req,
871 struct dos_mode_at_state);
872 NTSTATUS status;
874 if (tevent_req_is_nterror(req, &status)) {
875 tevent_req_received(req);
876 return status;
879 *dosmode = state->dosmode;
880 tevent_req_received(req);
881 return NT_STATUS_OK;
884 /*******************************************************************
885 chmod a file - but preserve some bits.
886 If "store dos attributes" is also set it will store the create time
887 from the stat struct in smb_fname (in NTTIME format) in the EA
888 attribute also.
889 ********************************************************************/
891 int file_set_dosmode(connection_struct *conn,
892 struct smb_filename *smb_fname,
893 uint32_t dosmode,
894 struct smb_filename *parent_dir,
895 bool newfile)
897 int mask=0;
898 mode_t tmp;
899 mode_t unixmode;
900 int ret = -1;
901 NTSTATUS status;
903 if (!CAN_WRITE(conn)) {
904 errno = EROFS;
905 return -1;
908 if (S_ISLNK(smb_fname->st.st_ex_mode)) {
909 /* A symlink in POSIX context, ignore */
910 return 0;
913 if ((S_ISDIR(smb_fname->st.st_ex_mode)) &&
914 (dosmode & FILE_ATTRIBUTE_TEMPORARY))
916 errno = EINVAL;
917 return -1;
920 dosmode &= SAMBA_ATTRIBUTES_MASK;
922 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
923 dosmode, smb_fname_str_dbg(smb_fname)));
925 if (smb_fname->fsp == NULL) {
926 errno = ENOENT;
927 return -1;
930 if ((smb_fname->fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) &&
931 !lp_store_dos_attributes(SNUM(conn)))
933 return 0;
936 unixmode = smb_fname->st.st_ex_mode;
938 get_acl_group_bits(conn, smb_fname->fsp, &smb_fname->st.st_ex_mode);
940 if (S_ISDIR(smb_fname->st.st_ex_mode))
941 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
942 else
943 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
945 /* Store the DOS attributes in an EA by preference. */
946 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn,
947 metadata_fsp(smb_fname->fsp),
948 dosmode);
949 if (NT_STATUS_IS_OK(status)) {
950 smb_fname->st.cached_dos_attributes = dosmode;
951 ret = 0;
952 goto done;
956 * Only fall back to using UNIX modes if
957 * we get NOT_IMPLEMENTED.
959 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
960 errno = map_errno_from_nt_status(status);
961 return -1;
964 /* Fall back to UNIX modes. */
965 unixmode = unix_mode(
966 conn,
967 dosmode,
968 smb_fname,
969 parent_dir != NULL ? parent_dir->fsp : NULL);
971 /* preserve the file type bits */
972 mask |= S_IFMT;
974 /* preserve the s bits */
975 mask |= (S_ISUID | S_ISGID);
977 /* preserve the t bit */
978 #ifdef S_ISVTX
979 mask |= S_ISVTX;
980 #endif
982 /* possibly preserve the x bits */
983 if (!MAP_ARCHIVE(conn))
984 mask |= S_IXUSR;
985 if (!MAP_SYSTEM(conn))
986 mask |= S_IXGRP;
987 if (!MAP_HIDDEN(conn))
988 mask |= S_IXOTH;
990 unixmode |= (smb_fname->st.st_ex_mode & mask);
992 /* if we previously had any r bits set then leave them alone */
993 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
994 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
995 unixmode |= tmp;
998 /* if we previously had any w bits set then leave them alone
999 whilst adding in the new w bits, if the new mode is not rdonly */
1000 if (!(dosmode & FILE_ATTRIBUTE_READONLY)) {
1001 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
1005 * From the chmod 2 man page:
1007 * "If the calling process is not privileged, and the group of the file
1008 * does not match the effective group ID of the process or one of its
1009 * supplementary group IDs, the S_ISGID bit will be turned off, but
1010 * this will not cause an error to be returned."
1012 * Simply refuse to do the chmod in this case.
1015 if (S_ISDIR(smb_fname->st.st_ex_mode) &&
1016 (unixmode & S_ISGID) &&
1017 geteuid() != sec_initial_uid() &&
1018 !current_user_in_group(conn, smb_fname->st.st_ex_gid))
1020 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1021 "set for directory %s\n",
1022 smb_fname_str_dbg(smb_fname)));
1023 errno = EPERM;
1024 return -1;
1027 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1028 if (ret == 0) {
1029 goto done;
1032 if((errno != EPERM) && (errno != EACCES))
1033 return -1;
1035 if(!lp_dos_filemode(SNUM(conn)))
1036 return -1;
1038 /* We want DOS semantics, ie allow non owner with write permission to change the
1039 bits on a file. Just like file_ntimes below.
1042 if (!can_write_to_fsp(smb_fname->fsp))
1044 errno = EACCES;
1045 return -1;
1048 become_root();
1049 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1050 unbecome_root();
1052 done:
1053 if (!newfile) {
1054 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1055 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1056 smb_fname->base_name);
1058 if (ret == 0) {
1059 smb_fname->st.st_ex_mode = unixmode;
1062 return( ret );
1066 NTSTATUS file_set_sparse(connection_struct *conn,
1067 files_struct *fsp,
1068 bool sparse)
1070 const struct loadparm_substitution *lp_sub =
1071 loadparm_s3_global_substitution();
1072 uint32_t old_dosmode;
1073 uint32_t new_dosmode;
1074 NTSTATUS status;
1076 if (!CAN_WRITE(conn)) {
1077 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1078 "on readonly share[%s]\n",
1079 smb_fname_str_dbg(fsp->fsp_name),
1080 sparse,
1081 lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
1082 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1086 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1087 * following access flags are granted.
1089 status = check_any_access_fsp(fsp,
1090 FILE_WRITE_DATA
1091 | FILE_WRITE_ATTRIBUTES
1092 | SEC_FILE_APPEND_DATA);
1093 if (!NT_STATUS_IS_OK(status)) {
1094 DBG_DEBUG("fname[%s] set[%u] "
1095 "access_mask[0x%08X] - access denied\n",
1096 smb_fname_str_dbg(fsp->fsp_name),
1097 sparse,
1098 fsp->access_mask);
1099 return status;
1102 if (fsp->fsp_flags.is_directory) {
1103 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1104 (sparse ? "set" : "clear"),
1105 smb_fname_str_dbg(fsp->fsp_name)));
1106 return NT_STATUS_INVALID_PARAMETER;
1109 if (IS_IPC(conn) || IS_PRINT(conn)) {
1110 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1111 (sparse ? "set" : "clear")));
1112 return NT_STATUS_INVALID_PARAMETER;
1115 if (fsp_is_alternate_stream(fsp)) {
1117 * MS-FSA 2.1.1.5 IsSparse
1119 * This is a per stream attribute, but our backends don't
1120 * support it a consistent way, therefore just pretend
1121 * success and ignore the request.
1123 DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
1124 "[%s]\n", fsp_str_dbg(fsp));
1125 return NT_STATUS_OK;
1128 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1129 sparse, smb_fname_str_dbg(fsp->fsp_name)));
1131 if (!lp_store_dos_attributes(SNUM(conn))) {
1132 return NT_STATUS_INVALID_DEVICE_REQUEST;
1135 status = vfs_stat_fsp(fsp);
1136 if (!NT_STATUS_IS_OK(status)) {
1137 return status;
1140 old_dosmode = fdos_mode(fsp);
1142 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1143 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1144 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1145 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1146 } else {
1147 return NT_STATUS_OK;
1150 /* Store the DOS attributes in an EA. */
1151 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1152 if (!NT_STATUS_IS_OK(status)) {
1153 return status;
1156 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1157 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1158 fsp->fsp_name->base_name);
1160 fsp->fsp_name->st.cached_dos_attributes = new_dosmode;
1161 fsp->fsp_flags.is_sparse = sparse;
1163 return NT_STATUS_OK;
1166 /*******************************************************************
1167 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1168 than POSIX.
1169 *******************************************************************/
1171 int file_ntimes(connection_struct *conn,
1172 files_struct *fsp,
1173 struct smb_file_time *ft)
1175 int ret = -1;
1177 errno = 0;
1179 DBG_INFO("actime: %s",
1180 time_to_asc(convert_timespec_to_time_t(ft->atime)));
1181 DBG_INFO("modtime: %s",
1182 time_to_asc(convert_timespec_to_time_t(ft->mtime)));
1183 DBG_INFO("ctime: %s",
1184 time_to_asc(convert_timespec_to_time_t(ft->ctime)));
1185 DBG_INFO("createtime: %s",
1186 time_to_asc(convert_timespec_to_time_t(ft->create_time)));
1188 /* Don't update the time on read-only shares */
1189 /* We need this as set_filetime (which can be called on
1190 close and other paths) can end up calling this function
1191 without the NEED_WRITE protection. Found by :
1192 Leo Weppelman <leo@wau.mis.ah.nl>
1195 if (!CAN_WRITE(conn)) {
1196 return 0;
1199 if (SMB_VFS_FNTIMES(fsp, ft) == 0) {
1200 ret = 0;
1201 goto done;
1204 if((errno != EPERM) && (errno != EACCES)) {
1205 return -1;
1208 if(!lp_dos_filetimes(SNUM(conn))) {
1209 return -1;
1212 /* We have permission (given by the Samba admin) to
1213 break POSIX semantics and allow a user to change
1214 the time on a file they don't own but can write to
1215 (as DOS does).
1218 /* Check if we have write access. */
1219 if (can_write_to_fsp(fsp)) {
1220 /* We are allowed to become root and change the filetime. */
1221 become_root();
1222 ret = SMB_VFS_FNTIMES(fsp, ft);
1223 unbecome_root();
1226 done:
1227 if (ret == 0) {
1228 copy_stat_ex_timestamps(fsp, ft);
1231 return ret;
1234 /******************************************************************
1235 Force a "sticky" write time on a pathname. This will always be
1236 returned on all future write time queries and set on close.
1237 ******************************************************************/
1239 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1241 if (is_omit_timespec(&mtime)) {
1242 return true;
1245 if (!set_sticky_write_time(fileid, mtime)) {
1246 return false;
1249 return true;
1252 /******************************************************************
1253 Force a "sticky" write time on an fsp. This will always be
1254 returned on all future write time queries and set on close.
1255 ******************************************************************/
1257 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1259 if (is_omit_timespec(&mtime)) {
1260 return true;
1263 fsp->fsp_flags.write_time_forced = true;
1264 TALLOC_FREE(fsp->update_write_time_event);
1266 return set_sticky_write_time_path(fsp->file_id, mtime);
1269 /******************************************************************
1270 Set a create time EA.
1271 ******************************************************************/
1273 NTSTATUS set_create_timespec_ea(struct files_struct *fsp,
1274 struct timespec create_time)
1276 uint32_t dosmode;
1277 int ret;
1279 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
1280 return NT_STATUS_OK;
1283 dosmode = fdos_mode(fsp);
1285 fsp->fsp_name->st.st_ex_btime = create_time;
1286 ret = file_set_dosmode(fsp->conn, fsp->fsp_name, dosmode, NULL, false);
1287 if (ret == -1) {
1288 return map_nt_error_from_unix(errno);
1291 DBG_DEBUG("wrote create time EA for file %s\n",
1292 smb_fname_str_dbg(fsp->fsp_name));
1294 return NT_STATUS_OK;
1297 /******************************************************************
1298 Return a create time.
1299 ******************************************************************/
1301 struct timespec get_create_timespec(connection_struct *conn,
1302 struct files_struct *fsp,
1303 const struct smb_filename *smb_fname)
1305 if (fsp != NULL) {
1306 struct files_struct *meta_fsp = metadata_fsp(fsp);
1307 return meta_fsp->fsp_name->st.st_ex_btime;
1309 return smb_fname->st.st_ex_btime;
1312 /******************************************************************
1313 Return a change time (may look at EA in future).
1314 ******************************************************************/
1316 struct timespec get_change_timespec(connection_struct *conn,
1317 struct files_struct *fsp,
1318 const struct smb_filename *smb_fname)
1320 return smb_fname->st.st_ex_mtime;