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(enum protocol_types protocol
,
75 if (protocol
<= PROTOCOL_LANMAN2
) {
76 DEBUG(10,("filter_mode_by_protocol: "
77 "filtering result 0x%x to 0x%x\n",
79 (unsigned int)(mode
& 0x3f) ));
85 /****************************************************************************
86 Change a dos mode to a unix mode.
87 Base permission for files:
88 if creating file and inheriting (i.e. parent_dir != NULL)
89 apply read/write bits from parent directory.
91 everybody gets read bit set
92 dos readonly is represented in unix by removing everyone's write bit
93 dos archive is represented in unix by the user's execute bit
94 dos system is represented in unix by the group's execute bit
95 dos hidden is represented in unix by the other's execute bit
97 Then apply create mask,
100 Base permission for directories:
101 dos directory is represented in unix by unix's dir bit and the exec bit
103 Then apply create mask,
106 ****************************************************************************/
108 mode_t
unix_mode(connection_struct
*conn
, int dosmode
,
109 const struct smb_filename
*smb_fname
,
110 struct files_struct
*parent_dirfsp
)
112 mode_t result
= (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IWGRP
| S_IWOTH
);
113 mode_t dir_mode
= 0; /* Mode of the inherit_from directory if
116 if ((dosmode
& FILE_ATTRIBUTE_READONLY
) &&
117 !lp_store_dos_attributes(SNUM(conn
))) {
118 result
&= ~(S_IWUSR
| S_IWGRP
| S_IWOTH
);
121 if ((parent_dirfsp
!= NULL
) && lp_inherit_permissions(SNUM(conn
))) {
122 struct stat_ex sbuf
= { .st_ex_nlink
= 0, };
125 DBG_DEBUG("[%s] inheriting from [%s]\n",
126 smb_fname_str_dbg(smb_fname
),
127 smb_fname_str_dbg(parent_dirfsp
->fsp_name
));
129 ret
= SMB_VFS_FSTAT(parent_dirfsp
, &sbuf
);
131 DBG_ERR("fstat failed [%s]: %s\n",
132 smb_fname_str_dbg(parent_dirfsp
->fsp_name
),
134 return(0); /* *** shouldn't happen! *** */
137 /* Save for later - but explicitly remove setuid bit for safety. */
138 dir_mode
= sbuf
.st_ex_mode
& ~S_ISUID
;
139 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
140 smb_fname_str_dbg(smb_fname
), (int)dir_mode
));
145 if (dosmode
& FILE_ATTRIBUTE_DIRECTORY
) {
146 /* We never make directories read only for the owner as under DOS a user
147 can always create a file in a read-only directory. */
148 result
|= (S_IFDIR
| S_IWUSR
);
151 /* Inherit mode of parent directory. */
154 /* Provisionally add all 'x' bits */
155 result
|= (S_IXUSR
| S_IXGRP
| S_IXOTH
);
157 /* Apply directory mask */
158 result
&= lp_directory_mask(SNUM(conn
));
159 /* Add in force bits */
160 result
|= lp_force_directory_mode(SNUM(conn
));
163 if ((dosmode
& FILE_ATTRIBUTE_ARCHIVE
) &&
164 lp_map_archive(SNUM(conn
))) {
168 if ((dosmode
& FILE_ATTRIBUTE_SYSTEM
) &&
169 lp_map_system(SNUM(conn
))) {
173 if ((dosmode
& FILE_ATTRIBUTE_HIDDEN
) &&
174 lp_map_hidden(SNUM(conn
))) {
179 /* Inherit 666 component of parent directory mode */
180 result
|= dir_mode
& (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IWGRP
| S_IWOTH
);
182 /* Apply mode mask */
183 result
&= lp_create_mask(SNUM(conn
));
184 /* Add in force bits */
185 result
|= lp_force_create_mode(SNUM(conn
));
189 DBG_INFO("unix_mode(%s) returning 0%o\n",
190 smb_fname_str_dbg(smb_fname
), (int)result
);
195 /****************************************************************************
196 Change a unix mode to a dos mode.
197 ****************************************************************************/
199 static uint32_t dos_mode_from_sbuf(connection_struct
*conn
,
200 const struct stat_ex
*st
,
201 struct files_struct
*fsp
)
204 enum mapreadonly_options ro_opts
=
205 (enum mapreadonly_options
)lp_map_readonly(SNUM(conn
));
207 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
208 /* if we can find out if a file is immutable we should report it r/o */
209 if (st
->st_ex_flags
& (UF_IMMUTABLE
| SF_IMMUTABLE
)) {
210 result
|= FILE_ATTRIBUTE_READONLY
;
213 if (ro_opts
== MAP_READONLY_YES
) {
214 /* Original Samba method - map inverse of user "w" bit. */
215 if ((st
->st_ex_mode
& S_IWUSR
) == 0) {
216 result
|= FILE_ATTRIBUTE_READONLY
;
218 } else if (ro_opts
== MAP_READONLY_PERMISSIONS
) {
219 /* smb_fname->fsp can be NULL for an MS-DFS link. */
220 /* Check actual permissions for read-only. */
221 if ((fsp
!= NULL
) && !can_write_to_fsp(fsp
)) {
222 result
|= FILE_ATTRIBUTE_READONLY
;
224 } /* Else never set the readonly bit. */
226 if (MAP_ARCHIVE(conn
) && ((st
->st_ex_mode
& S_IXUSR
) != 0)) {
227 result
|= FILE_ATTRIBUTE_ARCHIVE
;
230 if (MAP_SYSTEM(conn
) && ((st
->st_ex_mode
& S_IXGRP
) != 0)) {
231 result
|= FILE_ATTRIBUTE_SYSTEM
;
234 if (MAP_HIDDEN(conn
) && ((st
->st_ex_mode
& S_IXOTH
) != 0)) {
235 result
|= FILE_ATTRIBUTE_HIDDEN
;
238 if (S_ISDIR(st
->st_ex_mode
)) {
239 result
= FILE_ATTRIBUTE_DIRECTORY
|
240 (result
& FILE_ATTRIBUTE_READONLY
);
243 dos_mode_debug_print(__func__
, result
);
248 /****************************************************************************
249 Get DOS attributes from an EA.
250 This can also pull the create time into the stat struct inside smb_fname.
251 ****************************************************************************/
253 NTSTATUS
parse_dos_attribute_blob(struct smb_filename
*smb_fname
,
257 struct xattr_DOSATTRIB dosattrib
;
258 enum ndr_err_code ndr_err
;
261 ndr_err
= ndr_pull_struct_blob(&blob
, talloc_tos(), &dosattrib
,
262 (ndr_pull_flags_fn_t
)ndr_pull_xattr_DOSATTRIB
);
264 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
265 DBG_WARNING("bad ndr decode "
266 "from EA on file %s: Error = %s\n",
267 smb_fname_str_dbg(smb_fname
),
268 ndr_errstr(ndr_err
));
269 return ndr_map_error2ntstatus(ndr_err
);
272 DBG_DEBUG("%s attr = %s\n",
273 smb_fname_str_dbg(smb_fname
), dosattrib
.attrib_hex
);
275 switch (dosattrib
.version
) {
277 dosattr
= dosattrib
.info
.compatinfoFFFF
.attrib
;
280 dosattr
= dosattrib
.info
.info1
.attrib
;
281 if (!null_nttime(dosattrib
.info
.info1
.create_time
)) {
282 struct timespec create_time
=
283 nt_time_to_unix_timespec(
284 dosattrib
.info
.info1
.create_time
);
286 update_stat_ex_create_time(&smb_fname
->st
,
289 DBG_DEBUG("file %s case 1 set btime %s",
290 smb_fname_str_dbg(smb_fname
),
291 time_to_asc(convert_timespec_to_time_t(
296 dosattr
= dosattrib
.info
.oldinfo2
.attrib
;
297 /* Don't know what flags to check for this case. */
300 dosattr
= dosattrib
.info
.info3
.attrib
;
301 if ((dosattrib
.info
.info3
.valid_flags
& XATTR_DOSINFO_CREATE_TIME
) &&
302 !null_nttime(dosattrib
.info
.info3
.create_time
)) {
303 struct timespec create_time
=
304 nt_time_to_full_timespec(
305 dosattrib
.info
.info3
.create_time
);
307 update_stat_ex_create_time(&smb_fname
->st
,
310 DBG_DEBUG("file %s case 3 set btime %s",
311 smb_fname_str_dbg(smb_fname
),
312 time_to_asc(convert_timespec_to_time_t(
319 uint32_t info_valid_flags
;
320 NTTIME info_create_time
;
322 if (dosattrib
.version
== 4) {
323 info_valid_flags
= dosattrib
.info
.info4
.valid_flags
;
324 info_create_time
= dosattrib
.info
.info4
.create_time
;
325 dosattr
= dosattrib
.info
.info4
.attrib
;
327 info_valid_flags
= dosattrib
.info
.info5
.valid_flags
;
328 info_create_time
= dosattrib
.info
.info5
.create_time
;
329 dosattr
= dosattrib
.info
.info5
.attrib
;
332 if ((info_valid_flags
& XATTR_DOSINFO_CREATE_TIME
) &&
333 !null_nttime(info_create_time
))
335 struct timespec creat_time
;
337 creat_time
= nt_time_to_full_timespec(info_create_time
);
338 update_stat_ex_create_time(&smb_fname
->st
, creat_time
);
340 DBG_DEBUG("file [%s] creation time [%s]\n",
341 smb_fname_str_dbg(smb_fname
),
342 nt_time_string(talloc_tos(), info_create_time
));
348 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
349 smb_fname_str_dbg(smb_fname
), blob
.data
);
350 /* Should this be INTERNAL_ERROR? */
351 return NT_STATUS_INVALID_PARAMETER
;
354 if (S_ISDIR(smb_fname
->st
.st_ex_mode
)) {
355 dosattr
|= FILE_ATTRIBUTE_DIRECTORY
;
359 * _SPARSE and _REPARSE_POINT are valid on get but not on
360 * set. Both are created via special fcntls.
363 dosattr
&= (SAMBA_ATTRIBUTES_MASK
|
364 FILE_ATTRIBUTE_SPARSE
|
365 FILE_ATTRIBUTE_REPARSE_POINT
);
369 dos_mode_debug_print(__func__
, *pattr
);
374 NTSTATUS
fget_ea_dos_attribute(struct files_struct
*fsp
,
382 if (!lp_store_dos_attributes(SNUM(fsp
->conn
))) {
383 return NT_STATUS_NOT_IMPLEMENTED
;
386 /* Don't reset pattr to zero as we may already have filename-based attributes we
389 sizeret
= SMB_VFS_FGETXATTR(fsp
,
390 SAMBA_XATTR_DOS_ATTRIB
,
393 if (sizeret
== -1 && ( errno
== EPERM
|| errno
== EACCES
)) {
394 /* we may also retrieve dos attribs for unreadable files, this
395 is why we'll retry as root. We don't use root in the first
396 run because in cases like NFS, root might have even less
397 rights than the real user
400 sizeret
= SMB_VFS_FGETXATTR(fsp
,
401 SAMBA_XATTR_DOS_ATTRIB
,
407 DBG_INFO("Cannot get attribute "
408 "from EA on file %s: Error = %s\n",
409 fsp_str_dbg(fsp
), strerror(errno
));
410 return map_nt_error_from_unix(errno
);
413 blob
.data
= (uint8_t *)attrstr
;
414 blob
.length
= sizeret
;
416 status
= parse_dos_attribute_blob(fsp
->fsp_name
, blob
, pattr
);
417 if (!NT_STATUS_IS_OK(status
)) {
424 /****************************************************************************
425 Set DOS attributes in an EA.
426 Also sets the create time.
427 ****************************************************************************/
429 NTSTATUS
set_ea_dos_attribute(connection_struct
*conn
,
430 struct smb_filename
*smb_fname
,
433 struct xattr_DOSATTRIB dosattrib
= { .version
= 0, };
434 enum ndr_err_code ndr_err
;
435 DATA_BLOB blob
= { .data
= NULL
, };
436 struct timespec btime
;
439 if (!lp_store_dos_attributes(SNUM(conn
))) {
440 return NT_STATUS_NOT_IMPLEMENTED
;
443 if (smb_fname
->fsp
== NULL
) {
445 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
448 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
449 * vfs_default via DMAPI if that is enabled.
451 dosmode
&= ~FILE_ATTRIBUTE_OFFLINE
;
453 dosattrib
.version
= 5;
454 dosattrib
.info
.info5
.valid_flags
= XATTR_DOSINFO_ATTRIB
|
455 XATTR_DOSINFO_CREATE_TIME
;
456 dosattrib
.info
.info5
.attrib
= dosmode
;
457 dosattrib
.info
.info5
.create_time
= full_timespec_to_nt_time(
458 &smb_fname
->st
.st_ex_btime
);
460 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
461 (unsigned int)dosmode
,
462 time_to_asc(convert_timespec_to_time_t(smb_fname
->st
.st_ex_btime
)),
463 smb_fname_str_dbg(smb_fname
) ));
465 ndr_err
= ndr_push_struct_blob(
466 &blob
, talloc_tos(), &dosattrib
,
467 (ndr_push_flags_fn_t
)ndr_push_xattr_DOSATTRIB
);
469 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
470 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
471 ndr_errstr(ndr_err
)));
472 return ndr_map_error2ntstatus(ndr_err
);
475 if (blob
.data
== NULL
|| blob
.length
== 0) {
476 /* Should this be INTERNAL_ERROR? */
477 return NT_STATUS_INVALID_PARAMETER
;
480 ret
= SMB_VFS_FSETXATTR(smb_fname
->fsp
,
481 SAMBA_XATTR_DOS_ATTRIB
,
482 blob
.data
, blob
.length
, 0);
484 NTSTATUS status
= NT_STATUS_OK
;
485 bool set_dosmode_ok
= false;
487 if ((errno
!= EPERM
) && (errno
!= EACCES
)) {
488 DBG_INFO("Cannot set "
489 "attribute EA on file %s: Error = %s\n",
490 smb_fname_str_dbg(smb_fname
), strerror(errno
));
491 return map_nt_error_from_unix(errno
);
494 /* We want DOS semantics, ie allow non owner with write permission to change the
495 bits on a file. Just like file_ntimes below.
498 /* Check if we have write access. */
499 if (!CAN_WRITE(conn
)) {
500 return NT_STATUS_ACCESS_DENIED
;
503 status
= smbd_check_access_rights_fsp(conn
->cwd_fsp
,
506 FILE_WRITE_ATTRIBUTES
);
507 if (NT_STATUS_IS_OK(status
)) {
508 set_dosmode_ok
= true;
511 if (!set_dosmode_ok
&& lp_dos_filemode(SNUM(conn
))) {
512 set_dosmode_ok
= can_write_to_fsp(smb_fname
->fsp
);
515 if (!set_dosmode_ok
) {
516 return NT_STATUS_ACCESS_DENIED
;
520 ret
= SMB_VFS_FSETXATTR(smb_fname
->fsp
,
521 SAMBA_XATTR_DOS_ATTRIB
,
522 blob
.data
, blob
.length
, 0);
524 status
= NT_STATUS_OK
;
527 if (!NT_STATUS_IS_OK(status
)) {
533 * We correctly stored the create time.
534 * We *always* set XATTR_DOSINFO_CREATE_TIME,
535 * so now it can no longer be considered
536 * calculated. Make sure to use the value rounded
537 * to NTTIME granularity we've stored in the xattr.
539 btime
= nt_time_to_full_timespec(dosattrib
.info
.info5
.create_time
);
540 update_stat_ex_create_time(&smb_fname
->st
, btime
);
542 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
543 (unsigned int)dosmode
,
544 smb_fname_str_dbg(smb_fname
)));
549 dos_mode_from_name(connection_struct
*conn
, const char *name
, uint32_t dosmode
)
551 const char *p
= NULL
;
552 uint32_t result
= dosmode
;
554 if (!(result
& FILE_ATTRIBUTE_HIDDEN
) &&
555 lp_hide_dot_files(SNUM(conn
)))
557 p
= strrchr_m(name
, '/');
564 /* Only . and .. are not hidden. */
565 if ((p
[0] == '.') && !(ISDOT(p
) || ISDOTDOT(p
))) {
566 result
|= FILE_ATTRIBUTE_HIDDEN
;
570 if (!(result
& FILE_ATTRIBUTE_HIDDEN
) && IS_HIDDEN_PATH(conn
, name
)) {
571 result
|= FILE_ATTRIBUTE_HIDDEN
;
577 /****************************************************************************
578 Change a unix mode to a dos mode for an ms dfs link.
579 ****************************************************************************/
581 uint32_t dos_mode_msdfs(connection_struct
*conn
,
583 const struct stat_ex
*st
)
587 DEBUG(8, ("dos_mode_msdfs: %s\n", name
));
589 if (!VALID_STAT(*st
)) {
593 result
= dos_mode_from_name(conn
, name
, result
);
594 result
|= dos_mode_from_sbuf(conn
, st
, NULL
);
597 result
= FILE_ATTRIBUTE_NORMAL
;
600 result
= filter_mode_by_protocol(conn_protocol(conn
->sconn
), result
);
603 * Add in that it is a reparse point
605 result
|= FILE_ATTRIBUTE_REPARSE_POINT
;
607 dos_mode_debug_print(__func__
, result
);
613 * check whether a file or directory is flagged as compressed.
615 static NTSTATUS
dos_mode_check_compressed(struct files_struct
*fsp
,
619 uint16_t compression_fmt
;
621 status
= SMB_VFS_FGET_COMPRESSION(
622 fsp
->conn
, talloc_tos(), fsp
, &compression_fmt
);
623 if (!NT_STATUS_IS_OK(status
)) {
627 if (compression_fmt
== COMPRESSION_FORMAT_LZNT1
) {
628 *is_compressed
= true;
630 *is_compressed
= false;
635 static uint32_t dos_mode_post(uint32_t dosmode
,
636 struct files_struct
*fsp
,
639 struct smb_filename
*smb_fname
= NULL
;
643 smb_fname
= fsp
->fsp_name
;
645 SMB_ASSERT(smb_fname
!= NULL
);
648 * According to MS-FSA a stream name does not have
649 * separate DOS attribute metadata, so we must return
650 * the DOS attribute from the base filename. With one caveat,
651 * a non-default stream name can never be a directory.
653 * As this is common to all streams data stores, we handle
654 * it here instead of inside all stream VFS modules.
656 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
659 if (is_named_stream(smb_fname
)) {
660 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
661 dosmode
&= ~(FILE_ATTRIBUTE_DIRECTORY
);
664 if (fsp
->conn
->fs_capabilities
& FILE_FILE_COMPRESSION
) {
665 bool compressed
= false;
667 status
= dos_mode_check_compressed(fsp
, &compressed
);
668 if (NT_STATUS_IS_OK(status
) && compressed
) {
669 dosmode
|= FILE_ATTRIBUTE_COMPRESSED
;
673 dosmode
|= dos_mode_from_name(fsp
->conn
, smb_fname
->base_name
, dosmode
);
675 if (S_ISDIR(smb_fname
->st
.st_ex_mode
)) {
676 dosmode
|= FILE_ATTRIBUTE_DIRECTORY
;
677 } else if (dosmode
== 0) {
678 dosmode
= FILE_ATTRIBUTE_NORMAL
;
681 dosmode
= filter_mode_by_protocol(conn_protocol(fsp
->conn
->sconn
),
684 dos_mode_debug_print(func
, dosmode
);
688 /****************************************************************************
689 Change a unix mode to a dos mode.
690 May also read the create timespec into the stat struct in smb_fname
691 if "store dos attributes" is true.
692 ****************************************************************************/
694 uint32_t fdos_mode(struct files_struct
*fsp
)
697 NTSTATUS status
= NT_STATUS_OK
;
699 DBG_DEBUG("%s\n", fsp_str_dbg(fsp
));
701 if (fsp
->fake_file_handle
!= NULL
) {
702 return dosmode_from_fake_filehandle(fsp
->fake_file_handle
);
705 if (!VALID_STAT(fsp
->fsp_name
->st
)) {
709 if (S_ISLNK(fsp
->fsp_name
->st
.st_ex_mode
)) {
710 return FILE_ATTRIBUTE_NORMAL
;
713 if (fsp
->fsp_name
->st
.cached_dos_attributes
!= FILE_ATTRIBUTE_INVALID
) {
714 return fsp
->fsp_name
->st
.cached_dos_attributes
;
717 /* Get the DOS attributes via the VFS if we can */
718 status
= SMB_VFS_FGET_DOS_ATTRIBUTES(fsp
->conn
,
721 if (!NT_STATUS_IS_OK(status
)) {
723 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
725 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_IMPLEMENTED
)) {
726 result
|= dos_mode_from_sbuf(fsp
->conn
,
732 fsp
->fsp_name
->st
.cached_dos_attributes
= dos_mode_post(result
, fsp
, __func__
);
733 return fsp
->fsp_name
->st
.cached_dos_attributes
;
736 struct dos_mode_at_state
{
737 files_struct
*dir_fsp
;
738 struct smb_filename
*smb_fname
;
742 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req
*subreq
);
744 struct tevent_req
*dos_mode_at_send(TALLOC_CTX
*mem_ctx
,
745 struct tevent_context
*ev
,
746 files_struct
*dir_fsp
,
747 struct smb_filename
*smb_fname
)
749 struct tevent_req
*req
= NULL
;
750 struct dos_mode_at_state
*state
= NULL
;
751 struct tevent_req
*subreq
= NULL
;
753 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname
));
755 req
= tevent_req_create(mem_ctx
, &state
,
756 struct dos_mode_at_state
);
761 *state
= (struct dos_mode_at_state
) {
763 .smb_fname
= smb_fname
,
766 if (!VALID_STAT(smb_fname
->st
)) {
767 tevent_req_done(req
);
768 return tevent_req_post(req
, ev
);
771 if (smb_fname
->fsp
== NULL
) {
772 if (ISDOTDOT(smb_fname
->base_name
)) {
774 * smb_fname->fsp is explicitly closed
775 * for ".." to prevent meta-data leakage.
777 state
->dosmode
= FILE_ATTRIBUTE_DIRECTORY
;
780 * This is a symlink in POSIX context.
781 * FIXME ? Should we move to returning
782 * FILE_ATTRIBUTE_REPARSE_POINT here ?
784 state
->dosmode
= FILE_ATTRIBUTE_NORMAL
;
786 tevent_req_done(req
);
787 return tevent_req_post(req
, ev
);
790 subreq
= SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state
,
794 if (tevent_req_nomem(subreq
, req
)) {
795 return tevent_req_post(req
, ev
);
797 tevent_req_set_callback(subreq
, dos_mode_at_vfs_get_dosmode_done
, req
);
802 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req
*subreq
)
804 struct tevent_req
*req
=
805 tevent_req_callback_data(subreq
,
807 struct dos_mode_at_state
*state
=
809 struct dos_mode_at_state
);
810 struct vfs_aio_state aio_state
;
815 * Make sure we run as the user again
817 ok
= change_to_user_and_service_by_fsp(state
->dir_fsp
);
820 status
= SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq
,
824 if (!NT_STATUS_IS_OK(status
)) {
826 * Both the sync dos_mode() as well as the async
827 * dos_mode_at_[send|recv] have no real error return, the only
828 * unhandled error is when the stat info in smb_fname is not
829 * valid (cf the checks in dos_mode() and dos_mode_at_send().
831 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
832 * dos_mode_post() which also does the mapping of a last resort
833 * from S_IFMT(st_mode).
835 * Only if we get NT_STATUS_NOT_IMPLEMENTED or
836 * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
837 * fallback to sync processing.
839 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_IMPLEMENTED
) &&
840 !NT_STATUS_EQUAL(status
, NT_STATUS_NOT_SUPPORTED
))
843 * state->dosmode should still be 0, but reset
847 status
= NT_STATUS_OK
;
850 if (NT_STATUS_IS_OK(status
)) {
851 state
->dosmode
= dos_mode_post(state
->dosmode
,
852 state
->smb_fname
->fsp
,
854 tevent_req_done(req
);
859 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
862 state
->dosmode
= fdos_mode(state
->smb_fname
->fsp
);
863 tevent_req_done(req
);
867 NTSTATUS
dos_mode_at_recv(struct tevent_req
*req
, uint32_t *dosmode
)
869 struct dos_mode_at_state
*state
=
871 struct dos_mode_at_state
);
874 if (tevent_req_is_nterror(req
, &status
)) {
875 tevent_req_received(req
);
879 *dosmode
= state
->dosmode
;
880 tevent_req_received(req
);
884 /*******************************************************************
885 chmod a file - but preserve some bits.
886 If "store dos attributes" is also set it will store the create time
887 from the stat struct in smb_fname (in NTTIME format) in the EA
889 ********************************************************************/
891 int file_set_dosmode(connection_struct
*conn
,
892 struct smb_filename
*smb_fname
,
894 struct smb_filename
*parent_dir
,
903 if (!CAN_WRITE(conn
)) {
908 if (S_ISLNK(smb_fname
->st
.st_ex_mode
)) {
909 /* A symlink in POSIX context, ignore */
913 if ((S_ISDIR(smb_fname
->st
.st_ex_mode
)) &&
914 (dosmode
& FILE_ATTRIBUTE_TEMPORARY
))
920 dosmode
&= SAMBA_ATTRIBUTES_MASK
;
922 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
923 dosmode
, smb_fname_str_dbg(smb_fname
)));
925 if (smb_fname
->fsp
== NULL
) {
930 if ((smb_fname
->fsp
->posix_flags
& FSP_POSIX_FLAGS_OPEN
) &&
931 !lp_store_dos_attributes(SNUM(conn
)))
936 unixmode
= smb_fname
->st
.st_ex_mode
;
938 get_acl_group_bits(conn
, smb_fname
->fsp
, &smb_fname
->st
.st_ex_mode
);
940 if (S_ISDIR(smb_fname
->st
.st_ex_mode
))
941 dosmode
|= FILE_ATTRIBUTE_DIRECTORY
;
943 dosmode
&= ~FILE_ATTRIBUTE_DIRECTORY
;
945 /* Store the DOS attributes in an EA by preference. */
946 status
= SMB_VFS_FSET_DOS_ATTRIBUTES(conn
,
947 metadata_fsp(smb_fname
->fsp
),
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 (!(dosmode
& FILE_ATTRIBUTE_READONLY
)) {
1001 unixmode
|= (smb_fname
->st
.st_ex_mode
& (S_IWUSR
|S_IWGRP
|S_IWOTH
));
1005 * From the chmod 2 man page:
1007 * "If the calling process is not privileged, and the group of the file
1008 * does not match the effective group ID of the process or one of its
1009 * supplementary group IDs, the S_ISGID bit will be turned off, but
1010 * this will not cause an error to be returned."
1012 * Simply refuse to do the chmod in this case.
1015 if (S_ISDIR(smb_fname
->st
.st_ex_mode
) &&
1016 (unixmode
& S_ISGID
) &&
1017 geteuid() != sec_initial_uid() &&
1018 !current_user_in_group(conn
, smb_fname
->st
.st_ex_gid
))
1020 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1021 "set for directory %s\n",
1022 smb_fname_str_dbg(smb_fname
)));
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 status
= check_any_access_fsp(fsp
,
1091 | FILE_WRITE_ATTRIBUTES
1092 | SEC_FILE_APPEND_DATA
);
1093 if (!NT_STATUS_IS_OK(status
)) {
1094 DBG_DEBUG("fname[%s] set[%u] "
1095 "access_mask[0x%08X] - access denied\n",
1096 smb_fname_str_dbg(fsp
->fsp_name
),
1102 if (fsp
->fsp_flags
.is_directory
) {
1103 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1104 (sparse
? "set" : "clear"),
1105 smb_fname_str_dbg(fsp
->fsp_name
)));
1106 return NT_STATUS_INVALID_PARAMETER
;
1109 if (IS_IPC(conn
) || IS_PRINT(conn
)) {
1110 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1111 (sparse
? "set" : "clear")));
1112 return NT_STATUS_INVALID_PARAMETER
;
1115 if (fsp_is_alternate_stream(fsp
)) {
1117 * MS-FSA 2.1.1.5 IsSparse
1119 * This is a per stream attribute, but our backends don't
1120 * support it a consistent way, therefore just pretend
1121 * success and ignore the request.
1123 DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
1124 "[%s]\n", fsp_str_dbg(fsp
));
1125 return NT_STATUS_OK
;
1128 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1129 sparse
, smb_fname_str_dbg(fsp
->fsp_name
)));
1131 if (!lp_store_dos_attributes(SNUM(conn
))) {
1132 return NT_STATUS_INVALID_DEVICE_REQUEST
;
1135 status
= vfs_stat_fsp(fsp
);
1136 if (!NT_STATUS_IS_OK(status
)) {
1140 old_dosmode
= fdos_mode(fsp
);
1142 if (sparse
&& !(old_dosmode
& FILE_ATTRIBUTE_SPARSE
)) {
1143 new_dosmode
= old_dosmode
| FILE_ATTRIBUTE_SPARSE
;
1144 } else if (!sparse
&& (old_dosmode
& FILE_ATTRIBUTE_SPARSE
)) {
1145 new_dosmode
= old_dosmode
& ~FILE_ATTRIBUTE_SPARSE
;
1147 return NT_STATUS_OK
;
1150 /* Store the DOS attributes in an EA. */
1151 status
= SMB_VFS_FSET_DOS_ATTRIBUTES(conn
, fsp
, new_dosmode
);
1152 if (!NT_STATUS_IS_OK(status
)) {
1156 notify_fname(conn
, NOTIFY_ACTION_MODIFIED
,
1157 FILE_NOTIFY_CHANGE_ATTRIBUTES
,
1158 fsp
->fsp_name
->base_name
);
1160 fsp
->fsp_name
->st
.cached_dos_attributes
= new_dosmode
;
1161 fsp
->fsp_flags
.is_sparse
= sparse
;
1163 return NT_STATUS_OK
;
1166 /*******************************************************************
1167 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1169 *******************************************************************/
1171 int file_ntimes(connection_struct
*conn
,
1173 struct smb_file_time
*ft
)
1179 DBG_INFO("actime: %s",
1180 time_to_asc(convert_timespec_to_time_t(ft
->atime
)));
1181 DBG_INFO("modtime: %s",
1182 time_to_asc(convert_timespec_to_time_t(ft
->mtime
)));
1183 DBG_INFO("ctime: %s",
1184 time_to_asc(convert_timespec_to_time_t(ft
->ctime
)));
1185 DBG_INFO("createtime: %s",
1186 time_to_asc(convert_timespec_to_time_t(ft
->create_time
)));
1188 /* Don't update the time on read-only shares */
1189 /* We need this as set_filetime (which can be called on
1190 close and other paths) can end up calling this function
1191 without the NEED_WRITE protection. Found by :
1192 Leo Weppelman <leo@wau.mis.ah.nl>
1195 if (!CAN_WRITE(conn
)) {
1199 if (SMB_VFS_FNTIMES(fsp
, ft
) == 0) {
1204 if((errno
!= EPERM
) && (errno
!= EACCES
)) {
1208 if(!lp_dos_filetimes(SNUM(conn
))) {
1212 /* We have permission (given by the Samba admin) to
1213 break POSIX semantics and allow a user to change
1214 the time on a file they don't own but can write to
1218 /* Check if we have write access. */
1219 if (can_write_to_fsp(fsp
)) {
1220 /* We are allowed to become root and change the filetime. */
1222 ret
= SMB_VFS_FNTIMES(fsp
, ft
);
1228 copy_stat_ex_timestamps(fsp
, ft
);
1234 /******************************************************************
1235 Force a "sticky" write time on a pathname. This will always be
1236 returned on all future write time queries and set on close.
1237 ******************************************************************/
1239 bool set_sticky_write_time_path(struct file_id fileid
, struct timespec mtime
)
1241 if (is_omit_timespec(&mtime
)) {
1245 if (!set_sticky_write_time(fileid
, mtime
)) {
1252 /******************************************************************
1253 Force a "sticky" write time on an fsp. This will always be
1254 returned on all future write time queries and set on close.
1255 ******************************************************************/
1257 bool set_sticky_write_time_fsp(struct files_struct
*fsp
, struct timespec mtime
)
1259 if (is_omit_timespec(&mtime
)) {
1263 fsp
->fsp_flags
.write_time_forced
= true;
1264 TALLOC_FREE(fsp
->update_write_time_event
);
1266 return set_sticky_write_time_path(fsp
->file_id
, mtime
);
1269 /******************************************************************
1270 Set a create time EA.
1271 ******************************************************************/
1273 NTSTATUS
set_create_timespec_ea(struct files_struct
*fsp
,
1274 struct timespec create_time
)
1279 if (!lp_store_dos_attributes(SNUM(fsp
->conn
))) {
1280 return NT_STATUS_OK
;
1283 dosmode
= fdos_mode(fsp
);
1285 fsp
->fsp_name
->st
.st_ex_btime
= create_time
;
1286 ret
= file_set_dosmode(fsp
->conn
, fsp
->fsp_name
, dosmode
, NULL
, false);
1288 return map_nt_error_from_unix(errno
);
1291 DBG_DEBUG("wrote create time EA for file %s\n",
1292 smb_fname_str_dbg(fsp
->fsp_name
));
1294 return NT_STATUS_OK
;
1297 /******************************************************************
1298 Return a create time.
1299 ******************************************************************/
1301 struct timespec
get_create_timespec(connection_struct
*conn
,
1302 struct files_struct
*fsp
,
1303 const struct smb_filename
*smb_fname
)
1306 struct files_struct
*meta_fsp
= metadata_fsp(fsp
);
1307 return meta_fsp
->fsp_name
->st
.st_ex_btime
;
1309 return smb_fname
->st
.st_ex_btime
;
1312 /******************************************************************
1313 Return a change time (may look at EA in future).
1314 ******************************************************************/
1316 struct timespec
get_change_timespec(connection_struct
*conn
,
1317 struct files_struct
*fsp
,
1318 const struct smb_filename
*smb_fname
)
1320 return smb_fname
->st
.st_ex_mtime
;