s4:dsdb/extended_dn_out: use dsdb_dn_val_rmd_flags() instead of dsdb_dn_is_deleted_val()
[Samba.git] / source3 / smbd / dosmode.c
blob5a88cd059b040a7f3fb53fb9b6cda12d0e7d5c6c
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 smb_filename *smb_fname)
194 int result = 0;
195 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
197 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
198 /* if we can find out if a file is immutable we should report it r/o */
199 if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
200 result |= FILE_ATTRIBUTE_READONLY;
202 #endif
203 if (ro_opts == MAP_READONLY_YES) {
204 /* Original Samba method - map inverse of user "w" bit. */
205 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
206 result |= FILE_ATTRIBUTE_READONLY;
208 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
209 /* smb_fname->fsp can be NULL for an MS-DFS link. */
210 /* Check actual permissions for read-only. */
211 if (smb_fname->fsp != NULL) {
212 if (!can_write_to_fsp(smb_fname->fsp))
214 result |= FILE_ATTRIBUTE_READONLY;
217 } /* Else never set the readonly bit. */
219 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
220 result |= FILE_ATTRIBUTE_ARCHIVE;
222 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
223 result |= FILE_ATTRIBUTE_SYSTEM;
225 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
226 result |= FILE_ATTRIBUTE_HIDDEN;
228 if (S_ISDIR(smb_fname->st.st_ex_mode))
229 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
231 dos_mode_debug_print(__func__, result);
233 return result;
236 /****************************************************************************
237 Get DOS attributes from an EA.
238 This can also pull the create time into the stat struct inside smb_fname.
239 ****************************************************************************/
241 NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
242 DATA_BLOB blob,
243 uint32_t *pattr)
245 struct xattr_DOSATTRIB dosattrib;
246 enum ndr_err_code ndr_err;
247 uint32_t dosattr;
249 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
250 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
252 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
253 DBG_WARNING("bad ndr decode "
254 "from EA on file %s: Error = %s\n",
255 smb_fname_str_dbg(smb_fname),
256 ndr_errstr(ndr_err));
257 return ndr_map_error2ntstatus(ndr_err);
260 DBG_DEBUG("%s attr = %s\n",
261 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex);
263 switch (dosattrib.version) {
264 case 0xFFFF:
265 dosattr = dosattrib.info.compatinfoFFFF.attrib;
266 break;
267 case 1:
268 dosattr = dosattrib.info.info1.attrib;
269 if (!null_nttime(dosattrib.info.info1.create_time)) {
270 struct timespec create_time =
271 nt_time_to_unix_timespec(
272 dosattrib.info.info1.create_time);
274 update_stat_ex_create_time(&smb_fname->st,
275 create_time);
277 DBG_DEBUG("file %s case 1 set btime %s\n",
278 smb_fname_str_dbg(smb_fname),
279 time_to_asc(convert_timespec_to_time_t(
280 create_time)));
282 break;
283 case 2:
284 dosattr = dosattrib.info.oldinfo2.attrib;
285 /* Don't know what flags to check for this case. */
286 break;
287 case 3:
288 dosattr = dosattrib.info.info3.attrib;
289 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
290 !null_nttime(dosattrib.info.info3.create_time)) {
291 struct timespec create_time =
292 nt_time_to_full_timespec(
293 dosattrib.info.info3.create_time);
295 update_stat_ex_create_time(&smb_fname->st,
296 create_time);
298 DBG_DEBUG("file %s case 3 set btime %s\n",
299 smb_fname_str_dbg(smb_fname),
300 time_to_asc(convert_timespec_to_time_t(
301 create_time)));
303 break;
304 case 4:
305 case 5:
307 uint32_t info_valid_flags;
308 NTTIME info_create_time;
310 if (dosattrib.version == 4) {
311 info_valid_flags = dosattrib.info.info4.valid_flags;
312 info_create_time = dosattrib.info.info4.create_time;
313 dosattr = dosattrib.info.info4.attrib;
314 } else {
315 info_valid_flags = dosattrib.info.info5.valid_flags;
316 info_create_time = dosattrib.info.info5.create_time;
317 dosattr = dosattrib.info.info5.attrib;
320 if ((info_valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
321 !null_nttime(info_create_time))
323 struct timespec creat_time;
325 creat_time = nt_time_to_full_timespec(info_create_time);
326 update_stat_ex_create_time(&smb_fname->st, creat_time);
328 DBG_DEBUG("file [%s] creation time [%s]\n",
329 smb_fname_str_dbg(smb_fname),
330 nt_time_string(talloc_tos(), info_create_time));
333 break;
335 default:
336 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
337 smb_fname_str_dbg(smb_fname), blob.data);
338 /* Should this be INTERNAL_ERROR? */
339 return NT_STATUS_INVALID_PARAMETER;
342 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
343 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
346 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
347 *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
349 dos_mode_debug_print(__func__, *pattr);
351 return NT_STATUS_OK;
354 NTSTATUS fget_ea_dos_attribute(struct files_struct *fsp,
355 uint32_t *pattr)
357 DATA_BLOB blob;
358 ssize_t sizeret;
359 fstring attrstr;
360 NTSTATUS status;
362 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
363 return NT_STATUS_NOT_IMPLEMENTED;
366 /* Don't reset pattr to zero as we may already have filename-based attributes we
367 need to preserve. */
369 sizeret = SMB_VFS_FGETXATTR(fsp,
370 SAMBA_XATTR_DOS_ATTRIB,
371 attrstr,
372 sizeof(attrstr));
373 if (sizeret == -1 && ( errno == EPERM || errno == EACCES )) {
374 /* we may also retrieve dos attribs for unreadable files, this
375 is why we'll retry as root. We don't use root in the first
376 run because in cases like NFS, root might have even less
377 rights than the real user
379 become_root();
380 sizeret = SMB_VFS_FGETXATTR(fsp,
381 SAMBA_XATTR_DOS_ATTRIB,
382 attrstr,
383 sizeof(attrstr));
384 unbecome_root();
386 if (sizeret == -1) {
387 DBG_INFO("Cannot get attribute "
388 "from EA on file %s: Error = %s\n",
389 fsp_str_dbg(fsp), strerror(errno));
390 return map_nt_error_from_unix(errno);
393 blob.data = (uint8_t *)attrstr;
394 blob.length = sizeret;
396 status = parse_dos_attribute_blob(fsp->fsp_name, blob, pattr);
397 if (!NT_STATUS_IS_OK(status)) {
398 return status;
401 return NT_STATUS_OK;
404 /****************************************************************************
405 Set DOS attributes in an EA.
406 Also sets the create time.
407 ****************************************************************************/
409 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
410 struct smb_filename *smb_fname,
411 uint32_t dosmode)
413 struct xattr_DOSATTRIB dosattrib = { .version = 0, };
414 enum ndr_err_code ndr_err;
415 DATA_BLOB blob = { .data = NULL, };
416 struct timespec btime;
417 int ret;
419 if (!lp_store_dos_attributes(SNUM(conn))) {
420 return NT_STATUS_NOT_IMPLEMENTED;
423 if (smb_fname->fsp == NULL) {
424 /* symlink */
425 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
428 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
429 * vfs_default via DMAPI if that is enabled.
431 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
433 dosattrib.version = 5;
434 dosattrib.info.info5.valid_flags = XATTR_DOSINFO_ATTRIB |
435 XATTR_DOSINFO_CREATE_TIME;
436 dosattrib.info.info5.attrib = dosmode;
437 dosattrib.info.info5.create_time = full_timespec_to_nt_time(
438 &smb_fname->st.st_ex_btime);
440 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
441 (unsigned int)dosmode,
442 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
443 smb_fname_str_dbg(smb_fname) ));
445 ndr_err = ndr_push_struct_blob(
446 &blob, talloc_tos(), &dosattrib,
447 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
449 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
450 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
451 ndr_errstr(ndr_err)));
452 return ndr_map_error2ntstatus(ndr_err);
455 if (blob.data == NULL || blob.length == 0) {
456 /* Should this be INTERNAL_ERROR? */
457 return NT_STATUS_INVALID_PARAMETER;
460 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
461 SAMBA_XATTR_DOS_ATTRIB,
462 blob.data, blob.length, 0);
463 if (ret != 0) {
464 NTSTATUS status = NT_STATUS_OK;
465 bool set_dosmode_ok = false;
467 if ((errno != EPERM) && (errno != EACCES)) {
468 DBG_INFO("Cannot set "
469 "attribute EA on file %s: Error = %s\n",
470 smb_fname_str_dbg(smb_fname), strerror(errno));
471 return map_nt_error_from_unix(errno);
474 /* We want DOS semantics, ie allow non owner with write permission to change the
475 bits on a file. Just like file_ntimes below.
478 /* Check if we have write access. */
479 if (!CAN_WRITE(conn)) {
480 return NT_STATUS_ACCESS_DENIED;
483 status = smbd_check_access_rights_fsp(conn->cwd_fsp,
484 smb_fname->fsp,
485 false,
486 FILE_WRITE_ATTRIBUTES);
487 if (NT_STATUS_IS_OK(status)) {
488 set_dosmode_ok = true;
491 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
492 set_dosmode_ok = can_write_to_fsp(smb_fname->fsp);
495 if (!set_dosmode_ok) {
496 return NT_STATUS_ACCESS_DENIED;
499 become_root();
500 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
501 SAMBA_XATTR_DOS_ATTRIB,
502 blob.data, blob.length, 0);
503 if (ret == 0) {
504 status = NT_STATUS_OK;
506 unbecome_root();
507 if (!NT_STATUS_IS_OK(status)) {
508 return status;
513 * We correctly stored the create time.
514 * We *always* set XATTR_DOSINFO_CREATE_TIME,
515 * so now it can no longer be considered
516 * calculated. Make sure to use the value rounded
517 * to NTTIME granularity we've stored in the xattr.
519 btime = nt_time_to_full_timespec(dosattrib.info.info5.create_time);
520 update_stat_ex_create_time(&smb_fname->st, btime);
522 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
523 (unsigned int)dosmode,
524 smb_fname_str_dbg(smb_fname)));
525 return NT_STATUS_OK;
528 /****************************************************************************
529 Change a unix mode to a dos mode for an ms dfs link.
530 ****************************************************************************/
532 uint32_t dos_mode_msdfs(connection_struct *conn,
533 const struct smb_filename *smb_fname)
535 uint32_t result = 0;
537 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
539 if (!VALID_STAT(smb_fname->st)) {
540 return 0;
543 /* First do any modifications that depend on the path name. */
544 /* hide files with a name starting with a . */
545 if (lp_hide_dot_files(SNUM(conn))) {
546 const char *p = strrchr_m(smb_fname->base_name, '/');
547 if (p) {
548 p++;
549 } else {
550 p = smb_fname->base_name;
553 /* Only . and .. are not hidden. */
554 if ((p[0] == '.') && !(ISDOT(p) || ISDOTDOT(p))) {
555 result |= FILE_ATTRIBUTE_HIDDEN;
559 result |= dos_mode_from_sbuf(conn, smb_fname);
561 /* Optimization : Only call is_hidden_path if it's not already
562 hidden. */
563 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
564 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
565 result |= FILE_ATTRIBUTE_HIDDEN;
568 if (result == 0) {
569 result = FILE_ATTRIBUTE_NORMAL;
572 result = filter_mode_by_protocol(result);
575 * Add in that it is a reparse point
577 result |= FILE_ATTRIBUTE_REPARSE_POINT;
579 dos_mode_debug_print(__func__, result);
581 return(result);
585 * check whether a file or directory is flagged as compressed.
587 static NTSTATUS dos_mode_check_compressed(struct files_struct *fsp,
588 bool *is_compressed)
590 NTSTATUS status;
591 uint16_t compression_fmt;
593 status = SMB_VFS_FGET_COMPRESSION(
594 fsp->conn, talloc_tos(), fsp, &compression_fmt);
595 if (!NT_STATUS_IS_OK(status)) {
596 return status;
599 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
600 *is_compressed = true;
601 } else {
602 *is_compressed = false;
604 return NT_STATUS_OK;
607 static uint32_t dos_mode_from_name(connection_struct *conn,
608 const struct smb_filename *smb_fname,
609 uint32_t dosmode)
611 const char *p = NULL;
612 uint32_t result = dosmode;
614 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
615 lp_hide_dot_files(SNUM(conn)))
617 p = strrchr_m(smb_fname->base_name, '/');
618 if (p) {
619 p++;
620 } else {
621 p = smb_fname->base_name;
624 /* Only . and .. are not hidden. */
625 if ((p[0] == '.') && !(ISDOT(p) || ISDOTDOT(p))) {
626 result |= FILE_ATTRIBUTE_HIDDEN;
630 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
631 IS_HIDDEN_PATH(conn, smb_fname->base_name))
633 result |= FILE_ATTRIBUTE_HIDDEN;
636 return result;
639 static uint32_t dos_mode_post(uint32_t dosmode,
640 struct files_struct *fsp,
641 const char *func)
643 struct smb_filename *smb_fname = NULL;
644 NTSTATUS status;
646 if (fsp != NULL) {
647 smb_fname = fsp->fsp_name;
649 SMB_ASSERT(smb_fname != NULL);
652 * According to MS-FSA a stream name does not have
653 * separate DOS attribute metadata, so we must return
654 * the DOS attribute from the base filename. With one caveat,
655 * a non-default stream name can never be a directory.
657 * As this is common to all streams data stores, we handle
658 * it here instead of inside all stream VFS modules.
660 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
663 if (is_named_stream(smb_fname)) {
664 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
665 dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
668 if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
669 bool compressed = false;
671 status = dos_mode_check_compressed(fsp, &compressed);
672 if (NT_STATUS_IS_OK(status) && compressed) {
673 dosmode |= FILE_ATTRIBUTE_COMPRESSED;
677 dosmode |= dos_mode_from_name(fsp->conn, smb_fname, dosmode);
679 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
680 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
681 } else if (dosmode == 0) {
682 dosmode = FILE_ATTRIBUTE_NORMAL;
685 dosmode = filter_mode_by_protocol(dosmode);
687 dos_mode_debug_print(func, dosmode);
688 return dosmode;
691 /****************************************************************************
692 Change a unix mode to a dos mode.
693 May also read the create timespec into the stat struct in smb_fname
694 if "store dos attributes" is true.
695 ****************************************************************************/
697 uint32_t fdos_mode(struct files_struct *fsp)
699 uint32_t result = 0;
700 NTSTATUS status = NT_STATUS_OK;
702 if (fsp == NULL) {
704 * The pathological case where a callers does
705 * fdos_mode(smb_fname->fsp) passing a pathref fsp. But as
706 * smb_fname points at a symlink in POSIX context smb_fname->fsp
707 * is NULL.
709 return FILE_ATTRIBUTE_NORMAL;
712 DBG_DEBUG("%s\n", fsp_str_dbg(fsp));
714 if (fsp->fake_file_handle != NULL) {
715 return dosmode_from_fake_filehandle(fsp->fake_file_handle);
718 if (!VALID_STAT(fsp->fsp_name->st)) {
719 return 0;
722 if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) {
723 return FILE_ATTRIBUTE_NORMAL;
726 if (fsp->fsp_name->st.cached_dos_attributes != FILE_ATTRIBUTES_INVALID) {
727 return fsp->fsp_name->st.cached_dos_attributes;
730 /* Get the DOS attributes via the VFS if we can */
731 status = vfs_fget_dos_attributes(fsp, &result);
732 if (!NT_STATUS_IS_OK(status)) {
734 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
736 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
737 result |= dos_mode_from_sbuf(fsp->conn, fsp->fsp_name);
741 fsp->fsp_name->st.cached_dos_attributes = dos_mode_post(result, fsp, __func__);
742 return fsp->fsp_name->st.cached_dos_attributes;
745 struct dos_mode_at_state {
746 files_struct *dir_fsp;
747 struct smb_filename *smb_fname;
748 uint32_t dosmode;
751 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
753 struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
754 struct tevent_context *ev,
755 files_struct *dir_fsp,
756 struct smb_filename *smb_fname)
758 struct tevent_req *req = NULL;
759 struct dos_mode_at_state *state = NULL;
760 struct tevent_req *subreq = NULL;
762 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
764 req = tevent_req_create(mem_ctx, &state,
765 struct dos_mode_at_state);
766 if (req == NULL) {
767 return NULL;
770 *state = (struct dos_mode_at_state) {
771 .dir_fsp = dir_fsp,
772 .smb_fname = smb_fname,
775 if (!VALID_STAT(smb_fname->st)) {
776 tevent_req_done(req);
777 return tevent_req_post(req, ev);
780 if (smb_fname->fsp == NULL) {
781 if (ISDOTDOT(smb_fname->base_name)) {
783 * smb_fname->fsp is explicitly closed
784 * for ".." to prevent meta-data leakage.
786 state->dosmode = FILE_ATTRIBUTE_DIRECTORY;
787 } else {
789 * This is a symlink in POSIX context.
790 * FIXME ? Should we move to returning
791 * FILE_ATTRIBUTE_REPARSE_POINT here ?
793 state->dosmode = FILE_ATTRIBUTE_NORMAL;
795 tevent_req_done(req);
796 return tevent_req_post(req, ev);
799 subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
801 dir_fsp,
802 smb_fname);
803 if (tevent_req_nomem(subreq, req)) {
804 return tevent_req_post(req, ev);
806 tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
808 return req;
811 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
813 struct tevent_req *req =
814 tevent_req_callback_data(subreq,
815 struct tevent_req);
816 struct dos_mode_at_state *state =
817 tevent_req_data(req,
818 struct dos_mode_at_state);
819 struct vfs_aio_state aio_state;
820 NTSTATUS status;
821 bool ok;
824 * Make sure we run as the user again
826 ok = change_to_user_and_service_by_fsp(state->dir_fsp);
827 SMB_ASSERT(ok);
829 status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
830 &aio_state,
831 &state->dosmode);
832 TALLOC_FREE(subreq);
833 if (!NT_STATUS_IS_OK(status)) {
835 * Both the sync dos_mode() as well as the async
836 * dos_mode_at_[send|recv] have no real error return, the only
837 * unhandled error is when the stat info in smb_fname is not
838 * valid (cf the checks in dos_mode() and dos_mode_at_send().
840 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
841 * dos_mode_post() which also does the mapping of a last resort
842 * from S_IFMT(st_mode).
844 * Only if we get NT_STATUS_NOT_IMPLEMENTED or
845 * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
846 * fallback to sync processing.
848 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) &&
849 !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED))
852 * state->dosmode should still be 0, but reset
853 * it to be sure.
855 state->dosmode = 0;
856 status = NT_STATUS_OK;
859 if (NT_STATUS_IS_OK(status)) {
860 state->dosmode = dos_mode_post(state->dosmode,
861 state->smb_fname->fsp,
862 __func__);
863 tevent_req_done(req);
864 return;
868 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
871 state->dosmode = fdos_mode(state->smb_fname->fsp);
872 tevent_req_done(req);
873 return;
876 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
878 struct dos_mode_at_state *state =
879 tevent_req_data(req,
880 struct dos_mode_at_state);
881 NTSTATUS status;
883 if (tevent_req_is_nterror(req, &status)) {
884 tevent_req_received(req);
885 return status;
888 *dosmode = state->dosmode;
889 tevent_req_received(req);
890 return NT_STATUS_OK;
893 /*******************************************************************
894 chmod a file - but preserve some bits.
895 If "store dos attributes" is also set it will store the create time
896 from the stat struct in smb_fname (in NTTIME format) in the EA
897 attribute also.
898 ********************************************************************/
900 int file_set_dosmode(connection_struct *conn,
901 struct smb_filename *smb_fname,
902 uint32_t dosmode,
903 struct smb_filename *parent_dir,
904 bool newfile)
906 int mask=0;
907 mode_t tmp;
908 mode_t unixmode;
909 int ret = -1;
910 NTSTATUS status;
912 if (!CAN_WRITE(conn)) {
913 errno = EROFS;
914 return -1;
917 if ((S_ISDIR(smb_fname->st.st_ex_mode)) &&
918 (dosmode & FILE_ATTRIBUTE_TEMPORARY))
920 errno = EINVAL;
921 return -1;
924 dosmode &= SAMBA_ATTRIBUTES_MASK;
926 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
927 dosmode, smb_fname_str_dbg(smb_fname)));
929 unixmode = smb_fname->st.st_ex_mode;
931 if (smb_fname->fsp != NULL) {
932 get_acl_group_bits(
933 conn, smb_fname->fsp, &smb_fname->st.st_ex_mode);
936 if (S_ISDIR(smb_fname->st.st_ex_mode))
937 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
938 else
939 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
941 if (smb_fname->fsp != NULL) {
942 /* Store the DOS attributes in an EA by preference. */
943 status = SMB_VFS_FSET_DOS_ATTRIBUTES(
944 conn, metadata_fsp(smb_fname->fsp), dosmode);
945 } else {
946 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
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 (!IS_DOS_READONLY(dosmode)) {
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 if ((fsp->access_mask & (FILE_WRITE_DATA
1090 | FILE_WRITE_ATTRIBUTES
1091 | SEC_FILE_APPEND_DATA)) == 0) {
1092 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1093 "access_mask[0x%08X] - access denied\n",
1094 smb_fname_str_dbg(fsp->fsp_name),
1095 sparse,
1096 fsp->access_mask));
1097 return NT_STATUS_ACCESS_DENIED;
1100 if (fsp->fsp_flags.is_directory) {
1101 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1102 (sparse ? "set" : "clear"),
1103 smb_fname_str_dbg(fsp->fsp_name)));
1104 return NT_STATUS_INVALID_PARAMETER;
1107 if (IS_IPC(conn) || IS_PRINT(conn)) {
1108 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1109 (sparse ? "set" : "clear")));
1110 return NT_STATUS_INVALID_PARAMETER;
1113 if (fsp_is_alternate_stream(fsp)) {
1115 * MS-FSA 2.1.1.5 IsSparse
1117 * This is a per stream attribute, but our backends don't
1118 * support it a consistent way, therefor just pretend
1119 * success and ignore the request.
1121 DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
1122 "[%s]\n", fsp_str_dbg(fsp));
1123 return NT_STATUS_OK;
1126 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1127 sparse, smb_fname_str_dbg(fsp->fsp_name)));
1129 if (!lp_store_dos_attributes(SNUM(conn))) {
1130 return NT_STATUS_INVALID_DEVICE_REQUEST;
1133 status = vfs_stat_fsp(fsp);
1134 if (!NT_STATUS_IS_OK(status)) {
1135 return status;
1138 old_dosmode = fdos_mode(fsp);
1140 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1141 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1142 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1143 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1144 } else {
1145 return NT_STATUS_OK;
1148 /* Store the DOS attributes in an EA. */
1149 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1150 if (!NT_STATUS_IS_OK(status)) {
1151 return status;
1154 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1155 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1156 fsp->fsp_name->base_name);
1158 fsp->fsp_name->st.cached_dos_attributes = new_dosmode;
1159 fsp->fsp_flags.is_sparse = sparse;
1161 return NT_STATUS_OK;
1164 /*******************************************************************
1165 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1166 than POSIX.
1167 *******************************************************************/
1169 int file_ntimes(connection_struct *conn,
1170 files_struct *fsp,
1171 struct smb_file_time *ft)
1173 int ret = -1;
1175 errno = 0;
1177 DBG_INFO("actime: %s",
1178 time_to_asc(convert_timespec_to_time_t(ft->atime)));
1179 DBG_INFO("modtime: %s",
1180 time_to_asc(convert_timespec_to_time_t(ft->mtime)));
1181 DBG_INFO("ctime: %s",
1182 time_to_asc(convert_timespec_to_time_t(ft->ctime)));
1183 DBG_INFO("createtime: %s",
1184 time_to_asc(convert_timespec_to_time_t(ft->create_time)));
1186 /* Don't update the time on read-only shares */
1187 /* We need this as set_filetime (which can be called on
1188 close and other paths) can end up calling this function
1189 without the NEED_WRITE protection. Found by :
1190 Leo Weppelman <leo@wau.mis.ah.nl>
1193 if (!CAN_WRITE(conn)) {
1194 return 0;
1197 if (SMB_VFS_FNTIMES(fsp, ft) == 0) {
1198 return 0;
1201 if((errno != EPERM) && (errno != EACCES)) {
1202 return -1;
1205 if(!lp_dos_filetimes(SNUM(conn))) {
1206 return -1;
1209 /* We have permission (given by the Samba admin) to
1210 break POSIX semantics and allow a user to change
1211 the time on a file they don't own but can write to
1212 (as DOS does).
1215 /* Check if we have write access. */
1216 if (can_write_to_fsp(fsp)) {
1217 /* We are allowed to become root and change the filetime. */
1218 become_root();
1219 ret = SMB_VFS_FNTIMES(fsp, ft);
1220 unbecome_root();
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 return smb_fname->st.st_ex_btime;
1300 /******************************************************************
1301 Return a change time (may look at EA in future).
1302 ******************************************************************/
1304 struct timespec get_change_timespec(connection_struct *conn,
1305 struct files_struct *fsp,
1306 const struct smb_filename *smb_fname)
1308 return smb_fname->st.st_ex_mtime;