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]");
67 if (mode
& FILE_ATTRIBUTE_REPARSE_POINT
) {
68 fstrcat(modestr
, "[reparse_point]");
71 DBG_INFO("%s returning (0x%x): \"%s\"\n", func
, (unsigned)mode
,
75 static uint32_t filter_mode_by_protocol(enum protocol_types protocol
,
78 if (protocol
<= PROTOCOL_LANMAN2
) {
79 DEBUG(10,("filter_mode_by_protocol: "
80 "filtering result 0x%x to 0x%x\n",
82 (unsigned int)(mode
& 0x3f) ));
88 /****************************************************************************
89 Change a dos mode to a unix mode.
90 Base permission for files:
91 if creating file and inheriting (i.e. parent_dir != NULL)
92 apply read/write bits from parent directory.
94 everybody gets read bit set
95 dos readonly is represented in unix by removing everyone's write bit
96 dos archive is represented in unix by the user's execute bit
97 dos system is represented in unix by the group's execute bit
98 dos hidden is represented in unix by the other's execute bit
100 Then apply create mask,
103 Base permission for directories:
104 dos directory is represented in unix by unix's dir bit and the exec bit
106 Then apply create mask,
109 ****************************************************************************/
111 mode_t
unix_mode(connection_struct
*conn
, int dosmode
,
112 const struct smb_filename
*smb_fname
,
113 struct files_struct
*parent_dirfsp
)
115 mode_t result
= (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IWGRP
| S_IWOTH
);
116 mode_t dir_mode
= 0; /* Mode of the inherit_from directory if
119 if ((dosmode
& FILE_ATTRIBUTE_READONLY
) &&
120 !lp_store_dos_attributes(SNUM(conn
))) {
121 result
&= ~(S_IWUSR
| S_IWGRP
| S_IWOTH
);
124 if ((parent_dirfsp
!= NULL
) && lp_inherit_permissions(SNUM(conn
))) {
125 struct stat_ex sbuf
= { .st_ex_nlink
= 0, };
128 DBG_DEBUG("[%s] inheriting from [%s]\n",
129 smb_fname_str_dbg(smb_fname
),
130 smb_fname_str_dbg(parent_dirfsp
->fsp_name
));
132 ret
= SMB_VFS_FSTAT(parent_dirfsp
, &sbuf
);
134 DBG_ERR("fstat failed [%s]: %s\n",
135 smb_fname_str_dbg(parent_dirfsp
->fsp_name
),
137 return(0); /* *** shouldn't happen! *** */
140 /* Save for later - but explicitly remove setuid bit for safety. */
141 dir_mode
= sbuf
.st_ex_mode
& ~S_ISUID
;
142 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
143 smb_fname_str_dbg(smb_fname
), (int)dir_mode
));
148 if (dosmode
& FILE_ATTRIBUTE_DIRECTORY
) {
149 /* We never make directories read only for the owner as under DOS a user
150 can always create a file in a read-only directory. */
151 result
|= (S_IFDIR
| S_IWUSR
);
154 /* Inherit mode of parent directory. */
157 /* Provisionally add all 'x' bits */
158 result
|= (S_IXUSR
| S_IXGRP
| S_IXOTH
);
160 /* Apply directory mask */
161 result
&= lp_directory_mask(SNUM(conn
));
162 /* Add in force bits */
163 result
|= lp_force_directory_mode(SNUM(conn
));
166 if ((dosmode
& FILE_ATTRIBUTE_ARCHIVE
) &&
167 lp_map_archive(SNUM(conn
))) {
171 if ((dosmode
& FILE_ATTRIBUTE_SYSTEM
) &&
172 lp_map_system(SNUM(conn
))) {
176 if ((dosmode
& FILE_ATTRIBUTE_HIDDEN
) &&
177 lp_map_hidden(SNUM(conn
))) {
182 /* Inherit 666 component of parent directory mode */
183 result
|= dir_mode
& (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IWGRP
| S_IWOTH
);
185 /* Apply mode mask */
186 result
&= lp_create_mask(SNUM(conn
));
187 /* Add in force bits */
188 result
|= lp_force_create_mode(SNUM(conn
));
192 DBG_INFO("unix_mode(%s) returning 0%o\n",
193 smb_fname_str_dbg(smb_fname
), (int)result
);
198 /****************************************************************************
199 Change a unix mode to a dos mode.
200 ****************************************************************************/
202 static uint32_t dos_mode_from_sbuf(connection_struct
*conn
,
203 const struct stat_ex
*st
,
204 struct files_struct
*fsp
)
207 enum mapreadonly_options ro_opts
=
208 (enum mapreadonly_options
)lp_map_readonly(SNUM(conn
));
210 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
211 /* if we can find out if a file is immutable we should report it r/o */
212 if (st
->st_ex_flags
& (UF_IMMUTABLE
| SF_IMMUTABLE
)) {
213 result
|= FILE_ATTRIBUTE_READONLY
;
216 if (ro_opts
== MAP_READONLY_YES
) {
217 /* Original Samba method - map inverse of user "w" bit. */
218 if ((st
->st_ex_mode
& S_IWUSR
) == 0) {
219 result
|= FILE_ATTRIBUTE_READONLY
;
222 if (ro_opts
== MAP_READONLY_PERMISSIONS
) {
223 /* smb_fname->fsp can be NULL for an MS-DFS link. */
224 /* Check actual permissions for read-only. */
225 if ((fsp
!= NULL
) && !can_write_to_fsp(fsp
)) {
226 result
|= FILE_ATTRIBUTE_READONLY
;
230 if (MAP_ARCHIVE(conn
) && ((st
->st_ex_mode
& S_IXUSR
) != 0)) {
231 result
|= FILE_ATTRIBUTE_ARCHIVE
;
234 if (MAP_SYSTEM(conn
) && ((st
->st_ex_mode
& S_IXGRP
) != 0)) {
235 result
|= FILE_ATTRIBUTE_SYSTEM
;
238 if (MAP_HIDDEN(conn
) && ((st
->st_ex_mode
& S_IXOTH
) != 0)) {
239 result
|= FILE_ATTRIBUTE_HIDDEN
;
242 if (S_ISDIR(st
->st_ex_mode
)) {
243 result
= FILE_ATTRIBUTE_DIRECTORY
|
244 (result
& FILE_ATTRIBUTE_READONLY
);
247 dos_mode_debug_print(__func__
, result
);
252 /****************************************************************************
253 Get DOS attributes from an EA.
254 This can also pull the create time into the stat struct inside smb_fname.
255 ****************************************************************************/
257 NTSTATUS
parse_dos_attribute_blob(struct smb_filename
*smb_fname
,
261 struct xattr_DOSATTRIB dosattrib
;
262 enum ndr_err_code ndr_err
;
265 ndr_err
= ndr_pull_struct_blob(&blob
, talloc_tos(), &dosattrib
,
266 (ndr_pull_flags_fn_t
)ndr_pull_xattr_DOSATTRIB
);
268 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
269 DBG_WARNING("bad ndr decode "
270 "from EA on file %s: Error = %s\n",
271 smb_fname_str_dbg(smb_fname
),
272 ndr_errstr(ndr_err
));
273 return ndr_map_error2ntstatus(ndr_err
);
276 DBG_DEBUG("%s attr = %s\n",
277 smb_fname_str_dbg(smb_fname
), dosattrib
.attrib_hex
);
279 switch (dosattrib
.version
) {
281 dosattr
= dosattrib
.info
.compatinfoFFFF
.attrib
;
284 dosattr
= dosattrib
.info
.info1
.attrib
;
285 if (!null_nttime(dosattrib
.info
.info1
.create_time
)) {
286 struct timespec create_time
=
287 nt_time_to_unix_timespec(
288 dosattrib
.info
.info1
.create_time
);
290 update_stat_ex_create_time(&smb_fname
->st
,
293 DBG_DEBUG("file %s case 1 set btime %s",
294 smb_fname_str_dbg(smb_fname
),
295 time_to_asc(convert_timespec_to_time_t(
300 dosattr
= dosattrib
.info
.oldinfo2
.attrib
;
301 /* Don't know what flags to check for this case. */
304 dosattr
= dosattrib
.info
.info3
.attrib
;
305 if ((dosattrib
.info
.info3
.valid_flags
& XATTR_DOSINFO_CREATE_TIME
) &&
306 !null_nttime(dosattrib
.info
.info3
.create_time
)) {
307 struct timespec create_time
=
308 nt_time_to_full_timespec(
309 dosattrib
.info
.info3
.create_time
);
311 update_stat_ex_create_time(&smb_fname
->st
,
314 DBG_DEBUG("file %s case 3 set btime %s",
315 smb_fname_str_dbg(smb_fname
),
316 time_to_asc(convert_timespec_to_time_t(
323 uint32_t info_valid_flags
;
324 NTTIME info_create_time
;
326 if (dosattrib
.version
== 4) {
327 info_valid_flags
= dosattrib
.info
.info4
.valid_flags
;
328 info_create_time
= dosattrib
.info
.info4
.create_time
;
329 dosattr
= dosattrib
.info
.info4
.attrib
;
331 info_valid_flags
= dosattrib
.info
.info5
.valid_flags
;
332 info_create_time
= dosattrib
.info
.info5
.create_time
;
333 dosattr
= dosattrib
.info
.info5
.attrib
;
336 if ((info_valid_flags
& XATTR_DOSINFO_CREATE_TIME
) &&
337 !null_nttime(info_create_time
))
339 struct timespec creat_time
;
341 creat_time
= nt_time_to_full_timespec(info_create_time
);
342 update_stat_ex_create_time(&smb_fname
->st
, creat_time
);
344 DBG_DEBUG("file [%s] creation time [%s]\n",
345 smb_fname_str_dbg(smb_fname
),
346 nt_time_string(talloc_tos(), info_create_time
));
352 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
353 smb_fname_str_dbg(smb_fname
), blob
.data
);
354 /* Should this be INTERNAL_ERROR? */
355 return NT_STATUS_INVALID_PARAMETER
;
358 if (S_ISDIR(smb_fname
->st
.st_ex_mode
)) {
359 dosattr
|= FILE_ATTRIBUTE_DIRECTORY
;
363 * _SPARSE and _REPARSE_POINT are valid on get but not on
364 * set. Both are created via special fcntls.
367 dosattr
&= (SAMBA_ATTRIBUTES_MASK
|
368 FILE_ATTRIBUTE_SPARSE
|
369 FILE_ATTRIBUTE_REPARSE_POINT
);
373 dos_mode_debug_print(__func__
, *pattr
);
378 NTSTATUS
fget_ea_dos_attribute(struct files_struct
*fsp
,
386 if (!lp_store_dos_attributes(SNUM(fsp
->conn
))) {
387 return NT_STATUS_NOT_IMPLEMENTED
;
390 /* Don't reset pattr to zero as we may already have filename-based attributes we
393 sizeret
= SMB_VFS_FGETXATTR(fsp
,
394 SAMBA_XATTR_DOS_ATTRIB
,
397 if (sizeret
== -1 && ( errno
== EPERM
|| errno
== EACCES
)) {
398 /* we may also retrieve dos attribs for unreadable files, this
399 is why we'll retry as root. We don't use root in the first
400 run because in cases like NFS, root might have even less
401 rights than the real user
404 sizeret
= SMB_VFS_FGETXATTR(fsp
,
405 SAMBA_XATTR_DOS_ATTRIB
,
411 DBG_INFO("Cannot get attribute "
412 "from EA on file %s: Error = %s\n",
413 fsp_str_dbg(fsp
), strerror(errno
));
414 return map_nt_error_from_unix(errno
);
417 blob
.data
= (uint8_t *)attrstr
;
418 blob
.length
= sizeret
;
420 status
= parse_dos_attribute_blob(fsp
->fsp_name
, blob
, pattr
);
421 if (!NT_STATUS_IS_OK(status
)) {
428 /****************************************************************************
429 Set DOS attributes in an EA.
430 Also sets the create time.
431 ****************************************************************************/
433 NTSTATUS
set_ea_dos_attribute(connection_struct
*conn
,
434 struct smb_filename
*smb_fname
,
437 struct xattr_DOSATTRIB dosattrib
= { .version
= 0, };
438 enum ndr_err_code ndr_err
;
439 DATA_BLOB blob
= { .data
= NULL
, };
440 struct timespec btime
;
443 if (!lp_store_dos_attributes(SNUM(conn
))) {
444 return NT_STATUS_NOT_IMPLEMENTED
;
447 if (smb_fname
->fsp
== NULL
) {
449 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
452 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
453 * vfs_default via DMAPI if that is enabled.
455 dosmode
&= ~FILE_ATTRIBUTE_OFFLINE
;
457 dosattrib
.version
= 5;
458 dosattrib
.info
.info5
.valid_flags
= XATTR_DOSINFO_ATTRIB
|
459 XATTR_DOSINFO_CREATE_TIME
;
460 dosattrib
.info
.info5
.attrib
= dosmode
;
461 dosattrib
.info
.info5
.create_time
= full_timespec_to_nt_time(
462 &smb_fname
->st
.st_ex_btime
);
464 DBG_DEBUG("set attribute 0x%" PRIx32
", btime = %s on file %s\n",
466 time_to_asc(convert_timespec_to_time_t(
467 smb_fname
->st
.st_ex_btime
)),
468 smb_fname_str_dbg(smb_fname
));
470 ndr_err
= ndr_push_struct_blob(
471 &blob
, talloc_tos(), &dosattrib
,
472 (ndr_push_flags_fn_t
)ndr_push_xattr_DOSATTRIB
);
474 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
475 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
476 ndr_errstr(ndr_err
)));
477 return ndr_map_error2ntstatus(ndr_err
);
480 if (blob
.data
== NULL
|| blob
.length
== 0) {
481 /* Should this be INTERNAL_ERROR? */
482 return NT_STATUS_INVALID_PARAMETER
;
485 ret
= SMB_VFS_FSETXATTR(smb_fname
->fsp
,
486 SAMBA_XATTR_DOS_ATTRIB
,
487 blob
.data
, blob
.length
, 0);
489 NTSTATUS status
= NT_STATUS_OK
;
490 bool set_dosmode_ok
= false;
492 if ((errno
!= EPERM
) && (errno
!= EACCES
)) {
493 DBG_INFO("Cannot set "
494 "attribute EA on file %s: Error = %s\n",
495 smb_fname_str_dbg(smb_fname
), strerror(errno
));
496 return map_nt_error_from_unix(errno
);
499 /* We want DOS semantics, ie allow non owner with write permission to change the
500 bits on a file. Just like file_ntimes below.
503 /* Check if we have write access. */
504 if (!CAN_WRITE(conn
)) {
505 return NT_STATUS_ACCESS_DENIED
;
508 status
= smbd_check_access_rights_fsp(conn
->cwd_fsp
,
511 FILE_WRITE_ATTRIBUTES
);
512 if (NT_STATUS_IS_OK(status
)) {
513 set_dosmode_ok
= true;
516 if (!set_dosmode_ok
&& lp_dos_filemode(SNUM(conn
))) {
517 set_dosmode_ok
= can_write_to_fsp(smb_fname
->fsp
);
520 if (!set_dosmode_ok
) {
521 return NT_STATUS_ACCESS_DENIED
;
525 ret
= SMB_VFS_FSETXATTR(smb_fname
->fsp
,
526 SAMBA_XATTR_DOS_ATTRIB
,
527 blob
.data
, blob
.length
, 0);
529 status
= NT_STATUS_OK
;
532 if (!NT_STATUS_IS_OK(status
)) {
538 * We correctly stored the create time.
539 * We *always* set XATTR_DOSINFO_CREATE_TIME,
540 * so now it can no longer be considered
541 * calculated. Make sure to use the value rounded
542 * to NTTIME granularity we've stored in the xattr.
544 btime
= nt_time_to_full_timespec(dosattrib
.info
.info5
.create_time
);
545 update_stat_ex_create_time(&smb_fname
->st
, btime
);
547 DBG_DEBUG("set EA 0x%" PRIx32
" on file %s\n",
549 smb_fname_str_dbg(smb_fname
));
554 dos_mode_from_name(connection_struct
*conn
, const char *name
, uint32_t dosmode
)
556 const char *p
= NULL
;
557 uint32_t result
= dosmode
;
559 if (!(result
& FILE_ATTRIBUTE_HIDDEN
) &&
560 lp_hide_dot_files(SNUM(conn
)))
562 p
= strrchr_m(name
, '/');
569 /* Only . and .. are not hidden. */
570 if ((p
[0] == '.') && !(ISDOT(p
) || ISDOTDOT(p
))) {
571 result
|= FILE_ATTRIBUTE_HIDDEN
;
575 if (!(result
& FILE_ATTRIBUTE_HIDDEN
) && IS_HIDDEN_PATH(conn
, name
)) {
576 result
|= FILE_ATTRIBUTE_HIDDEN
;
582 /****************************************************************************
583 Change a unix mode to a dos mode for an ms dfs link.
584 ****************************************************************************/
586 uint32_t dos_mode_msdfs(connection_struct
*conn
,
588 const struct stat_ex
*st
)
592 DEBUG(8, ("dos_mode_msdfs: %s\n", name
));
594 if (!VALID_STAT(*st
)) {
598 result
= dos_mode_from_name(conn
, name
, result
);
599 result
|= dos_mode_from_sbuf(conn
, st
, NULL
);
602 result
= FILE_ATTRIBUTE_NORMAL
;
605 result
= filter_mode_by_protocol(conn_protocol(conn
->sconn
), result
);
608 * Add in that it is a reparse point
610 result
|= FILE_ATTRIBUTE_REPARSE_POINT
;
612 dos_mode_debug_print(__func__
, result
);
618 * check whether a file or directory is flagged as compressed.
620 static NTSTATUS
dos_mode_check_compressed(struct files_struct
*fsp
,
624 uint16_t compression_fmt
;
626 status
= SMB_VFS_FGET_COMPRESSION(
627 fsp
->conn
, talloc_tos(), fsp
, &compression_fmt
);
628 if (!NT_STATUS_IS_OK(status
)) {
632 if (compression_fmt
== COMPRESSION_FORMAT_LZNT1
) {
633 *is_compressed
= true;
635 *is_compressed
= false;
640 static uint32_t dos_mode_post(uint32_t dosmode
,
641 struct files_struct
*fsp
,
644 struct smb_filename
*smb_fname
= NULL
;
648 smb_fname
= fsp
->fsp_name
;
650 SMB_ASSERT(smb_fname
!= NULL
);
653 * According to MS-FSA a stream name does not have
654 * separate DOS attribute metadata, so we must return
655 * the DOS attribute from the base filename. With one caveat,
656 * a non-default stream name can never be a directory.
658 * As this is common to all streams data stores, we handle
659 * it here instead of inside all stream VFS modules.
661 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
664 if (is_named_stream(smb_fname
)) {
665 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
666 dosmode
&= ~(FILE_ATTRIBUTE_DIRECTORY
);
669 if (fsp
->conn
->fs_capabilities
& FILE_FILE_COMPRESSION
) {
670 bool compressed
= false;
672 status
= dos_mode_check_compressed(fsp
, &compressed
);
673 if (NT_STATUS_IS_OK(status
) && compressed
) {
674 dosmode
|= FILE_ATTRIBUTE_COMPRESSED
;
678 dosmode
|= dos_mode_from_name(fsp
->conn
, smb_fname
->base_name
, dosmode
);
680 if (S_ISDIR(smb_fname
->st
.st_ex_mode
)) {
681 dosmode
|= FILE_ATTRIBUTE_DIRECTORY
;
682 } else if (dosmode
== 0) {
683 dosmode
= FILE_ATTRIBUTE_NORMAL
;
686 dosmode
= filter_mode_by_protocol(conn_protocol(fsp
->conn
->sconn
),
689 dos_mode_debug_print(func
, dosmode
);
693 /****************************************************************************
694 Change a unix mode to a dos mode.
695 May also read the create timespec into the stat struct in smb_fname
696 if "store dos attributes" is true.
697 ****************************************************************************/
699 uint32_t fdos_mode(struct files_struct
*fsp
)
702 NTSTATUS status
= NT_STATUS_OK
;
704 DBG_DEBUG("%s\n", fsp_str_dbg(fsp
));
706 if (fsp
->fake_file_handle
!= NULL
) {
707 return dosmode_from_fake_filehandle(fsp
->fake_file_handle
);
710 if (!VALID_STAT(fsp
->fsp_name
->st
)) {
714 if (S_ISLNK(fsp
->fsp_name
->st
.st_ex_mode
)) {
715 return FILE_ATTRIBUTE_NORMAL
;
718 if (fsp
->fsp_name
->st
.cached_dos_attributes
!= FILE_ATTRIBUTE_INVALID
) {
719 return fsp
->fsp_name
->st
.cached_dos_attributes
;
722 /* Get the DOS attributes via the VFS if we can */
723 status
= SMB_VFS_FGET_DOS_ATTRIBUTES(fsp
->conn
,
727 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_IMPLEMENTED
)) {
728 result
|= dos_mode_from_sbuf(fsp
->conn
,
733 fsp
->fsp_name
->st
.cached_dos_attributes
= dos_mode_post(result
, fsp
, __func__
);
734 return fsp
->fsp_name
->st
.cached_dos_attributes
;
737 struct dos_mode_at_state
{
738 files_struct
*dir_fsp
;
739 struct smb_filename
*smb_fname
;
743 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req
*subreq
);
745 struct tevent_req
*dos_mode_at_send(TALLOC_CTX
*mem_ctx
,
746 struct tevent_context
*ev
,
747 files_struct
*dir_fsp
,
748 struct smb_filename
*smb_fname
)
750 struct tevent_req
*req
= NULL
;
751 struct dos_mode_at_state
*state
= NULL
;
752 struct tevent_req
*subreq
= NULL
;
754 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname
));
756 req
= tevent_req_create(mem_ctx
, &state
,
757 struct dos_mode_at_state
);
762 *state
= (struct dos_mode_at_state
) {
764 .smb_fname
= smb_fname
,
767 if (!VALID_STAT(smb_fname
->st
)) {
768 tevent_req_done(req
);
769 return tevent_req_post(req
, ev
);
772 if (smb_fname
->fsp
== NULL
) {
773 if (ISDOTDOT(smb_fname
->base_name
)) {
775 * smb_fname->fsp is explicitly closed
776 * for ".." to prevent meta-data leakage.
778 state
->dosmode
= FILE_ATTRIBUTE_DIRECTORY
;
781 * This is a symlink in POSIX context.
782 * FIXME ? Should we move to returning
783 * FILE_ATTRIBUTE_REPARSE_POINT here ?
785 state
->dosmode
= FILE_ATTRIBUTE_NORMAL
;
787 tevent_req_done(req
);
788 return tevent_req_post(req
, ev
);
791 subreq
= SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state
,
795 if (tevent_req_nomem(subreq
, req
)) {
796 return tevent_req_post(req
, ev
);
798 tevent_req_set_callback(subreq
, dos_mode_at_vfs_get_dosmode_done
, req
);
803 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req
*subreq
)
805 struct tevent_req
*req
=
806 tevent_req_callback_data(subreq
,
808 struct dos_mode_at_state
*state
=
810 struct dos_mode_at_state
);
811 struct vfs_aio_state aio_state
;
816 * Make sure we run as the user again
818 ok
= change_to_user_and_service_by_fsp(state
->dir_fsp
);
821 status
= SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq
,
825 if (!NT_STATUS_IS_OK(status
)) {
827 * Both the sync dos_mode() as well as the async
828 * dos_mode_at_[send|recv] have no real error return, the only
829 * unhandled error is when the stat info in smb_fname is not
830 * valid (cf the checks in dos_mode() and dos_mode_at_send().
832 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
833 * dos_mode_post() which also does the mapping of a last resort
834 * from S_IFMT(st_mode).
836 * Only if we get NT_STATUS_NOT_IMPLEMENTED or
837 * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
838 * fallback to sync processing.
840 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_IMPLEMENTED
) &&
841 !NT_STATUS_EQUAL(status
, NT_STATUS_NOT_SUPPORTED
))
844 * state->dosmode should still be 0, but reset
848 status
= NT_STATUS_OK
;
851 if (NT_STATUS_IS_OK(status
)) {
852 state
->dosmode
= dos_mode_post(state
->dosmode
,
853 state
->smb_fname
->fsp
,
855 tevent_req_done(req
);
860 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
863 state
->dosmode
= fdos_mode(state
->smb_fname
->fsp
);
864 tevent_req_done(req
);
868 NTSTATUS
dos_mode_at_recv(struct tevent_req
*req
, uint32_t *dosmode
)
870 struct dos_mode_at_state
*state
=
872 struct dos_mode_at_state
);
875 if (tevent_req_is_nterror(req
, &status
)) {
876 tevent_req_received(req
);
880 *dosmode
= state
->dosmode
;
881 tevent_req_received(req
);
885 /*******************************************************************
886 chmod a file - but preserve some bits.
887 If "store dos attributes" is also set it will store the create time
888 from the stat struct in smb_fname (in NTTIME format) in the EA
890 ********************************************************************/
892 int file_set_dosmode(connection_struct
*conn
,
893 struct smb_filename
*smb_fname
,
895 struct smb_filename
*parent_dir
,
904 if (!CAN_WRITE(conn
)) {
909 if (S_ISLNK(smb_fname
->st
.st_ex_mode
)) {
910 /* A symlink in POSIX context, ignore */
914 if ((S_ISDIR(smb_fname
->st
.st_ex_mode
)) &&
915 (dosmode
& FILE_ATTRIBUTE_TEMPORARY
))
921 dosmode
&= SAMBA_ATTRIBUTES_MASK
;
923 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
924 dosmode
, smb_fname_str_dbg(smb_fname
)));
926 if (smb_fname
->fsp
== NULL
) {
931 if ((smb_fname
->fsp
->posix_flags
& FSP_POSIX_FLAGS_OPEN
) &&
932 !lp_store_dos_attributes(SNUM(conn
)))
937 unixmode
= smb_fname
->st
.st_ex_mode
;
939 get_acl_group_bits(conn
, smb_fname
->fsp
, &smb_fname
->st
.st_ex_mode
);
941 if (S_ISDIR(smb_fname
->st
.st_ex_mode
))
942 dosmode
|= FILE_ATTRIBUTE_DIRECTORY
;
944 dosmode
&= ~FILE_ATTRIBUTE_DIRECTORY
;
946 /* Store the DOS attributes in an EA by preference. */
947 status
= SMB_VFS_FSET_DOS_ATTRIBUTES(conn
,
948 metadata_fsp(smb_fname
->fsp
),
950 if (NT_STATUS_IS_OK(status
)) {
951 smb_fname
->st
.cached_dos_attributes
= dosmode
;
957 * Only fall back to using UNIX modes if
958 * we get NOT_IMPLEMENTED.
960 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_IMPLEMENTED
)) {
961 errno
= map_errno_from_nt_status(status
);
965 /* Fall back to UNIX modes. */
966 unixmode
= unix_mode(
970 parent_dir
!= NULL
? parent_dir
->fsp
: NULL
);
972 /* preserve the file type bits */
975 /* preserve the s bits */
976 mask
|= (S_ISUID
| S_ISGID
);
978 /* preserve the t bit */
983 /* possibly preserve the x bits */
984 if (!MAP_ARCHIVE(conn
))
986 if (!MAP_SYSTEM(conn
))
988 if (!MAP_HIDDEN(conn
))
991 unixmode
|= (smb_fname
->st
.st_ex_mode
& mask
);
993 /* if we previously had any r bits set then leave them alone */
994 if ((tmp
= smb_fname
->st
.st_ex_mode
& (S_IRUSR
|S_IRGRP
|S_IROTH
))) {
995 unixmode
&= ~(S_IRUSR
|S_IRGRP
|S_IROTH
);
999 /* if we previously had any w bits set then leave them alone
1000 whilst adding in the new w bits, if the new mode is not rdonly */
1001 if (!(dosmode
& FILE_ATTRIBUTE_READONLY
)) {
1002 unixmode
|= (smb_fname
->st
.st_ex_mode
& (S_IWUSR
|S_IWGRP
|S_IWOTH
));
1006 * From the chmod 2 man page:
1008 * "If the calling process is not privileged, and the group of the file
1009 * does not match the effective group ID of the process or one of its
1010 * supplementary group IDs, the S_ISGID bit will be turned off, but
1011 * this will not cause an error to be returned."
1013 * Simply refuse to do the chmod in this case.
1016 if (S_ISDIR(smb_fname
->st
.st_ex_mode
) &&
1017 (unixmode
& S_ISGID
) &&
1018 geteuid() != sec_initial_uid() &&
1019 !current_user_in_group(conn
, smb_fname
->st
.st_ex_gid
))
1021 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1022 "set for directory %s\n",
1023 smb_fname_str_dbg(smb_fname
)));
1028 ret
= SMB_VFS_FCHMOD(smb_fname
->fsp
, unixmode
);
1033 if((errno
!= EPERM
) && (errno
!= EACCES
))
1036 if(!lp_dos_filemode(SNUM(conn
)))
1039 /* We want DOS semantics, ie allow non owner with write permission to change the
1040 bits on a file. Just like file_ntimes below.
1043 if (!can_write_to_fsp(smb_fname
->fsp
))
1050 ret
= SMB_VFS_FCHMOD(smb_fname
->fsp
, unixmode
);
1055 notify_fname(conn
, NOTIFY_ACTION_MODIFIED
,
1056 FILE_NOTIFY_CHANGE_ATTRIBUTES
,
1057 smb_fname
->base_name
);
1060 smb_fname
->st
.st_ex_mode
= unixmode
;
1067 NTSTATUS
file_set_sparse(connection_struct
*conn
,
1071 const struct loadparm_substitution
*lp_sub
=
1072 loadparm_s3_global_substitution();
1073 uint32_t old_dosmode
;
1074 uint32_t new_dosmode
;
1077 if (!CAN_WRITE(conn
)) {
1078 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1079 "on readonly share[%s]\n",
1080 smb_fname_str_dbg(fsp
->fsp_name
),
1082 lp_servicename(talloc_tos(), lp_sub
, SNUM(conn
))));
1083 return NT_STATUS_MEDIA_WRITE_PROTECTED
;
1087 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1088 * following access flags are granted.
1090 status
= check_any_access_fsp(fsp
,
1092 | FILE_WRITE_ATTRIBUTES
1093 | SEC_FILE_APPEND_DATA
);
1094 if (!NT_STATUS_IS_OK(status
)) {
1095 DBG_DEBUG("fname[%s] set[%u] "
1096 "access_mask[0x%08X] - access denied\n",
1097 smb_fname_str_dbg(fsp
->fsp_name
),
1103 if (fsp
->fsp_flags
.is_directory
) {
1104 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1105 (sparse
? "set" : "clear"),
1106 smb_fname_str_dbg(fsp
->fsp_name
)));
1107 return NT_STATUS_INVALID_PARAMETER
;
1110 if (IS_IPC(conn
) || IS_PRINT(conn
)) {
1111 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1112 (sparse
? "set" : "clear")));
1113 return NT_STATUS_INVALID_PARAMETER
;
1116 if (fsp_is_alternate_stream(fsp
)) {
1118 * MS-FSA 2.1.1.5 IsSparse
1120 * This is a per stream attribute, but our backends don't
1121 * support it a consistent way, therefore just pretend
1122 * success and ignore the request.
1124 DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
1125 "[%s]\n", fsp_str_dbg(fsp
));
1126 return NT_STATUS_OK
;
1129 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1130 sparse
, smb_fname_str_dbg(fsp
->fsp_name
)));
1132 if (!lp_store_dos_attributes(SNUM(conn
))) {
1133 return NT_STATUS_INVALID_DEVICE_REQUEST
;
1136 status
= vfs_stat_fsp(fsp
);
1137 if (!NT_STATUS_IS_OK(status
)) {
1141 old_dosmode
= fdos_mode(fsp
);
1143 if (sparse
&& !(old_dosmode
& FILE_ATTRIBUTE_SPARSE
)) {
1144 new_dosmode
= old_dosmode
| FILE_ATTRIBUTE_SPARSE
;
1145 } else if (!sparse
&& (old_dosmode
& FILE_ATTRIBUTE_SPARSE
)) {
1146 new_dosmode
= old_dosmode
& ~FILE_ATTRIBUTE_SPARSE
;
1148 return NT_STATUS_OK
;
1151 /* Store the DOS attributes in an EA. */
1152 status
= SMB_VFS_FSET_DOS_ATTRIBUTES(conn
, fsp
, new_dosmode
);
1153 if (!NT_STATUS_IS_OK(status
)) {
1157 notify_fname(conn
, NOTIFY_ACTION_MODIFIED
,
1158 FILE_NOTIFY_CHANGE_ATTRIBUTES
,
1159 fsp
->fsp_name
->base_name
);
1161 fsp
->fsp_name
->st
.cached_dos_attributes
= new_dosmode
;
1162 fsp
->fsp_flags
.is_sparse
= sparse
;
1164 return NT_STATUS_OK
;
1167 /*******************************************************************
1168 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1170 *******************************************************************/
1172 int file_ntimes(connection_struct
*conn
,
1174 struct smb_file_time
*ft
)
1180 DBG_INFO("actime: %s",
1181 time_to_asc(convert_timespec_to_time_t(ft
->atime
)));
1182 DBG_INFO("modtime: %s",
1183 time_to_asc(convert_timespec_to_time_t(ft
->mtime
)));
1184 DBG_INFO("ctime: %s",
1185 time_to_asc(convert_timespec_to_time_t(ft
->ctime
)));
1186 DBG_INFO("createtime: %s",
1187 time_to_asc(convert_timespec_to_time_t(ft
->create_time
)));
1189 /* Don't update the time on read-only shares */
1190 /* We need this as set_filetime (which can be called on
1191 close and other paths) can end up calling this function
1192 without the NEED_WRITE protection. Found by :
1193 Leo Weppelman <leo@wau.mis.ah.nl>
1196 if (!CAN_WRITE(conn
)) {
1200 if (SMB_VFS_FNTIMES(fsp
, ft
) == 0) {
1205 if((errno
!= EPERM
) && (errno
!= EACCES
)) {
1209 if(!lp_dos_filetimes(SNUM(conn
))) {
1213 /* We have permission (given by the Samba admin) to
1214 break POSIX semantics and allow a user to change
1215 the time on a file they don't own but can write to
1219 /* Check if we have write access. */
1220 if (can_write_to_fsp(fsp
)) {
1221 /* We are allowed to become root and change the filetime. */
1223 ret
= SMB_VFS_FNTIMES(fsp
, ft
);
1229 copy_stat_ex_timestamps(&fsp
->fsp_name
->st
, ft
);
1235 /******************************************************************
1236 Force a "sticky" write time on a pathname. This will always be
1237 returned on all future write time queries and set on close.
1238 ******************************************************************/
1240 bool set_sticky_write_time_path(struct file_id fileid
, struct timespec mtime
)
1242 if (is_omit_timespec(&mtime
)) {
1246 if (!set_sticky_write_time(fileid
, mtime
)) {
1253 /******************************************************************
1254 Force a "sticky" write time on an fsp. This will always be
1255 returned on all future write time queries and set on close.
1256 ******************************************************************/
1258 bool set_sticky_write_time_fsp(struct files_struct
*fsp
, struct timespec mtime
)
1260 if (is_omit_timespec(&mtime
)) {
1264 fsp
->fsp_flags
.write_time_forced
= true;
1265 TALLOC_FREE(fsp
->update_write_time_event
);
1267 return set_sticky_write_time_path(fsp
->file_id
, mtime
);
1270 /******************************************************************
1271 Set a create time EA.
1272 ******************************************************************/
1274 NTSTATUS
set_create_timespec_ea(struct files_struct
*fsp
,
1275 struct timespec create_time
)
1280 if (!lp_store_dos_attributes(SNUM(fsp
->conn
))) {
1281 return NT_STATUS_OK
;
1284 dosmode
= fdos_mode(fsp
);
1286 fsp
->fsp_name
->st
.st_ex_btime
= create_time
;
1287 ret
= file_set_dosmode(fsp
->conn
, fsp
->fsp_name
, dosmode
, NULL
, false);
1289 return map_nt_error_from_unix(errno
);
1292 DBG_DEBUG("wrote create time EA for file %s\n",
1293 smb_fname_str_dbg(fsp
->fsp_name
));
1295 return NT_STATUS_OK
;
1298 /******************************************************************
1299 Return a create time.
1300 ******************************************************************/
1302 struct timespec
get_create_timespec(connection_struct
*conn
,
1303 struct files_struct
*fsp
,
1304 const struct smb_filename
*smb_fname
)
1307 struct files_struct
*meta_fsp
= metadata_fsp(fsp
);
1308 return meta_fsp
->fsp_name
->st
.st_ex_btime
;
1310 return smb_fname
->st
.st_ex_btime
;
1313 /******************************************************************
1314 Return a change time (may look at EA in future).
1315 ******************************************************************/
1317 struct timespec
get_change_timespec(connection_struct
*conn
,
1318 struct files_struct
*fsp
,
1319 const struct smb_filename
*smb_fname
)
1321 return smb_fname
->st
.st_ex_mtime
;