s3: smbd. Generic fix for incorrect reporting of stream dos attributes on a directory
[Samba.git] / source3 / smbd / dosmode.c
blob7ac876a47bf66eeb9efd9d6e59c1a794d224bedc
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 "system/filesys.h"
23 #include "librpc/gen_ndr/ndr_xattr.h"
24 #include "librpc/gen_ndr/ioctl.h"
25 #include "../libcli/security/security.h"
26 #include "smbd/smbd.h"
27 #include "lib/param/loadparm.h"
29 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
30 const struct smb_filename *smb_fname,
31 files_struct **ret_fsp,
32 bool *need_close);
34 static void dos_mode_debug_print(const char *func, uint32_t mode)
36 fstring modestr;
38 if (DEBUGLEVEL < DBGLVL_INFO) {
39 return;
42 modestr[0] = '\0';
44 if (mode & FILE_ATTRIBUTE_HIDDEN) {
45 fstrcat(modestr, "h");
47 if (mode & FILE_ATTRIBUTE_READONLY) {
48 fstrcat(modestr, "r");
50 if (mode & FILE_ATTRIBUTE_SYSTEM) {
51 fstrcat(modestr, "s");
53 if (mode & FILE_ATTRIBUTE_DIRECTORY) {
54 fstrcat(modestr, "d");
56 if (mode & FILE_ATTRIBUTE_ARCHIVE) {
57 fstrcat(modestr, "a");
59 if (mode & FILE_ATTRIBUTE_SPARSE) {
60 fstrcat(modestr, "[sparse]");
62 if (mode & FILE_ATTRIBUTE_OFFLINE) {
63 fstrcat(modestr, "[offline]");
65 if (mode & FILE_ATTRIBUTE_COMPRESSED) {
66 fstrcat(modestr, "[compressed]");
69 DBG_INFO("%s returning (0x%x): \"%s\"\n", func, (unsigned)mode,
70 modestr);
73 static uint32_t filter_mode_by_protocol(uint32_t mode)
75 if (get_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 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
87 #ifdef S_ISLNK
88 #if LINKS_READ_ONLY
89 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
90 return FILE_ATTRIBUTE_READONLY;
91 #endif
92 #endif
93 return 0;
96 /****************************************************************************
97 Change a dos mode to a unix mode.
98 Base permission for files:
99 if creating file and inheriting (i.e. parent_dir != NULL)
100 apply read/write bits from parent directory.
101 else
102 everybody gets read bit set
103 dos readonly is represented in unix by removing everyone's write bit
104 dos archive is represented in unix by the user's execute bit
105 dos system is represented in unix by the group's execute bit
106 dos hidden is represented in unix by the other's execute bit
107 if !inheriting {
108 Then apply create mask,
109 then add force bits.
111 Base permission for directories:
112 dos directory is represented in unix by unix's dir bit and the exec bit
113 if !inheriting {
114 Then apply create mask,
115 then add force bits.
117 ****************************************************************************/
119 mode_t unix_mode(connection_struct *conn, int dosmode,
120 const struct smb_filename *smb_fname,
121 const char *inherit_from_dir)
123 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
124 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
125 * inheriting. */
127 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
128 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
131 if ((inherit_from_dir != NULL) && lp_inherit_permissions(SNUM(conn))) {
132 struct smb_filename *smb_fname_parent;
134 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
135 smb_fname_str_dbg(smb_fname),
136 inherit_from_dir));
138 smb_fname_parent = synthetic_smb_fname(talloc_tos(),
139 inherit_from_dir,
140 NULL,
141 NULL,
142 smb_fname->flags);
143 if (smb_fname_parent == NULL) {
144 DEBUG(1,("unix_mode(%s) failed, [dir %s]: No memory\n",
145 smb_fname_str_dbg(smb_fname),
146 inherit_from_dir));
147 return(0);
150 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
151 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
152 smb_fname_str_dbg(smb_fname),
153 inherit_from_dir, strerror(errno)));
154 TALLOC_FREE(smb_fname_parent);
155 return(0); /* *** shouldn't happen! *** */
158 /* Save for later - but explicitly remove setuid bit for safety. */
159 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
160 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
161 smb_fname_str_dbg(smb_fname), (int)dir_mode));
162 /* Clear "result" */
163 result = 0;
164 TALLOC_FREE(smb_fname_parent);
167 if (IS_DOS_DIR(dosmode)) {
168 /* We never make directories read only for the owner as under DOS a user
169 can always create a file in a read-only directory. */
170 result |= (S_IFDIR | S_IWUSR);
172 if (dir_mode) {
173 /* Inherit mode of parent directory. */
174 result |= dir_mode;
175 } else {
176 /* Provisionally add all 'x' bits */
177 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
179 /* Apply directory mask */
180 result &= lp_directory_mask(SNUM(conn));
181 /* Add in force bits */
182 result |= lp_force_directory_mode(SNUM(conn));
184 } else {
185 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
186 result |= S_IXUSR;
188 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
189 result |= S_IXGRP;
191 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
192 result |= S_IXOTH;
194 if (dir_mode) {
195 /* Inherit 666 component of parent directory mode */
196 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
197 } else {
198 /* Apply mode mask */
199 result &= lp_create_mask(SNUM(conn));
200 /* Add in force bits */
201 result |= lp_force_create_mode(SNUM(conn));
205 DBG_INFO("unix_mode(%s) returning 0%o\n",
206 smb_fname_str_dbg(smb_fname), (int)result);
208 return(result);
211 /****************************************************************************
212 Change a unix mode to a dos mode.
213 ****************************************************************************/
215 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
216 const struct smb_filename *smb_fname)
218 int result = 0;
219 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
221 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
222 /* if we can find out if a file is immutable we should report it r/o */
223 if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
224 result |= FILE_ATTRIBUTE_READONLY;
226 #endif
227 if (ro_opts == MAP_READONLY_YES) {
228 /* Original Samba method - map inverse of user "w" bit. */
229 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
230 result |= FILE_ATTRIBUTE_READONLY;
232 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
233 /* Check actual permissions for read-only. */
234 if (!can_write_to_file(conn, smb_fname)) {
235 result |= FILE_ATTRIBUTE_READONLY;
237 } /* Else never set the readonly bit. */
239 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
240 result |= FILE_ATTRIBUTE_ARCHIVE;
242 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
243 result |= FILE_ATTRIBUTE_SYSTEM;
245 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
246 result |= FILE_ATTRIBUTE_HIDDEN;
248 if (S_ISDIR(smb_fname->st.st_ex_mode))
249 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
251 result |= set_link_read_only_flag(&smb_fname->st);
253 dos_mode_debug_print(__func__, result);
255 return result;
258 /****************************************************************************
259 Get DOS attributes from an EA.
260 This can also pull the create time into the stat struct inside smb_fname.
261 ****************************************************************************/
263 NTSTATUS get_ea_dos_attribute(connection_struct *conn,
264 struct smb_filename *smb_fname,
265 uint32_t *pattr)
267 struct xattr_DOSATTRIB dosattrib;
268 enum ndr_err_code ndr_err;
269 DATA_BLOB blob;
270 ssize_t sizeret;
271 fstring attrstr;
272 uint32_t dosattr;
274 if (!lp_store_dos_attributes(SNUM(conn))) {
275 return NT_STATUS_NOT_IMPLEMENTED;
278 /* Don't reset pattr to zero as we may already have filename-based attributes we
279 need to preserve. */
281 sizeret = SMB_VFS_GETXATTR(conn, smb_fname,
282 SAMBA_XATTR_DOS_ATTRIB, attrstr,
283 sizeof(attrstr));
284 if (sizeret == -1 && errno == EACCES) {
285 int saved_errno = 0;
288 * According to MS-FSA 2.1.5.1.2.1 "Algorithm to Check Access to
289 * an Existing File" FILE_LIST_DIRECTORY on a directory implies
290 * FILE_READ_ATTRIBUTES for directory entries. Being able to
291 * stat() a file implies FILE_LIST_DIRECTORY for the directory
292 * containing the file.
295 if (!VALID_STAT(smb_fname->st)) {
297 * Safety net: dos_mode() already checks this, but as we
298 * become root based on this, add an additional layer of
299 * defense.
301 DBG_ERR("Rejecting root override, invalid stat [%s]\n",
302 smb_fname_str_dbg(smb_fname));
303 return NT_STATUS_ACCESS_DENIED;
306 become_root();
307 sizeret = SMB_VFS_GETXATTR(conn, smb_fname,
308 SAMBA_XATTR_DOS_ATTRIB,
309 attrstr,
310 sizeof(attrstr));
311 if (sizeret == -1) {
312 saved_errno = errno;
314 unbecome_root();
316 if (saved_errno != 0) {
317 errno = saved_errno;
320 if (sizeret == -1) {
321 DBG_INFO("Cannot get attribute "
322 "from EA on file %s: Error = %s\n",
323 smb_fname_str_dbg(smb_fname), strerror(errno));
324 return map_nt_error_from_unix(errno);
327 blob.data = (uint8_t *)attrstr;
328 blob.length = sizeret;
330 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
331 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
333 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
334 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
335 "from EA on file %s: Error = %s\n",
336 smb_fname_str_dbg(smb_fname),
337 ndr_errstr(ndr_err)));
338 return ndr_map_error2ntstatus(ndr_err);
341 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
342 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
344 switch (dosattrib.version) {
345 case 0xFFFF:
346 dosattr = dosattrib.info.compatinfoFFFF.attrib;
347 break;
348 case 1:
349 dosattr = dosattrib.info.info1.attrib;
350 if (!null_nttime(dosattrib.info.info1.create_time)) {
351 struct timespec create_time =
352 nt_time_to_unix_timespec(
353 dosattrib.info.info1.create_time);
355 update_stat_ex_create_time(&smb_fname->st,
356 create_time);
358 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
359 "set btime %s\n",
360 smb_fname_str_dbg(smb_fname),
361 time_to_asc(convert_timespec_to_time_t(
362 create_time)) ));
364 break;
365 case 2:
366 dosattr = dosattrib.info.oldinfo2.attrib;
367 /* Don't know what flags to check for this case. */
368 break;
369 case 3:
370 dosattr = dosattrib.info.info3.attrib;
371 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
372 !null_nttime(dosattrib.info.info3.create_time)) {
373 struct timespec create_time =
374 nt_time_to_unix_timespec(
375 dosattrib.info.info3.create_time);
377 update_stat_ex_create_time(&smb_fname->st,
378 create_time);
380 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
381 "set btime %s\n",
382 smb_fname_str_dbg(smb_fname),
383 time_to_asc(convert_timespec_to_time_t(
384 create_time)) ));
386 break;
387 default:
388 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
389 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
390 attrstr));
391 /* Should this be INTERNAL_ERROR? */
392 return NT_STATUS_INVALID_PARAMETER;
395 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
396 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
398 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
399 *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
401 dos_mode_debug_print(__func__, *pattr);
403 return NT_STATUS_OK;
406 /****************************************************************************
407 Set DOS attributes in an EA.
408 Also sets the create time.
409 ****************************************************************************/
411 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
412 const struct smb_filename *smb_fname,
413 uint32_t dosmode)
415 struct xattr_DOSATTRIB dosattrib;
416 enum ndr_err_code ndr_err;
417 DATA_BLOB blob;
418 int ret;
420 if (!lp_store_dos_attributes(SNUM(conn))) {
421 return NT_STATUS_NOT_IMPLEMENTED;
425 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
426 * vfs_default via DMAPI if that is enabled.
428 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
430 ZERO_STRUCT(dosattrib);
431 ZERO_STRUCT(blob);
433 dosattrib.version = 3;
434 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
435 XATTR_DOSINFO_CREATE_TIME;
436 dosattrib.info.info3.attrib = dosmode;
437 dosattrib.info.info3.create_time = unix_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_SETXATTR(conn, smb_fname,
461 SAMBA_XATTR_DOS_ATTRIB,
462 blob.data, blob.length, 0);
463 if (ret != 0) {
464 NTSTATUS status = NT_STATUS_OK;
465 bool need_close = false;
466 files_struct *fsp = NULL;
467 bool set_dosmode_ok = false;
469 if ((errno != EPERM) && (errno != EACCES)) {
470 DBG_INFO("Cannot set "
471 "attribute EA on file %s: Error = %s\n",
472 smb_fname_str_dbg(smb_fname), strerror(errno));
473 return map_nt_error_from_unix(errno);
476 /* We want DOS semantics, ie allow non owner with write permission to change the
477 bits on a file. Just like file_ntimes below.
480 /* Check if we have write access. */
481 if (!CAN_WRITE(conn)) {
482 return NT_STATUS_ACCESS_DENIED;
485 status = smbd_check_access_rights(conn, smb_fname, 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_file(conn, smb_fname);
495 if (!set_dosmode_ok) {
496 return NT_STATUS_ACCESS_DENIED;
500 * We need to get an open file handle to do the
501 * metadata operation under root.
504 status = get_file_handle_for_metadata(conn,
505 smb_fname,
506 &fsp,
507 &need_close);
508 if (!NT_STATUS_IS_OK(status)) {
509 return status;
512 become_root();
513 ret = SMB_VFS_FSETXATTR(fsp,
514 SAMBA_XATTR_DOS_ATTRIB,
515 blob.data, blob.length, 0);
516 if (ret == 0) {
517 status = NT_STATUS_OK;
519 unbecome_root();
520 if (need_close) {
521 close_file(NULL, fsp, NORMAL_CLOSE);
523 return status;
525 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
526 (unsigned int)dosmode,
527 smb_fname_str_dbg(smb_fname)));
528 return NT_STATUS_OK;
531 /****************************************************************************
532 Change a unix mode to a dos mode for an ms dfs link.
533 ****************************************************************************/
535 uint32_t dos_mode_msdfs(connection_struct *conn,
536 const struct smb_filename *smb_fname)
538 uint32_t result = 0;
540 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
542 if (!VALID_STAT(smb_fname->st)) {
543 return 0;
546 /* First do any modifications that depend on the path name. */
547 /* hide files with a name starting with a . */
548 if (lp_hide_dot_files(SNUM(conn))) {
549 const char *p = strrchr_m(smb_fname->base_name, '/');
550 if (p) {
551 p++;
552 } else {
553 p = smb_fname->base_name;
556 /* Only . and .. are not hidden. */
557 if (p[0] == '.' && !((p[1] == '\0') ||
558 (p[1] == '.' && p[2] == '\0'))) {
559 result |= FILE_ATTRIBUTE_HIDDEN;
563 result |= dos_mode_from_sbuf(conn, smb_fname);
565 /* Optimization : Only call is_hidden_path if it's not already
566 hidden. */
567 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
568 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
569 result |= FILE_ATTRIBUTE_HIDDEN;
572 if (result == 0) {
573 result = FILE_ATTRIBUTE_NORMAL;
576 result = filter_mode_by_protocol(result);
579 * Add in that it is a reparse point
581 result |= FILE_ATTRIBUTE_REPARSE_POINT;
583 dos_mode_debug_print(__func__, result);
585 return(result);
589 * check whether a file or directory is flagged as compressed.
591 static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
592 struct smb_filename *smb_fname,
593 bool *is_compressed)
595 NTSTATUS status;
596 uint16_t compression_fmt;
597 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
598 if (tmp_ctx == NULL) {
599 status = NT_STATUS_NO_MEMORY;
600 goto err_out;
603 status = SMB_VFS_GET_COMPRESSION(conn, tmp_ctx, NULL, smb_fname,
604 &compression_fmt);
605 if (!NT_STATUS_IS_OK(status)) {
606 goto err_ctx_free;
609 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
610 *is_compressed = true;
611 } else {
612 *is_compressed = false;
614 status = NT_STATUS_OK;
616 err_ctx_free:
617 talloc_free(tmp_ctx);
618 err_out:
619 return status;
622 static uint32_t dos_mode_from_name(connection_struct *conn,
623 const struct smb_filename *smb_fname,
624 uint32_t dosmode)
626 const char *p = NULL;
627 uint32_t result = dosmode;
629 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
630 lp_hide_dot_files(SNUM(conn)))
632 p = strrchr_m(smb_fname->base_name, '/');
633 if (p) {
634 p++;
635 } else {
636 p = smb_fname->base_name;
639 /* Only . and .. are not hidden. */
640 if ((p[0] == '.') &&
641 !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0')))
643 result |= FILE_ATTRIBUTE_HIDDEN;
647 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
648 IS_HIDDEN_PATH(conn, smb_fname->base_name))
650 result |= FILE_ATTRIBUTE_HIDDEN;
653 return result;
656 /****************************************************************************
657 Change a unix mode to a dos mode.
658 May also read the create timespec into the stat struct in smb_fname
659 if "store dos attributes" is true.
660 ****************************************************************************/
662 uint32_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
664 uint32_t result = 0;
665 NTSTATUS status = NT_STATUS_OK;
667 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
669 if (!VALID_STAT(smb_fname->st)) {
670 return 0;
673 /* Get the DOS attributes via the VFS if we can */
674 status = SMB_VFS_GET_DOS_ATTRIBUTES(conn, smb_fname, &result);
675 if (!NT_STATUS_IS_OK(status)) {
677 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
679 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
680 result |= dos_mode_from_sbuf(conn, smb_fname);
685 * According to MS-FSA a stream name does not have
686 * separate DOS attribute metadata, so we must return
687 * the DOS attribute from the base filename. With one caveat,
688 * a non-default stream name can never be a directory.
690 * As this is common to all streams data stores, we handle
691 * it here instead of inside all stream VFS modules.
693 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
696 if (is_ntfs_stream_smb_fname(smb_fname)) {
697 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
698 if (!is_ntfs_default_stream_smb_fname(smb_fname)) {
700 * Non-default stream name, not a posix path.
702 result &= ~(FILE_ATTRIBUTE_DIRECTORY);
706 if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
707 bool compressed = false;
708 status = dos_mode_check_compressed(conn, smb_fname,
709 &compressed);
710 if (NT_STATUS_IS_OK(status) && compressed) {
711 result |= FILE_ATTRIBUTE_COMPRESSED;
715 result |= dos_mode_from_name(conn, smb_fname, result);
717 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
718 result |= FILE_ATTRIBUTE_DIRECTORY;
719 } else if (result == 0) {
720 result = FILE_ATTRIBUTE_NORMAL;
723 result = filter_mode_by_protocol(result);
725 dos_mode_debug_print(__func__, result);
727 return result;
730 /*******************************************************************
731 chmod a file - but preserve some bits.
732 If "store dos attributes" is also set it will store the create time
733 from the stat struct in smb_fname (in NTTIME format) in the EA
734 attribute also.
735 ********************************************************************/
737 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
738 uint32_t dosmode, const char *parent_dir, bool newfile)
740 int mask=0;
741 mode_t tmp;
742 mode_t unixmode;
743 int ret = -1, lret = -1;
744 files_struct *fsp = NULL;
745 bool need_close = false;
746 NTSTATUS status;
748 if (!CAN_WRITE(conn)) {
749 errno = EROFS;
750 return -1;
753 dosmode &= SAMBA_ATTRIBUTES_MASK;
755 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
756 dosmode, smb_fname_str_dbg(smb_fname)));
758 unixmode = smb_fname->st.st_ex_mode;
760 get_acl_group_bits(conn, smb_fname,
761 &smb_fname->st.st_ex_mode);
763 if (S_ISDIR(smb_fname->st.st_ex_mode))
764 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
765 else
766 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
768 /* Store the DOS attributes in an EA by preference. */
769 status = SMB_VFS_SET_DOS_ATTRIBUTES(conn, smb_fname, dosmode);
770 if (NT_STATUS_IS_OK(status)) {
771 if (!newfile) {
772 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
773 FILE_NOTIFY_CHANGE_ATTRIBUTES,
774 smb_fname->base_name);
776 smb_fname->st.st_ex_mode = unixmode;
777 return 0;
778 } else {
780 * Only fall back to using UNIX modes if
781 * we get NOT_IMPLEMENTED.
783 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
784 errno = map_errno_from_nt_status(status);
785 return -1;
789 /* Fall back to UNIX modes. */
790 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
792 /* preserve the file type bits */
793 mask |= S_IFMT;
795 /* preserve the s bits */
796 mask |= (S_ISUID | S_ISGID);
798 /* preserve the t bit */
799 #ifdef S_ISVTX
800 mask |= S_ISVTX;
801 #endif
803 /* possibly preserve the x bits */
804 if (!MAP_ARCHIVE(conn))
805 mask |= S_IXUSR;
806 if (!MAP_SYSTEM(conn))
807 mask |= S_IXGRP;
808 if (!MAP_HIDDEN(conn))
809 mask |= S_IXOTH;
811 unixmode |= (smb_fname->st.st_ex_mode & mask);
813 /* if we previously had any r bits set then leave them alone */
814 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
815 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
816 unixmode |= tmp;
819 /* if we previously had any w bits set then leave them alone
820 whilst adding in the new w bits, if the new mode is not rdonly */
821 if (!IS_DOS_READONLY(dosmode)) {
822 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
826 * From the chmod 2 man page:
828 * "If the calling process is not privileged, and the group of the file
829 * does not match the effective group ID of the process or one of its
830 * supplementary group IDs, the S_ISGID bit will be turned off, but
831 * this will not cause an error to be returned."
833 * Simply refuse to do the chmod in this case.
836 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
837 geteuid() != sec_initial_uid() &&
838 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
839 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
840 "set for directory %s\n",
841 smb_fname_str_dbg(smb_fname)));
842 errno = EPERM;
843 return -1;
846 ret = SMB_VFS_CHMOD(conn, smb_fname, unixmode);
847 if (ret == 0) {
848 if(!newfile || (lret != -1)) {
849 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
850 FILE_NOTIFY_CHANGE_ATTRIBUTES,
851 smb_fname->base_name);
853 smb_fname->st.st_ex_mode = unixmode;
854 return 0;
857 if((errno != EPERM) && (errno != EACCES))
858 return -1;
860 if(!lp_dos_filemode(SNUM(conn)))
861 return -1;
863 /* We want DOS semantics, ie allow non owner with write permission to change the
864 bits on a file. Just like file_ntimes below.
867 if (!can_write_to_file(conn, smb_fname)) {
868 errno = EACCES;
869 return -1;
873 * We need to get an open file handle to do the
874 * metadata operation under root.
877 status = get_file_handle_for_metadata(conn,
878 smb_fname,
879 &fsp,
880 &need_close);
881 if (!NT_STATUS_IS_OK(status)) {
882 errno = map_errno_from_nt_status(status);
883 return -1;
886 become_root();
887 ret = SMB_VFS_FCHMOD(fsp, unixmode);
888 unbecome_root();
889 if (need_close) {
890 close_file(NULL, fsp, NORMAL_CLOSE);
892 if (!newfile) {
893 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
894 FILE_NOTIFY_CHANGE_ATTRIBUTES,
895 smb_fname->base_name);
897 if (ret == 0) {
898 smb_fname->st.st_ex_mode = unixmode;
901 return( ret );
905 NTSTATUS file_set_sparse(connection_struct *conn,
906 files_struct *fsp,
907 bool sparse)
909 uint32_t old_dosmode;
910 uint32_t new_dosmode;
911 NTSTATUS status;
913 if (!CAN_WRITE(conn)) {
914 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
915 "on readonly share[%s]\n",
916 smb_fname_str_dbg(fsp->fsp_name),
917 sparse,
918 lp_servicename(talloc_tos(), SNUM(conn))));
919 return NT_STATUS_MEDIA_WRITE_PROTECTED;
923 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
924 * following access flags are granted.
926 if ((fsp->access_mask & (FILE_WRITE_DATA
927 | FILE_WRITE_ATTRIBUTES
928 | SEC_FILE_APPEND_DATA)) == 0) {
929 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
930 "access_mask[0x%08X] - access denied\n",
931 smb_fname_str_dbg(fsp->fsp_name),
932 sparse,
933 fsp->access_mask));
934 return NT_STATUS_ACCESS_DENIED;
937 if (fsp->is_directory) {
938 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
939 (sparse ? "set" : "clear"),
940 smb_fname_str_dbg(fsp->fsp_name)));
941 return NT_STATUS_INVALID_PARAMETER;
944 if (IS_IPC(conn) || IS_PRINT(conn)) {
945 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
946 (sparse ? "set" : "clear")));
947 return NT_STATUS_INVALID_PARAMETER;
950 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
951 sparse, smb_fname_str_dbg(fsp->fsp_name)));
953 if (!lp_store_dos_attributes(SNUM(conn))) {
954 return NT_STATUS_INVALID_DEVICE_REQUEST;
957 status = vfs_stat_fsp(fsp);
958 if (!NT_STATUS_IS_OK(status)) {
959 return status;
962 old_dosmode = dos_mode(conn, fsp->fsp_name);
964 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
965 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
966 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
967 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
968 } else {
969 return NT_STATUS_OK;
972 /* Store the DOS attributes in an EA. */
973 status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
974 if (!NT_STATUS_IS_OK(status)) {
975 return status;
978 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
979 FILE_NOTIFY_CHANGE_ATTRIBUTES,
980 fsp->fsp_name->base_name);
982 fsp->is_sparse = sparse;
984 return NT_STATUS_OK;
987 /*******************************************************************
988 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
989 than POSIX.
990 *******************************************************************/
992 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
993 struct smb_file_time *ft)
995 int ret = -1;
997 errno = 0;
999 DEBUG(6, ("file_ntime: actime: %s",
1000 time_to_asc(convert_timespec_to_time_t(ft->atime))));
1001 DEBUG(6, ("file_ntime: modtime: %s",
1002 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
1003 DEBUG(6, ("file_ntime: ctime: %s",
1004 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
1005 DEBUG(6, ("file_ntime: createtime: %s",
1006 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
1008 /* Don't update the time on read-only shares */
1009 /* We need this as set_filetime (which can be called on
1010 close and other paths) can end up calling this function
1011 without the NEED_WRITE protection. Found by :
1012 Leo Weppelman <leo@wau.mis.ah.nl>
1015 if (!CAN_WRITE(conn)) {
1016 return 0;
1019 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
1020 return 0;
1023 if((errno != EPERM) && (errno != EACCES)) {
1024 return -1;
1027 if(!lp_dos_filetimes(SNUM(conn))) {
1028 return -1;
1031 /* We have permission (given by the Samba admin) to
1032 break POSIX semantics and allow a user to change
1033 the time on a file they don't own but can write to
1034 (as DOS does).
1037 /* Check if we have write access. */
1038 if (can_write_to_file(conn, smb_fname)) {
1039 /* We are allowed to become root and change the filetime. */
1040 become_root();
1041 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1042 unbecome_root();
1045 return ret;
1048 /******************************************************************
1049 Force a "sticky" write time on a pathname. This will always be
1050 returned on all future write time queries and set on close.
1051 ******************************************************************/
1053 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1055 if (null_timespec(mtime)) {
1056 return true;
1059 if (!set_sticky_write_time(fileid, mtime)) {
1060 return false;
1063 return true;
1066 /******************************************************************
1067 Force a "sticky" write time on an fsp. This will always be
1068 returned on all future write time queries and set on close.
1069 ******************************************************************/
1071 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1073 if (null_timespec(mtime)) {
1074 return true;
1077 fsp->write_time_forced = true;
1078 TALLOC_FREE(fsp->update_write_time_event);
1080 return set_sticky_write_time_path(fsp->file_id, mtime);
1083 /******************************************************************
1084 Set a create time EA.
1085 ******************************************************************/
1087 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1088 const struct smb_filename *psmb_fname,
1089 struct timespec create_time)
1091 struct smb_filename *smb_fname;
1092 uint32_t dosmode;
1093 int ret;
1095 if (!lp_store_dos_attributes(SNUM(conn))) {
1096 return NT_STATUS_OK;
1099 smb_fname = synthetic_smb_fname(talloc_tos(),
1100 psmb_fname->base_name,
1101 NULL,
1102 &psmb_fname->st,
1103 psmb_fname->flags);
1105 if (smb_fname == NULL) {
1106 return NT_STATUS_NO_MEMORY;
1109 dosmode = dos_mode(conn, smb_fname);
1111 smb_fname->st.st_ex_btime = create_time;
1113 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1114 if (ret == -1) {
1115 return map_nt_error_from_unix(errno);
1118 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1119 smb_fname_str_dbg(smb_fname)));
1121 return NT_STATUS_OK;
1124 /******************************************************************
1125 Return a create time.
1126 ******************************************************************/
1128 struct timespec get_create_timespec(connection_struct *conn,
1129 struct files_struct *fsp,
1130 const struct smb_filename *smb_fname)
1132 return smb_fname->st.st_ex_btime;
1135 /******************************************************************
1136 Return a change time (may look at EA in future).
1137 ******************************************************************/
1139 struct timespec get_change_timespec(connection_struct *conn,
1140 struct files_struct *fsp,
1141 const struct smb_filename *smb_fname)
1143 return smb_fname->st.st_ex_mtime;
1146 /****************************************************************************
1147 Get a real open file handle we can do meta-data operations on. As it's
1148 going to be used under root access only on meta-data we should look for
1149 any existing open file handle first, and use that in preference (also to
1150 avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1151 ****************************************************************************/
1153 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1154 const struct smb_filename *smb_fname,
1155 files_struct **ret_fsp,
1156 bool *need_close)
1158 NTSTATUS status;
1159 files_struct *fsp;
1160 struct file_id file_id;
1161 struct smb_filename *smb_fname_cp = NULL;
1163 *need_close = false;
1165 if (!VALID_STAT(smb_fname->st)) {
1166 return NT_STATUS_INVALID_PARAMETER;
1169 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1171 for(fsp = file_find_di_first(conn->sconn, file_id);
1172 fsp;
1173 fsp = file_find_di_next(fsp)) {
1174 if (fsp->fh->fd != -1) {
1175 *ret_fsp = fsp;
1176 return NT_STATUS_OK;
1180 smb_fname_cp = cp_smb_filename(talloc_tos(),
1181 smb_fname);
1182 if (smb_fname_cp == NULL) {
1183 return NT_STATUS_NO_MEMORY;
1186 /* Opens an INTERNAL_OPEN_ONLY write handle. */
1187 status = SMB_VFS_CREATE_FILE(
1188 conn, /* conn */
1189 NULL, /* req */
1190 0, /* root_dir_fid */
1191 smb_fname_cp, /* fname */
1192 FILE_WRITE_ATTRIBUTES, /* access_mask */
1193 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1194 FILE_SHARE_DELETE),
1195 FILE_OPEN, /* create_disposition*/
1196 0, /* create_options */
1197 0, /* file_attributes */
1198 INTERNAL_OPEN_ONLY, /* oplock_request */
1199 NULL, /* lease */
1200 0, /* allocation_size */
1201 0, /* private_flags */
1202 NULL, /* sd */
1203 NULL, /* ea_list */
1204 ret_fsp, /* result */
1205 NULL, /* pinfo */
1206 NULL, NULL); /* create context */
1208 TALLOC_FREE(smb_fname_cp);
1210 if (NT_STATUS_IS_OK(status)) {
1211 *need_close = true;
1213 return status;