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/>.
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
)
37 if (DEBUGLEVEL
< DBGLVL_INFO
) {
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
,
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",
78 (unsigned int)(mode
& 0x3f) ));
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.
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
96 Then apply create mask,
99 Base permission for directories:
100 dos directory is represented in unix by unix's dir bit and the exec bit
102 Then apply create mask,
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
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, };
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
);
129 DBG_ERR("fstat failed [%s]: %s\n",
130 smb_fname_str_dbg(parent_dirfsp
->fsp_name
),
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
));
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
);
149 /* Inherit mode of parent directory. */
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
));
161 if (lp_map_archive(SNUM(conn
)) && IS_DOS_ARCHIVE(dosmode
))
164 if (lp_map_system(SNUM(conn
)) && IS_DOS_SYSTEM(dosmode
))
167 if (lp_map_hidden(SNUM(conn
)) && IS_DOS_HIDDEN(dosmode
))
171 /* Inherit 666 component of parent directory mode */
172 result
|= dir_mode
& (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IWGRP
| S_IWOTH
);
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
);
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
)
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
;
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
);
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
,
245 struct xattr_DOSATTRIB dosattrib
;
246 enum ndr_err_code ndr_err
;
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
) {
265 dosattr
= dosattrib
.info
.compatinfoFFFF
.attrib
;
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
,
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(
284 dosattr
= dosattrib
.info
.oldinfo2
.attrib
;
285 /* Don't know what flags to check for this case. */
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
,
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(
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
;
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
));
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
);
354 NTSTATUS
fget_ea_dos_attribute(struct files_struct
*fsp
,
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
369 sizeret
= SMB_VFS_FGETXATTR(fsp
,
370 SAMBA_XATTR_DOS_ATTRIB
,
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
380 sizeret
= SMB_VFS_FGETXATTR(fsp
,
381 SAMBA_XATTR_DOS_ATTRIB
,
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
)) {
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
,
413 struct xattr_DOSATTRIB dosattrib
= { .version
= 0, };
414 enum ndr_err_code ndr_err
;
415 DATA_BLOB blob
= { .data
= NULL
, };
416 struct timespec btime
;
419 if (!lp_store_dos_attributes(SNUM(conn
))) {
420 return NT_STATUS_NOT_IMPLEMENTED
;
423 if (smb_fname
->fsp
== NULL
) {
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);
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
,
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
;
500 ret
= SMB_VFS_FSETXATTR(smb_fname
->fsp
,
501 SAMBA_XATTR_DOS_ATTRIB
,
502 blob
.data
, blob
.length
, 0);
504 status
= NT_STATUS_OK
;
507 if (!NT_STATUS_IS_OK(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
)));
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
)
537 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname
)));
539 if (!VALID_STAT(smb_fname
->st
)) {
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
, '/');
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
563 if (!(result
& FILE_ATTRIBUTE_HIDDEN
) &&
564 IS_HIDDEN_PATH(conn
, smb_fname
->base_name
)) {
565 result
|= FILE_ATTRIBUTE_HIDDEN
;
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
);
585 * check whether a file or directory is flagged as compressed.
587 static NTSTATUS
dos_mode_check_compressed(struct files_struct
*fsp
,
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
)) {
599 if (compression_fmt
== COMPRESSION_FORMAT_LZNT1
) {
600 *is_compressed
= true;
602 *is_compressed
= false;
607 static uint32_t dos_mode_from_name(connection_struct
*conn
,
608 const struct smb_filename
*smb_fname
,
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
, '/');
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
;
639 static uint32_t dos_mode_post(uint32_t dosmode
,
640 struct files_struct
*fsp
,
643 struct smb_filename
*smb_fname
= 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
);
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
)
700 NTSTATUS status
= NT_STATUS_OK
;
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
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
)) {
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
;
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
);
770 *state
= (struct dos_mode_at_state
) {
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
;
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
,
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
);
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
,
816 struct dos_mode_at_state
*state
=
818 struct dos_mode_at_state
);
819 struct vfs_aio_state aio_state
;
824 * Make sure we run as the user again
826 ok
= change_to_user_and_service_by_fsp(state
->dir_fsp
);
829 status
= SMB_VFS_GET_DOS_ATTRIBUTES_RECV(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
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
,
863 tevent_req_done(req
);
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
);
876 NTSTATUS
dos_mode_at_recv(struct tevent_req
*req
, uint32_t *dosmode
)
878 struct dos_mode_at_state
*state
=
880 struct dos_mode_at_state
);
883 if (tevent_req_is_nterror(req
, &status
)) {
884 tevent_req_received(req
);
888 *dosmode
= state
->dosmode
;
889 tevent_req_received(req
);
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
898 ********************************************************************/
900 int file_set_dosmode(connection_struct
*conn
,
901 struct smb_filename
*smb_fname
,
903 struct smb_filename
*parent_dir
,
912 if (!CAN_WRITE(conn
)) {
917 if ((S_ISDIR(smb_fname
->st
.st_ex_mode
)) &&
918 (dosmode
& FILE_ATTRIBUTE_TEMPORARY
))
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
) {
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
;
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
);
946 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
949 if (NT_STATUS_IS_OK(status
)) {
950 smb_fname
->st
.cached_dos_attributes
= dosmode
;
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
);
964 /* Fall back to UNIX modes. */
965 unixmode
= unix_mode(
969 parent_dir
!= NULL
? parent_dir
->fsp
: NULL
);
971 /* preserve the file type bits */
974 /* preserve the s bits */
975 mask
|= (S_ISUID
| S_ISGID
);
977 /* preserve the t bit */
982 /* possibly preserve the x bits */
983 if (!MAP_ARCHIVE(conn
))
985 if (!MAP_SYSTEM(conn
))
987 if (!MAP_HIDDEN(conn
))
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
);
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
)));
1027 ret
= SMB_VFS_FCHMOD(smb_fname
->fsp
, unixmode
);
1032 if((errno
!= EPERM
) && (errno
!= EACCES
))
1035 if(!lp_dos_filemode(SNUM(conn
)))
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
))
1049 ret
= SMB_VFS_FCHMOD(smb_fname
->fsp
, unixmode
);
1054 notify_fname(conn
, NOTIFY_ACTION_MODIFIED
,
1055 FILE_NOTIFY_CHANGE_ATTRIBUTES
,
1056 smb_fname
->base_name
);
1059 smb_fname
->st
.st_ex_mode
= unixmode
;
1066 NTSTATUS
file_set_sparse(connection_struct
*conn
,
1070 const struct loadparm_substitution
*lp_sub
=
1071 loadparm_s3_global_substitution();
1072 uint32_t old_dosmode
;
1073 uint32_t new_dosmode
;
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
),
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
),
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
)) {
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
;
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
)) {
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
1167 *******************************************************************/
1169 int file_ntimes(connection_struct
*conn
,
1171 struct smb_file_time
*ft
)
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
)) {
1197 if (SMB_VFS_FNTIMES(fsp
, ft
) == 0) {
1201 if((errno
!= EPERM
) && (errno
!= EACCES
)) {
1205 if(!lp_dos_filetimes(SNUM(conn
))) {
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
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. */
1219 ret
= SMB_VFS_FNTIMES(fsp
, ft
);
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
)) {
1237 if (!set_sticky_write_time(fileid
, mtime
)) {
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
)) {
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
)
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);
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
;