tests/krb5: Add more encryption type constants
[Samba.git] / source3 / smbd / dosmode.c
blob659066642c2aa53ef05f49ba0e669d8c22479609
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:
306 struct xattr_DosInfo4 *info = &dosattrib.info.info4;
308 dosattr = info->attrib;
310 if ((info->valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
311 !null_nttime(info->create_time))
313 struct timespec creat_time;
315 creat_time = nt_time_to_full_timespec(info->create_time);
316 update_stat_ex_create_time(&smb_fname->st, creat_time);
318 DBG_DEBUG("file [%s] creation time [%s]\n",
319 smb_fname_str_dbg(smb_fname),
320 nt_time_string(talloc_tos(), info->create_time));
323 if (info->valid_flags & XATTR_DOSINFO_ITIME) {
324 struct timespec itime;
325 uint64_t file_id;
327 itime = nt_time_to_unix_timespec(info->itime);
328 if (smb_fname->st.st_ex_iflags &
329 ST_EX_IFLAG_CALCULATED_ITIME)
331 update_stat_ex_itime(&smb_fname->st, itime);
334 file_id = make_file_id_from_itime(&smb_fname->st);
335 if (smb_fname->st.st_ex_iflags &
336 ST_EX_IFLAG_CALCULATED_FILE_ID)
338 update_stat_ex_file_id(&smb_fname->st, file_id);
341 DBG_DEBUG("file [%s] itime [%s] fileid [%"PRIx64"]\n",
342 smb_fname_str_dbg(smb_fname),
343 nt_time_string(talloc_tos(), info->itime),
344 file_id);
346 break;
348 default:
349 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
350 smb_fname_str_dbg(smb_fname), blob.data);
351 /* Should this be INTERNAL_ERROR? */
352 return NT_STATUS_INVALID_PARAMETER;
355 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
356 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
359 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
360 *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
362 dos_mode_debug_print(__func__, *pattr);
364 return NT_STATUS_OK;
367 NTSTATUS fget_ea_dos_attribute(struct files_struct *fsp,
368 uint32_t *pattr)
370 DATA_BLOB blob;
371 ssize_t sizeret;
372 fstring attrstr;
373 NTSTATUS status;
375 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
376 return NT_STATUS_NOT_IMPLEMENTED;
379 /* Don't reset pattr to zero as we may already have filename-based attributes we
380 need to preserve. */
382 sizeret = SMB_VFS_FGETXATTR(fsp->base_fsp ? fsp->base_fsp : fsp,
383 SAMBA_XATTR_DOS_ATTRIB,
384 attrstr,
385 sizeof(attrstr));
386 if (sizeret == -1 && ( errno == EPERM || errno == EACCES )) {
387 /* we may also retrieve dos attribs for unreadable files, this
388 is why we'll retry as root. We don't use root in the first
389 run because in cases like NFS, root might have even less
390 rights than the real user
392 become_root();
393 sizeret = SMB_VFS_FGETXATTR(fsp->base_fsp ? fsp->base_fsp : fsp,
394 SAMBA_XATTR_DOS_ATTRIB,
395 attrstr,
396 sizeof(attrstr));
397 unbecome_root();
399 if (sizeret == -1) {
400 DBG_INFO("Cannot get attribute "
401 "from EA on file %s: Error = %s\n",
402 fsp_str_dbg(fsp), strerror(errno));
403 return map_nt_error_from_unix(errno);
406 blob.data = (uint8_t *)attrstr;
407 blob.length = sizeret;
409 status = parse_dos_attribute_blob(fsp->fsp_name, blob, pattr);
410 if (!NT_STATUS_IS_OK(status)) {
411 return status;
414 return NT_STATUS_OK;
417 /****************************************************************************
418 Set DOS attributes in an EA.
419 Also sets the create time.
420 ****************************************************************************/
422 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
423 const struct smb_filename *smb_fname,
424 uint32_t dosmode)
426 struct xattr_DOSATTRIB dosattrib;
427 enum ndr_err_code ndr_err;
428 DATA_BLOB blob;
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 ZERO_STRUCT(dosattrib);
446 ZERO_STRUCT(blob);
448 dosattrib.version = 4;
449 dosattrib.info.info4.valid_flags = XATTR_DOSINFO_ATTRIB |
450 XATTR_DOSINFO_CREATE_TIME;
451 dosattrib.info.info4.attrib = dosmode;
452 dosattrib.info.info4.create_time = full_timespec_to_nt_time(
453 &smb_fname->st.st_ex_btime);
455 if (!(smb_fname->st.st_ex_iflags & ST_EX_IFLAG_CALCULATED_ITIME)) {
456 dosattrib.info.info4.valid_flags |= XATTR_DOSINFO_ITIME;
457 dosattrib.info.info4.itime = full_timespec_to_nt_time(
458 &smb_fname->st.st_ex_itime);
461 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
462 (unsigned int)dosmode,
463 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
464 smb_fname_str_dbg(smb_fname) ));
466 ndr_err = ndr_push_struct_blob(
467 &blob, talloc_tos(), &dosattrib,
468 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
470 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
471 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
472 ndr_errstr(ndr_err)));
473 return ndr_map_error2ntstatus(ndr_err);
476 if (blob.data == NULL || blob.length == 0) {
477 /* Should this be INTERNAL_ERROR? */
478 return NT_STATUS_INVALID_PARAMETER;
481 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
482 SAMBA_XATTR_DOS_ATTRIB,
483 blob.data, blob.length, 0);
484 if (ret != 0) {
485 NTSTATUS status = NT_STATUS_OK;
486 bool set_dosmode_ok = false;
488 if ((errno != EPERM) && (errno != EACCES)) {
489 DBG_INFO("Cannot set "
490 "attribute EA on file %s: Error = %s\n",
491 smb_fname_str_dbg(smb_fname), strerror(errno));
492 return map_nt_error_from_unix(errno);
495 /* We want DOS semantics, ie allow non owner with write permission to change the
496 bits on a file. Just like file_ntimes below.
499 /* Check if we have write access. */
500 if (!CAN_WRITE(conn)) {
501 return NT_STATUS_ACCESS_DENIED;
504 status = smbd_check_access_rights_fsp(conn->cwd_fsp,
505 smb_fname->fsp,
506 false,
507 FILE_WRITE_ATTRIBUTES);
508 if (NT_STATUS_IS_OK(status)) {
509 set_dosmode_ok = true;
512 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
513 set_dosmode_ok = can_write_to_fsp(smb_fname->fsp);
516 if (!set_dosmode_ok) {
517 return NT_STATUS_ACCESS_DENIED;
520 become_root();
521 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
522 SAMBA_XATTR_DOS_ATTRIB,
523 blob.data, blob.length, 0);
524 if (ret == 0) {
525 status = NT_STATUS_OK;
527 unbecome_root();
528 return status;
530 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
531 (unsigned int)dosmode,
532 smb_fname_str_dbg(smb_fname)));
533 return NT_STATUS_OK;
536 /****************************************************************************
537 Change a unix mode to a dos mode for an ms dfs link.
538 ****************************************************************************/
540 uint32_t dos_mode_msdfs(connection_struct *conn,
541 const struct smb_filename *smb_fname)
543 uint32_t result = 0;
545 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
547 if (!VALID_STAT(smb_fname->st)) {
548 return 0;
551 /* First do any modifications that depend on the path name. */
552 /* hide files with a name starting with a . */
553 if (lp_hide_dot_files(SNUM(conn))) {
554 const char *p = strrchr_m(smb_fname->base_name, '/');
555 if (p) {
556 p++;
557 } else {
558 p = smb_fname->base_name;
561 /* Only . and .. are not hidden. */
562 if (p[0] == '.' && !((p[1] == '\0') ||
563 (p[1] == '.' && p[2] == '\0'))) {
564 result |= FILE_ATTRIBUTE_HIDDEN;
568 result |= dos_mode_from_sbuf(conn, smb_fname);
570 /* Optimization : Only call is_hidden_path if it's not already
571 hidden. */
572 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
573 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
574 result |= FILE_ATTRIBUTE_HIDDEN;
577 if (result == 0) {
578 result = FILE_ATTRIBUTE_NORMAL;
581 result = filter_mode_by_protocol(result);
584 * Add in that it is a reparse point
586 result |= FILE_ATTRIBUTE_REPARSE_POINT;
588 dos_mode_debug_print(__func__, result);
590 return(result);
594 * check whether a file or directory is flagged as compressed.
596 static NTSTATUS dos_mode_check_compressed(struct files_struct *fsp,
597 bool *is_compressed)
599 NTSTATUS status;
600 uint16_t compression_fmt;
602 status = SMB_VFS_FGET_COMPRESSION(
603 fsp->conn, talloc_tos(), fsp, &compression_fmt);
604 if (!NT_STATUS_IS_OK(status)) {
605 return status;
608 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
609 *is_compressed = true;
610 } else {
611 *is_compressed = false;
613 return NT_STATUS_OK;
616 static uint32_t dos_mode_from_name(connection_struct *conn,
617 const struct smb_filename *smb_fname,
618 uint32_t dosmode)
620 const char *p = NULL;
621 uint32_t result = dosmode;
623 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
624 lp_hide_dot_files(SNUM(conn)))
626 p = strrchr_m(smb_fname->base_name, '/');
627 if (p) {
628 p++;
629 } else {
630 p = smb_fname->base_name;
633 /* Only . and .. are not hidden. */
634 if ((p[0] == '.') && !(ISDOT(p) || ISDOTDOT(p))) {
635 result |= FILE_ATTRIBUTE_HIDDEN;
639 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
640 IS_HIDDEN_PATH(conn, smb_fname->base_name))
642 result |= FILE_ATTRIBUTE_HIDDEN;
645 return result;
648 static uint32_t dos_mode_post(uint32_t dosmode,
649 struct files_struct *fsp,
650 const char *func)
652 struct smb_filename *smb_fname = NULL;
653 NTSTATUS status;
655 if (fsp != NULL) {
656 smb_fname = fsp->fsp_name;
658 SMB_ASSERT(smb_fname != NULL);
661 * According to MS-FSA a stream name does not have
662 * separate DOS attribute metadata, so we must return
663 * the DOS attribute from the base filename. With one caveat,
664 * a non-default stream name can never be a directory.
666 * As this is common to all streams data stores, we handle
667 * it here instead of inside all stream VFS modules.
669 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
672 if (is_named_stream(smb_fname)) {
673 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
674 dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
677 if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
678 bool compressed = false;
680 status = dos_mode_check_compressed(fsp, &compressed);
681 if (NT_STATUS_IS_OK(status) && compressed) {
682 dosmode |= FILE_ATTRIBUTE_COMPRESSED;
686 dosmode |= dos_mode_from_name(fsp->conn, smb_fname, dosmode);
688 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
689 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
690 } else if (dosmode == 0) {
691 dosmode = FILE_ATTRIBUTE_NORMAL;
694 dosmode = filter_mode_by_protocol(dosmode);
696 dos_mode_debug_print(func, dosmode);
697 return dosmode;
700 /****************************************************************************
701 Change a unix mode to a dos mode.
702 May also read the create timespec into the stat struct in smb_fname
703 if "store dos attributes" is true.
704 ****************************************************************************/
706 uint32_t fdos_mode(struct files_struct *fsp)
708 uint32_t result = 0;
709 NTSTATUS status = NT_STATUS_OK;
711 if (fsp == NULL) {
713 * The pathological case where a callers does
714 * fdos_mode(smb_fname->fsp) passing a pathref fsp. But as
715 * smb_fname points at a symlink in POSIX context smb_fname->fsp
716 * is NULL.
718 return FILE_ATTRIBUTE_NORMAL;
721 DBG_DEBUG("%s\n", fsp_str_dbg(fsp));
723 if (fsp->fake_file_handle != NULL) {
724 return dosmode_from_fake_filehandle(fsp->fake_file_handle);
727 if (!VALID_STAT(fsp->fsp_name->st)) {
728 return 0;
731 if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) {
732 return FILE_ATTRIBUTE_NORMAL;
735 /* Get the DOS attributes via the VFS if we can */
736 status = SMB_VFS_FGET_DOS_ATTRIBUTES(fsp->conn, fsp, &result);
737 if (!NT_STATUS_IS_OK(status)) {
739 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
741 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
742 result |= dos_mode_from_sbuf(fsp->conn, fsp->fsp_name);
746 result = dos_mode_post(result, fsp, __func__);
747 return result;
750 struct dos_mode_at_state {
751 files_struct *dir_fsp;
752 struct smb_filename *smb_fname;
753 uint32_t dosmode;
756 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
758 struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
759 struct tevent_context *ev,
760 files_struct *dir_fsp,
761 struct smb_filename *smb_fname)
763 struct tevent_req *req = NULL;
764 struct dos_mode_at_state *state = NULL;
765 struct tevent_req *subreq = NULL;
767 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
769 req = tevent_req_create(mem_ctx, &state,
770 struct dos_mode_at_state);
771 if (req == NULL) {
772 return NULL;
775 *state = (struct dos_mode_at_state) {
776 .dir_fsp = dir_fsp,
777 .smb_fname = smb_fname,
780 if (!VALID_STAT(smb_fname->st)) {
781 tevent_req_done(req);
782 return tevent_req_post(req, ev);
785 if (smb_fname->fsp == NULL) {
786 if (ISDOTDOT(smb_fname->base_name)) {
788 * smb_fname->fsp is explicitly closed
789 * for ".." to prevent meta-data leakage.
791 state->dosmode = FILE_ATTRIBUTE_DIRECTORY;
792 } else {
794 * This is a symlink in POSIX context.
795 * FIXME ? Should we move to returning
796 * FILE_ATTRIBUTE_REPARSE_POINT here ?
798 state->dosmode = FILE_ATTRIBUTE_NORMAL;
800 tevent_req_done(req);
801 return tevent_req_post(req, ev);
804 subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
806 dir_fsp,
807 smb_fname);
808 if (tevent_req_nomem(subreq, req)) {
809 return tevent_req_post(req, ev);
811 tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
813 return req;
816 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
818 struct tevent_req *req =
819 tevent_req_callback_data(subreq,
820 struct tevent_req);
821 struct dos_mode_at_state *state =
822 tevent_req_data(req,
823 struct dos_mode_at_state);
824 struct vfs_aio_state aio_state;
825 NTSTATUS status;
826 bool ok;
829 * Make sure we run as the user again
831 ok = change_to_user_and_service_by_fsp(state->dir_fsp);
832 SMB_ASSERT(ok);
834 status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
835 &aio_state,
836 &state->dosmode);
837 TALLOC_FREE(subreq);
838 if (!NT_STATUS_IS_OK(status)) {
840 * Both the sync dos_mode() as well as the async
841 * dos_mode_at_[send|recv] have no real error return, the only
842 * unhandled error is when the stat info in smb_fname is not
843 * valid (cf the checks in dos_mode() and dos_mode_at_send().
845 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
846 * dos_mode_post() which also does the mapping of a last resort
847 * from S_IFMT(st_mode).
849 * Only if we get NT_STATUS_NOT_IMPLEMENTED or
850 * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
851 * fallback to sync processing.
853 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) &&
854 !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED))
857 * state->dosmode should still be 0, but reset
858 * it to be sure.
860 state->dosmode = 0;
861 status = NT_STATUS_OK;
864 if (NT_STATUS_IS_OK(status)) {
865 state->dosmode = dos_mode_post(state->dosmode,
866 state->smb_fname->fsp,
867 __func__);
868 tevent_req_done(req);
869 return;
873 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
876 state->dosmode = fdos_mode(state->smb_fname->fsp);
877 tevent_req_done(req);
878 return;
881 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
883 struct dos_mode_at_state *state =
884 tevent_req_data(req,
885 struct dos_mode_at_state);
886 NTSTATUS status;
888 if (tevent_req_is_nterror(req, &status)) {
889 tevent_req_received(req);
890 return status;
893 *dosmode = state->dosmode;
894 tevent_req_received(req);
895 return NT_STATUS_OK;
898 /*******************************************************************
899 chmod a file - but preserve some bits.
900 If "store dos attributes" is also set it will store the create time
901 from the stat struct in smb_fname (in NTTIME format) in the EA
902 attribute also.
903 ********************************************************************/
905 int file_set_dosmode(connection_struct *conn,
906 struct smb_filename *smb_fname,
907 uint32_t dosmode,
908 struct smb_filename *parent_dir,
909 bool newfile)
911 int mask=0;
912 mode_t tmp;
913 mode_t unixmode;
914 int ret = -1;
915 NTSTATUS status;
917 if (!CAN_WRITE(conn)) {
918 errno = EROFS;
919 return -1;
922 dosmode &= SAMBA_ATTRIBUTES_MASK;
924 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
925 dosmode, smb_fname_str_dbg(smb_fname)));
927 unixmode = smb_fname->st.st_ex_mode;
929 if (smb_fname->fsp != NULL) {
930 get_acl_group_bits(
931 conn, smb_fname->fsp, &smb_fname->st.st_ex_mode);
934 if (S_ISDIR(smb_fname->st.st_ex_mode))
935 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
936 else
937 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
939 if (smb_fname->fsp != NULL) {
940 /* Store the DOS attributes in an EA by preference. */
941 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn,
942 smb_fname->fsp,
943 dosmode);
944 } else {
945 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
948 if (NT_STATUS_IS_OK(status)) {
949 ret = 0;
950 goto done;
954 * Only fall back to using UNIX modes if
955 * we get NOT_IMPLEMENTED.
957 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
958 errno = map_errno_from_nt_status(status);
959 return -1;
962 /* Fall back to UNIX modes. */
963 unixmode = unix_mode(
964 conn,
965 dosmode,
966 smb_fname,
967 parent_dir != NULL ? parent_dir->fsp : NULL);
969 /* preserve the file type bits */
970 mask |= S_IFMT;
972 /* preserve the s bits */
973 mask |= (S_ISUID | S_ISGID);
975 /* preserve the t bit */
976 #ifdef S_ISVTX
977 mask |= S_ISVTX;
978 #endif
980 /* possibly preserve the x bits */
981 if (!MAP_ARCHIVE(conn))
982 mask |= S_IXUSR;
983 if (!MAP_SYSTEM(conn))
984 mask |= S_IXGRP;
985 if (!MAP_HIDDEN(conn))
986 mask |= S_IXOTH;
988 unixmode |= (smb_fname->st.st_ex_mode & mask);
990 /* if we previously had any r bits set then leave them alone */
991 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
992 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
993 unixmode |= tmp;
996 /* if we previously had any w bits set then leave them alone
997 whilst adding in the new w bits, if the new mode is not rdonly */
998 if (!IS_DOS_READONLY(dosmode)) {
999 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
1003 * From the chmod 2 man page:
1005 * "If the calling process is not privileged, and the group of the file
1006 * does not match the effective group ID of the process or one of its
1007 * supplementary group IDs, the S_ISGID bit will be turned off, but
1008 * this will not cause an error to be returned."
1010 * Simply refuse to do the chmod in this case.
1013 if (S_ISDIR(smb_fname->st.st_ex_mode) &&
1014 (unixmode & S_ISGID) &&
1015 geteuid() != sec_initial_uid() &&
1016 !current_user_in_group(conn, smb_fname->st.st_ex_gid))
1018 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1019 "set for directory %s\n",
1020 smb_fname_str_dbg(smb_fname)));
1021 errno = EPERM;
1022 return -1;
1025 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1026 if (ret == 0) {
1027 goto done;
1030 if((errno != EPERM) && (errno != EACCES))
1031 return -1;
1033 if(!lp_dos_filemode(SNUM(conn)))
1034 return -1;
1036 /* We want DOS semantics, ie allow non owner with write permission to change the
1037 bits on a file. Just like file_ntimes below.
1040 if (!can_write_to_fsp(smb_fname->fsp))
1042 errno = EACCES;
1043 return -1;
1046 become_root();
1047 ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1048 unbecome_root();
1050 done:
1051 if (!newfile) {
1052 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1053 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1054 smb_fname->base_name);
1056 if (ret == 0) {
1057 smb_fname->st.st_ex_mode = unixmode;
1060 return( ret );
1064 NTSTATUS file_set_sparse(connection_struct *conn,
1065 files_struct *fsp,
1066 bool sparse)
1068 const struct loadparm_substitution *lp_sub =
1069 loadparm_s3_global_substitution();
1070 uint32_t old_dosmode;
1071 uint32_t new_dosmode;
1072 NTSTATUS status;
1074 if (!CAN_WRITE(conn)) {
1075 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1076 "on readonly share[%s]\n",
1077 smb_fname_str_dbg(fsp->fsp_name),
1078 sparse,
1079 lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
1080 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1084 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1085 * following access flags are granted.
1087 if ((fsp->access_mask & (FILE_WRITE_DATA
1088 | FILE_WRITE_ATTRIBUTES
1089 | SEC_FILE_APPEND_DATA)) == 0) {
1090 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1091 "access_mask[0x%08X] - access denied\n",
1092 smb_fname_str_dbg(fsp->fsp_name),
1093 sparse,
1094 fsp->access_mask));
1095 return NT_STATUS_ACCESS_DENIED;
1098 if (fsp->fsp_flags.is_directory) {
1099 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1100 (sparse ? "set" : "clear"),
1101 smb_fname_str_dbg(fsp->fsp_name)));
1102 return NT_STATUS_INVALID_PARAMETER;
1105 if (IS_IPC(conn) || IS_PRINT(conn)) {
1106 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1107 (sparse ? "set" : "clear")));
1108 return NT_STATUS_INVALID_PARAMETER;
1111 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1112 sparse, smb_fname_str_dbg(fsp->fsp_name)));
1114 if (!lp_store_dos_attributes(SNUM(conn))) {
1115 return NT_STATUS_INVALID_DEVICE_REQUEST;
1118 status = vfs_stat_fsp(fsp);
1119 if (!NT_STATUS_IS_OK(status)) {
1120 return status;
1123 old_dosmode = fdos_mode(fsp);
1125 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1126 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1127 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1128 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1129 } else {
1130 return NT_STATUS_OK;
1133 /* Store the DOS attributes in an EA. */
1134 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1135 if (!NT_STATUS_IS_OK(status)) {
1136 return status;
1139 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1140 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1141 fsp->fsp_name->base_name);
1143 fsp->fsp_flags.is_sparse = sparse;
1145 return NT_STATUS_OK;
1148 /*******************************************************************
1149 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1150 than POSIX.
1151 *******************************************************************/
1153 int file_ntimes(connection_struct *conn,
1154 files_struct *fsp,
1155 struct smb_file_time *ft)
1157 int ret = -1;
1159 errno = 0;
1161 DBG_INFO("actime: %s",
1162 time_to_asc(convert_timespec_to_time_t(ft->atime)));
1163 DBG_INFO("modtime: %s",
1164 time_to_asc(convert_timespec_to_time_t(ft->mtime)));
1165 DBG_INFO("ctime: %s",
1166 time_to_asc(convert_timespec_to_time_t(ft->ctime)));
1167 DBG_INFO("createtime: %s",
1168 time_to_asc(convert_timespec_to_time_t(ft->create_time)));
1170 /* Don't update the time on read-only shares */
1171 /* We need this as set_filetime (which can be called on
1172 close and other paths) can end up calling this function
1173 without the NEED_WRITE protection. Found by :
1174 Leo Weppelman <leo@wau.mis.ah.nl>
1177 if (!CAN_WRITE(conn)) {
1178 return 0;
1181 if (SMB_VFS_FNTIMES(fsp, ft) == 0) {
1182 return 0;
1185 if((errno != EPERM) && (errno != EACCES)) {
1186 return -1;
1189 if(!lp_dos_filetimes(SNUM(conn))) {
1190 return -1;
1193 /* We have permission (given by the Samba admin) to
1194 break POSIX semantics and allow a user to change
1195 the time on a file they don't own but can write to
1196 (as DOS does).
1199 /* Check if we have write access. */
1200 if (can_write_to_fsp(fsp)) {
1201 /* We are allowed to become root and change the filetime. */
1202 become_root();
1203 ret = SMB_VFS_FNTIMES(fsp, ft);
1204 unbecome_root();
1207 return ret;
1210 /******************************************************************
1211 Force a "sticky" write time on a pathname. This will always be
1212 returned on all future write time queries and set on close.
1213 ******************************************************************/
1215 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1217 if (is_omit_timespec(&mtime)) {
1218 return true;
1221 if (!set_sticky_write_time(fileid, mtime)) {
1222 return false;
1225 return true;
1228 /******************************************************************
1229 Force a "sticky" write time on an fsp. This will always be
1230 returned on all future write time queries and set on close.
1231 ******************************************************************/
1233 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1235 if (is_omit_timespec(&mtime)) {
1236 return true;
1239 fsp->fsp_flags.write_time_forced = true;
1240 TALLOC_FREE(fsp->update_write_time_event);
1242 return set_sticky_write_time_path(fsp->file_id, mtime);
1245 /******************************************************************
1246 Set a create time EA.
1247 ******************************************************************/
1249 NTSTATUS set_create_timespec_ea(struct files_struct *fsp,
1250 struct timespec create_time)
1252 uint32_t dosmode;
1253 int ret;
1255 if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
1256 return NT_STATUS_OK;
1259 dosmode = fdos_mode(fsp);
1261 fsp->fsp_name->st.st_ex_btime = create_time;
1262 ret = file_set_dosmode(fsp->conn, fsp->fsp_name, dosmode, NULL, false);
1263 if (ret == -1) {
1264 return map_nt_error_from_unix(errno);
1267 DBG_DEBUG("wrote create time EA for file %s\n",
1268 smb_fname_str_dbg(fsp->fsp_name));
1270 return NT_STATUS_OK;
1273 /******************************************************************
1274 Return a create time.
1275 ******************************************************************/
1277 struct timespec get_create_timespec(connection_struct *conn,
1278 struct files_struct *fsp,
1279 const struct smb_filename *smb_fname)
1281 return smb_fname->st.st_ex_btime;
1284 /******************************************************************
1285 Return a change time (may look at EA in future).
1286 ******************************************************************/
1288 struct timespec get_change_timespec(connection_struct *conn,
1289 struct files_struct *fsp,
1290 const struct smb_filename *smb_fname)
1292 return smb_fname->st.st_ex_mtime;