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%" PRIx32
"): \"%s\"\n",
77 static uint32_t filter_mode_by_protocol(enum protocol_types protocol
,
80 if (protocol
<= PROTOCOL_LANMAN2
) {
81 DEBUG(10,("filter_mode_by_protocol: "
82 "filtering result 0x%x to 0x%x\n",
84 (unsigned int)(mode
& 0x3f) ));
90 mode_t
apply_conf_file_mask(struct connection_struct
*conn
, mode_t mode
)
92 mode
&= lp_create_mask(SNUM(conn
));
93 mode
|= lp_force_create_mode(SNUM(conn
));
97 mode_t
apply_conf_dir_mask(struct connection_struct
*conn
, mode_t mode
)
99 mode
&= lp_directory_mask(SNUM(conn
));
100 mode
|= lp_force_directory_mode(SNUM(conn
));
104 /****************************************************************************
105 Change a dos mode to a unix mode.
106 Base permission for files:
107 if creating file and inheriting (i.e. parent_dir != NULL)
108 apply read/write bits from parent directory.
110 everybody gets read bit set
111 dos readonly is represented in unix by removing everyone's write bit
112 dos archive is represented in unix by the user's execute bit
113 dos system is represented in unix by the group's execute bit
114 dos hidden is represented in unix by the other's execute bit
116 Then apply create mask,
119 Base permission for directories:
120 dos directory is represented in unix by unix's dir bit and the exec bit
122 Then apply create mask,
125 ****************************************************************************/
127 mode_t
unix_mode(connection_struct
*conn
, int dosmode
,
128 const struct smb_filename
*smb_fname
,
129 struct files_struct
*parent_dirfsp
)
131 mode_t result
= (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IWGRP
| S_IWOTH
);
132 mode_t dir_mode
= 0; /* Mode of the inherit_from directory if
135 if ((dosmode
& FILE_ATTRIBUTE_READONLY
) &&
136 !lp_store_dos_attributes(SNUM(conn
))) {
137 result
&= ~(S_IWUSR
| S_IWGRP
| S_IWOTH
);
140 if ((parent_dirfsp
!= NULL
) && lp_inherit_permissions(SNUM(conn
))) {
141 struct stat_ex sbuf
= { .st_ex_nlink
= 0, };
144 DBG_DEBUG("[%s] inheriting from [%s]\n",
145 smb_fname_str_dbg(smb_fname
),
146 smb_fname_str_dbg(parent_dirfsp
->fsp_name
));
148 ret
= SMB_VFS_FSTAT(parent_dirfsp
, &sbuf
);
150 DBG_ERR("fstat failed [%s]: %s\n",
151 smb_fname_str_dbg(parent_dirfsp
->fsp_name
),
153 return(0); /* *** shouldn't happen! *** */
156 /* Save for later - but explicitly remove setuid bit for safety. */
157 dir_mode
= sbuf
.st_ex_mode
& ~S_ISUID
;
158 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
159 smb_fname_str_dbg(smb_fname
), (int)dir_mode
));
164 if (dosmode
& FILE_ATTRIBUTE_DIRECTORY
) {
165 /* We never make directories read only for the owner as under DOS a user
166 can always create a file in a read-only directory. */
167 result
|= (S_IFDIR
| S_IWUSR
);
170 /* Inherit mode of parent directory. */
173 /* Provisionally add all 'x' bits */
174 result
|= (S_IXUSR
| S_IXGRP
| S_IXOTH
);
175 result
= apply_conf_dir_mask(conn
, result
);
178 if ((dosmode
& FILE_ATTRIBUTE_ARCHIVE
) &&
179 lp_map_archive(SNUM(conn
))) {
183 if ((dosmode
& FILE_ATTRIBUTE_SYSTEM
) &&
184 lp_map_system(SNUM(conn
))) {
188 if ((dosmode
& FILE_ATTRIBUTE_HIDDEN
) &&
189 lp_map_hidden(SNUM(conn
))) {
194 /* Inherit 666 component of parent directory mode */
195 result
|= dir_mode
& (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IWGRP
| S_IWOTH
);
197 result
= apply_conf_file_mask(conn
, result
);
201 DBG_INFO("unix_mode(%s) returning 0%o\n",
202 smb_fname_str_dbg(smb_fname
), (int)result
);
207 /****************************************************************************
208 Change a unix mode to a dos mode.
209 ****************************************************************************/
211 static uint32_t dos_mode_from_sbuf(connection_struct
*conn
,
212 const struct stat_ex
*st
,
213 struct files_struct
*fsp
)
216 enum mapreadonly_options ro_opts
=
217 (enum mapreadonly_options
)lp_map_readonly(SNUM(conn
));
219 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
220 /* if we can find out if a file is immutable we should report it r/o */
221 if (st
->st_ex_flags
& (UF_IMMUTABLE
| SF_IMMUTABLE
)) {
222 result
|= FILE_ATTRIBUTE_READONLY
;
225 if (ro_opts
== MAP_READONLY_YES
) {
226 /* Original Samba method - map inverse of user "w" bit. */
227 if ((st
->st_ex_mode
& S_IWUSR
) == 0) {
228 result
|= FILE_ATTRIBUTE_READONLY
;
231 if (ro_opts
== MAP_READONLY_PERMISSIONS
) {
232 /* smb_fname->fsp can be NULL for an MS-DFS link. */
233 /* Check actual permissions for read-only. */
234 if ((fsp
!= NULL
) && !can_write_to_fsp(fsp
)) {
235 result
|= FILE_ATTRIBUTE_READONLY
;
239 if (MAP_ARCHIVE(conn
) && ((st
->st_ex_mode
& S_IXUSR
) != 0)) {
240 result
|= FILE_ATTRIBUTE_ARCHIVE
;
243 if (MAP_SYSTEM(conn
) && ((st
->st_ex_mode
& S_IXGRP
) != 0)) {
244 result
|= FILE_ATTRIBUTE_SYSTEM
;
247 if (MAP_HIDDEN(conn
) && ((st
->st_ex_mode
& S_IXOTH
) != 0)) {
248 result
|= FILE_ATTRIBUTE_HIDDEN
;
251 if (S_ISDIR(st
->st_ex_mode
)) {
252 result
= FILE_ATTRIBUTE_DIRECTORY
|
253 (result
& FILE_ATTRIBUTE_READONLY
);
256 dos_mode_debug_print(__func__
, result
);
261 /****************************************************************************
262 Get DOS attributes from an EA.
263 This can also pull the create time into the stat struct inside smb_fname.
264 ****************************************************************************/
266 NTSTATUS
parse_dos_attribute_blob(struct smb_filename
*smb_fname
,
270 struct xattr_DOSATTRIB dosattrib
;
271 enum ndr_err_code ndr_err
;
274 ndr_err
= ndr_pull_struct_blob(&blob
, talloc_tos(), &dosattrib
,
275 (ndr_pull_flags_fn_t
)ndr_pull_xattr_DOSATTRIB
);
277 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
278 DBG_WARNING("bad ndr decode "
279 "from EA on file %s: Error = %s\n",
280 smb_fname_str_dbg(smb_fname
),
281 ndr_errstr(ndr_err
));
282 return ndr_map_error2ntstatus(ndr_err
);
285 DBG_DEBUG("%s attr = %s\n",
286 smb_fname_str_dbg(smb_fname
), dosattrib
.attrib_hex
);
288 switch (dosattrib
.version
) {
290 dosattr
= dosattrib
.info
.compatinfoFFFF
.attrib
;
293 dosattr
= dosattrib
.info
.info1
.attrib
;
294 if (!null_nttime(dosattrib
.info
.info1
.create_time
)) {
295 struct timespec create_time
=
296 nt_time_to_unix_timespec(
297 dosattrib
.info
.info1
.create_time
);
299 update_stat_ex_create_time(&smb_fname
->st
,
302 DBG_DEBUG("file %s case 1 set btime %s",
303 smb_fname_str_dbg(smb_fname
),
304 time_to_asc(convert_timespec_to_time_t(
309 dosattr
= dosattrib
.info
.oldinfo2
.attrib
;
310 /* Don't know what flags to check for this case. */
313 dosattr
= dosattrib
.info
.info3
.attrib
;
314 if ((dosattrib
.info
.info3
.valid_flags
& XATTR_DOSINFO_CREATE_TIME
) &&
315 !null_nttime(dosattrib
.info
.info3
.create_time
)) {
316 struct timespec create_time
=
317 nt_time_to_full_timespec(
318 dosattrib
.info
.info3
.create_time
);
320 update_stat_ex_create_time(&smb_fname
->st
,
323 DBG_DEBUG("file %s case 3 set btime %s",
324 smb_fname_str_dbg(smb_fname
),
325 time_to_asc(convert_timespec_to_time_t(
332 uint32_t info_valid_flags
;
333 NTTIME info_create_time
;
335 if (dosattrib
.version
== 4) {
336 info_valid_flags
= dosattrib
.info
.info4
.valid_flags
;
337 info_create_time
= dosattrib
.info
.info4
.create_time
;
338 dosattr
= dosattrib
.info
.info4
.attrib
;
340 info_valid_flags
= dosattrib
.info
.info5
.valid_flags
;
341 info_create_time
= dosattrib
.info
.info5
.create_time
;
342 dosattr
= dosattrib
.info
.info5
.attrib
;
345 if ((info_valid_flags
& XATTR_DOSINFO_CREATE_TIME
) &&
346 !null_nttime(info_create_time
))
348 struct timespec creat_time
;
350 creat_time
= nt_time_to_full_timespec(info_create_time
);
351 update_stat_ex_create_time(&smb_fname
->st
, creat_time
);
353 DBG_DEBUG("file [%s] creation time [%s]\n",
354 smb_fname_str_dbg(smb_fname
),
355 nt_time_string(talloc_tos(), info_create_time
));
361 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
362 smb_fname_str_dbg(smb_fname
), blob
.data
);
363 /* Should this be INTERNAL_ERROR? */
364 return NT_STATUS_INVALID_PARAMETER
;
367 if (S_ISDIR(smb_fname
->st
.st_ex_mode
)) {
368 dosattr
|= FILE_ATTRIBUTE_DIRECTORY
;
372 * _SPARSE and _REPARSE_POINT are valid on get but not on
373 * set. Both are created via special fcntls.
376 dosattr
&= (SAMBA_ATTRIBUTES_MASK
|
377 FILE_ATTRIBUTE_SPARSE
|
378 FILE_ATTRIBUTE_REPARSE_POINT
);
382 dos_mode_debug_print(__func__
, *pattr
);
387 NTSTATUS
fget_ea_dos_attribute(struct files_struct
*fsp
,
395 if (!lp_store_dos_attributes(SNUM(fsp
->conn
))) {
396 return NT_STATUS_NOT_IMPLEMENTED
;
399 /* Don't reset pattr to zero as we may already have filename-based attributes we
402 sizeret
= SMB_VFS_FGETXATTR(fsp
,
403 SAMBA_XATTR_DOS_ATTRIB
,
406 if (sizeret
== -1 && ( errno
== EPERM
|| errno
== EACCES
)) {
407 /* we may also retrieve dos attribs for unreadable files, this
408 is why we'll retry as root. We don't use root in the first
409 run because in cases like NFS, root might have even less
410 rights than the real user
413 sizeret
= SMB_VFS_FGETXATTR(fsp
,
414 SAMBA_XATTR_DOS_ATTRIB
,
420 DBG_INFO("Cannot get attribute "
421 "from EA on file %s: Error = %s\n",
422 fsp_str_dbg(fsp
), strerror(errno
));
423 return map_nt_error_from_unix(errno
);
426 blob
.data
= (uint8_t *)attrstr
;
427 blob
.length
= sizeret
;
429 status
= parse_dos_attribute_blob(fsp
->fsp_name
, blob
, pattr
);
430 if (!NT_STATUS_IS_OK(status
)) {
437 /****************************************************************************
438 Set DOS attributes in an EA.
439 Also sets the create time.
440 ****************************************************************************/
442 NTSTATUS
set_ea_dos_attribute(connection_struct
*conn
,
443 struct smb_filename
*smb_fname
,
446 struct xattr_DOSATTRIB dosattrib
= { .version
= 0, };
447 enum ndr_err_code ndr_err
;
448 DATA_BLOB blob
= { .data
= NULL
, };
449 struct timespec btime
;
452 if (!lp_store_dos_attributes(SNUM(conn
))) {
453 return NT_STATUS_NOT_IMPLEMENTED
;
456 if (smb_fname
->fsp
== NULL
) {
458 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
461 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
462 * vfs_default via DMAPI if that is enabled.
464 dosmode
&= ~FILE_ATTRIBUTE_OFFLINE
;
466 dosattrib
.version
= 5;
467 dosattrib
.info
.info5
.valid_flags
= XATTR_DOSINFO_ATTRIB
|
468 XATTR_DOSINFO_CREATE_TIME
;
469 dosattrib
.info
.info5
.attrib
= dosmode
;
470 dosattrib
.info
.info5
.create_time
= full_timespec_to_nt_time(
471 &smb_fname
->st
.st_ex_btime
);
473 DBG_DEBUG("set attribute 0x%" PRIx32
", btime = %s on file %s\n",
475 time_to_asc(convert_timespec_to_time_t(
476 smb_fname
->st
.st_ex_btime
)),
477 smb_fname_str_dbg(smb_fname
));
479 ndr_err
= ndr_push_struct_blob(
480 &blob
, talloc_tos(), &dosattrib
,
481 (ndr_push_flags_fn_t
)ndr_push_xattr_DOSATTRIB
);
483 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
484 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
485 ndr_errstr(ndr_err
)));
486 return ndr_map_error2ntstatus(ndr_err
);
489 if (blob
.data
== NULL
|| blob
.length
== 0) {
490 /* Should this be INTERNAL_ERROR? */
491 return NT_STATUS_INVALID_PARAMETER
;
494 ret
= SMB_VFS_FSETXATTR(smb_fname
->fsp
,
495 SAMBA_XATTR_DOS_ATTRIB
,
496 blob
.data
, blob
.length
, 0);
498 NTSTATUS status
= NT_STATUS_OK
;
499 bool set_dosmode_ok
= false;
501 if ((errno
!= EPERM
) && (errno
!= EACCES
)) {
502 DBG_INFO("Cannot set "
503 "attribute EA on file %s: Error = %s\n",
504 smb_fname_str_dbg(smb_fname
), strerror(errno
));
505 return map_nt_error_from_unix(errno
);
508 /* We want DOS semantics, ie allow non owner with write permission to change the
509 bits on a file. Just like file_ntimes below.
512 /* Check if we have write access. */
513 if (!CAN_WRITE(conn
)) {
514 return NT_STATUS_ACCESS_DENIED
;
517 status
= smbd_check_access_rights_fsp(conn
->cwd_fsp
,
520 FILE_WRITE_ATTRIBUTES
);
521 if (NT_STATUS_IS_OK(status
)) {
522 set_dosmode_ok
= true;
525 if (!set_dosmode_ok
&& lp_dos_filemode(SNUM(conn
))) {
526 set_dosmode_ok
= can_write_to_fsp(smb_fname
->fsp
);
529 if (!set_dosmode_ok
) {
530 return NT_STATUS_ACCESS_DENIED
;
534 ret
= SMB_VFS_FSETXATTR(smb_fname
->fsp
,
535 SAMBA_XATTR_DOS_ATTRIB
,
536 blob
.data
, blob
.length
, 0);
538 status
= NT_STATUS_OK
;
541 if (!NT_STATUS_IS_OK(status
)) {
547 * We correctly stored the create time.
548 * We *always* set XATTR_DOSINFO_CREATE_TIME,
549 * so now it can no longer be considered
550 * calculated. Make sure to use the value rounded
551 * to NTTIME granularity we've stored in the xattr.
553 btime
= nt_time_to_full_timespec(dosattrib
.info
.info5
.create_time
);
554 update_stat_ex_create_time(&smb_fname
->st
, btime
);
556 DBG_DEBUG("set EA 0x%" PRIx32
" on file %s\n",
558 smb_fname_str_dbg(smb_fname
));
563 dos_mode_from_name(connection_struct
*conn
, const char *name
, uint32_t dosmode
)
565 const char *p
= NULL
;
566 uint32_t result
= dosmode
;
568 if (!(result
& FILE_ATTRIBUTE_HIDDEN
) &&
569 lp_hide_dot_files(SNUM(conn
)))
571 p
= strrchr_m(name
, '/');
578 /* Only . and .. are not hidden. */
579 if ((p
[0] == '.') && !(ISDOT(p
) || ISDOTDOT(p
))) {
580 result
|= FILE_ATTRIBUTE_HIDDEN
;
584 if (!(result
& FILE_ATTRIBUTE_HIDDEN
) && IS_HIDDEN_PATH(conn
, name
)) {
585 result
|= FILE_ATTRIBUTE_HIDDEN
;
591 /****************************************************************************
592 Change a unix mode to a dos mode for an ms dfs link.
593 ****************************************************************************/
595 uint32_t dos_mode_msdfs(connection_struct
*conn
,
597 const struct stat_ex
*st
)
601 DEBUG(8, ("dos_mode_msdfs: %s\n", name
));
603 if (!VALID_STAT(*st
)) {
607 result
= dos_mode_from_name(conn
, name
, result
);
608 result
|= dos_mode_from_sbuf(conn
, st
, NULL
);
611 result
= FILE_ATTRIBUTE_NORMAL
;
614 result
= filter_mode_by_protocol(conn_protocol(conn
->sconn
), result
);
617 * Add in that it is a reparse point
619 result
|= FILE_ATTRIBUTE_REPARSE_POINT
;
621 dos_mode_debug_print(__func__
, result
);
627 * check whether a file or directory is flagged as compressed.
629 static NTSTATUS
dos_mode_check_compressed(struct files_struct
*fsp
,
633 uint16_t compression_fmt
;
635 status
= SMB_VFS_FGET_COMPRESSION(
636 fsp
->conn
, talloc_tos(), fsp
, &compression_fmt
);
637 if (!NT_STATUS_IS_OK(status
)) {
641 if (compression_fmt
== COMPRESSION_FORMAT_LZNT1
) {
642 *is_compressed
= true;
644 *is_compressed
= false;
649 static uint32_t dos_mode_post(uint32_t dosmode
,
650 struct files_struct
*fsp
,
653 struct smb_filename
*smb_fname
= NULL
;
657 smb_fname
= fsp
->fsp_name
;
659 SMB_ASSERT(smb_fname
!= NULL
);
662 * According to MS-FSA a stream name does not have
663 * separate DOS attribute metadata, so we must return
664 * the DOS attribute from the base filename. With one caveat,
665 * a non-default stream name can never be a directory.
667 * As this is common to all streams data stores, we handle
668 * it here instead of inside all stream VFS modules.
670 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
673 if (is_named_stream(smb_fname
)) {
674 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
675 dosmode
&= ~(FILE_ATTRIBUTE_DIRECTORY
);
678 if (fsp
->conn
->fs_capabilities
& FILE_FILE_COMPRESSION
) {
679 bool compressed
= false;
681 status
= dos_mode_check_compressed(fsp
, &compressed
);
682 if (NT_STATUS_IS_OK(status
) && compressed
) {
683 dosmode
|= FILE_ATTRIBUTE_COMPRESSED
;
687 dosmode
|= dos_mode_from_name(fsp
->conn
, smb_fname
->base_name
, dosmode
);
689 if (S_ISDIR(smb_fname
->st
.st_ex_mode
)) {
690 dosmode
|= FILE_ATTRIBUTE_DIRECTORY
;
691 } else if (dosmode
== 0) {
692 dosmode
= FILE_ATTRIBUTE_NORMAL
;
695 dosmode
= filter_mode_by_protocol(conn_protocol(fsp
->conn
->sconn
),
698 dos_mode_debug_print(func
, dosmode
);
702 /****************************************************************************
703 Change a unix mode to a dos mode.
704 May also read the create timespec into the stat struct in smb_fname
705 if "store dos attributes" is true.
706 ****************************************************************************/
708 uint32_t fdos_mode(struct files_struct
*fsp
)
711 NTSTATUS status
= NT_STATUS_OK
;
713 DBG_DEBUG("%s\n", fsp_str_dbg(fsp
));
715 if (fsp
->fake_file_handle
!= NULL
) {
716 return dosmode_from_fake_filehandle(fsp
->fake_file_handle
);
719 if (!VALID_STAT(fsp
->fsp_name
->st
)) {
723 switch (fsp
->fsp_name
->st
.st_ex_mode
& S_IFMT
) {
725 return FILE_ATTRIBUTE_NORMAL
;
731 return FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_REPARSE_POINT
;
737 if (fsp
->fsp_name
->st
.cached_dos_attributes
!= FILE_ATTRIBUTE_INVALID
) {
738 return fsp
->fsp_name
->st
.cached_dos_attributes
;
741 /* Get the DOS attributes via the VFS if we can */
742 status
= SMB_VFS_FGET_DOS_ATTRIBUTES(fsp
->conn
,
746 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_IMPLEMENTED
)) {
747 result
|= dos_mode_from_sbuf(fsp
->conn
,
752 fsp
->fsp_name
->st
.cached_dos_attributes
= dos_mode_post(result
, fsp
, __func__
);
753 return fsp
->fsp_name
->st
.cached_dos_attributes
;
756 struct dos_mode_at_state
{
757 files_struct
*dir_fsp
;
758 struct smb_filename
*smb_fname
;
762 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req
*subreq
);
764 struct tevent_req
*dos_mode_at_send(TALLOC_CTX
*mem_ctx
,
765 struct tevent_context
*ev
,
766 files_struct
*dir_fsp
,
767 struct smb_filename
*smb_fname
)
769 struct tevent_req
*req
= NULL
;
770 struct dos_mode_at_state
*state
= NULL
;
771 struct tevent_req
*subreq
= NULL
;
773 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname
));
775 req
= tevent_req_create(mem_ctx
, &state
,
776 struct dos_mode_at_state
);
781 *state
= (struct dos_mode_at_state
) {
783 .smb_fname
= smb_fname
,
786 if (!VALID_STAT(smb_fname
->st
)) {
787 tevent_req_done(req
);
788 return tevent_req_post(req
, ev
);
791 if (smb_fname
->fsp
== NULL
) {
792 if (ISDOTDOT(smb_fname
->base_name
)) {
794 * smb_fname->fsp is explicitly closed
795 * for ".." to prevent meta-data leakage.
797 state
->dosmode
= FILE_ATTRIBUTE_DIRECTORY
;
800 * This is a symlink in POSIX context.
801 * FIXME ? Should we move to returning
802 * FILE_ATTRIBUTE_REPARSE_POINT here ?
804 state
->dosmode
= FILE_ATTRIBUTE_NORMAL
;
806 tevent_req_done(req
);
807 return tevent_req_post(req
, ev
);
810 subreq
= SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state
,
814 if (tevent_req_nomem(subreq
, req
)) {
815 return tevent_req_post(req
, ev
);
817 tevent_req_set_callback(subreq
, dos_mode_at_vfs_get_dosmode_done
, req
);
822 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req
*subreq
)
824 struct tevent_req
*req
=
825 tevent_req_callback_data(subreq
,
827 struct dos_mode_at_state
*state
=
829 struct dos_mode_at_state
);
830 struct vfs_aio_state aio_state
;
835 * Make sure we run as the user again
837 ok
= change_to_user_and_service_by_fsp(state
->dir_fsp
);
840 status
= SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq
,
844 if (!NT_STATUS_IS_OK(status
)) {
846 * Both the sync dos_mode() as well as the async
847 * dos_mode_at_[send|recv] have no real error return, the only
848 * unhandled error is when the stat info in smb_fname is not
849 * valid (cf the checks in dos_mode() and dos_mode_at_send().
851 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
852 * dos_mode_post() which also does the mapping of a last resort
853 * from S_IFMT(st_mode).
855 * Only if we get NT_STATUS_NOT_IMPLEMENTED or
856 * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
857 * fallback to sync processing.
859 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_IMPLEMENTED
) &&
860 !NT_STATUS_EQUAL(status
, NT_STATUS_NOT_SUPPORTED
))
863 * state->dosmode should still be 0, but reset
867 status
= NT_STATUS_OK
;
870 if (NT_STATUS_IS_OK(status
)) {
871 state
->dosmode
= dos_mode_post(state
->dosmode
,
872 state
->smb_fname
->fsp
,
874 tevent_req_done(req
);
879 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
882 state
->dosmode
= fdos_mode(state
->smb_fname
->fsp
);
883 tevent_req_done(req
);
887 NTSTATUS
dos_mode_at_recv(struct tevent_req
*req
, uint32_t *dosmode
)
889 struct dos_mode_at_state
*state
=
891 struct dos_mode_at_state
);
894 if (tevent_req_is_nterror(req
, &status
)) {
895 tevent_req_received(req
);
899 *dosmode
= state
->dosmode
;
900 tevent_req_received(req
);
904 /*******************************************************************
905 chmod a file - but preserve some bits.
906 If "store dos attributes" is also set it will store the create time
907 from the stat struct in smb_fname (in NTTIME format) in the EA
909 ********************************************************************/
911 int file_set_dosmode(connection_struct
*conn
,
912 struct smb_filename
*smb_fname
,
914 struct smb_filename
*parent_dir
,
923 if (!CAN_WRITE(conn
)) {
928 if (S_ISLNK(smb_fname
->st
.st_ex_mode
)) {
929 /* A symlink in POSIX context, ignore */
933 if ((S_ISDIR(smb_fname
->st
.st_ex_mode
)) &&
934 (dosmode
& FILE_ATTRIBUTE_TEMPORARY
))
940 dosmode
&= SAMBA_ATTRIBUTES_MASK
;
942 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
943 dosmode
, smb_fname_str_dbg(smb_fname
)));
945 if (smb_fname
->fsp
== NULL
) {
950 if (smb_fname
->fsp
->fsp_flags
.posix_open
&&
951 !lp_store_dos_attributes(SNUM(conn
)))
956 unixmode
= smb_fname
->st
.st_ex_mode
;
958 get_acl_group_bits(conn
, smb_fname
->fsp
, &smb_fname
->st
.st_ex_mode
);
960 if (S_ISDIR(smb_fname
->st
.st_ex_mode
))
961 dosmode
|= FILE_ATTRIBUTE_DIRECTORY
;
963 dosmode
&= ~FILE_ATTRIBUTE_DIRECTORY
;
965 /* Store the DOS attributes in an EA by preference. */
966 status
= SMB_VFS_FSET_DOS_ATTRIBUTES(conn
,
967 metadata_fsp(smb_fname
->fsp
),
969 if (NT_STATUS_IS_OK(status
)) {
970 smb_fname
->st
.cached_dos_attributes
= dosmode
;
976 * Only fall back to using UNIX modes if
977 * we get NOT_IMPLEMENTED.
979 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_IMPLEMENTED
)) {
980 errno
= map_errno_from_nt_status(status
);
984 /* Fall back to UNIX modes. */
985 unixmode
= unix_mode(
989 parent_dir
!= NULL
? parent_dir
->fsp
: NULL
);
991 /* preserve the file type bits */
994 /* preserve the s bits */
995 mask
|= (S_ISUID
| S_ISGID
);
997 /* preserve the t bit */
1002 /* possibly preserve the x bits */
1003 if (!MAP_ARCHIVE(conn
))
1005 if (!MAP_SYSTEM(conn
))
1007 if (!MAP_HIDDEN(conn
))
1010 unixmode
|= (smb_fname
->st
.st_ex_mode
& mask
);
1012 /* if we previously had any r bits set then leave them alone */
1013 if ((tmp
= smb_fname
->st
.st_ex_mode
& (S_IRUSR
|S_IRGRP
|S_IROTH
))) {
1014 unixmode
&= ~(S_IRUSR
|S_IRGRP
|S_IROTH
);
1018 /* if we previously had any w bits set then leave them alone
1019 whilst adding in the new w bits, if the new mode is not rdonly */
1020 if (!(dosmode
& FILE_ATTRIBUTE_READONLY
)) {
1021 unixmode
|= (smb_fname
->st
.st_ex_mode
& (S_IWUSR
|S_IWGRP
|S_IWOTH
));
1025 * From the chmod 2 man page:
1027 * "If the calling process is not privileged, and the group of the file
1028 * does not match the effective group ID of the process or one of its
1029 * supplementary group IDs, the S_ISGID bit will be turned off, but
1030 * this will not cause an error to be returned."
1032 * Simply refuse to do the chmod in this case.
1035 if (S_ISDIR(smb_fname
->st
.st_ex_mode
) &&
1036 (unixmode
& S_ISGID
) &&
1037 geteuid() != sec_initial_uid() &&
1038 !current_user_in_group(conn
, smb_fname
->st
.st_ex_gid
))
1040 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1041 "set for directory %s\n",
1042 smb_fname_str_dbg(smb_fname
)));
1047 ret
= SMB_VFS_FCHMOD(smb_fname
->fsp
, unixmode
);
1052 if((errno
!= EPERM
) && (errno
!= EACCES
))
1055 if(!lp_dos_filemode(SNUM(conn
)))
1058 /* We want DOS semantics, ie allow non owner with write permission to change the
1059 bits on a file. Just like file_ntimes below.
1062 if (!can_write_to_fsp(smb_fname
->fsp
))
1069 ret
= SMB_VFS_FCHMOD(smb_fname
->fsp
, unixmode
);
1075 NOTIFY_ACTION_MODIFIED
|
1076 NOTIFY_ACTION_DIRLEASE_BREAK
,
1077 FILE_NOTIFY_CHANGE_ATTRIBUTES
,
1079 fsp_get_smb2_lease(smb_fname
->fsp
));
1082 smb_fname
->st
.st_ex_mode
= unixmode
;
1089 NTSTATUS
file_set_sparse(connection_struct
*conn
,
1093 const struct loadparm_substitution
*lp_sub
=
1094 loadparm_s3_global_substitution();
1095 uint32_t old_dosmode
;
1096 uint32_t new_dosmode
;
1099 if (!CAN_WRITE(conn
)) {
1100 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1101 "on readonly share[%s]\n",
1102 smb_fname_str_dbg(fsp
->fsp_name
),
1104 lp_servicename(talloc_tos(), lp_sub
, SNUM(conn
))));
1105 return NT_STATUS_MEDIA_WRITE_PROTECTED
;
1109 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1110 * following access flags are granted.
1112 status
= check_any_access_fsp(fsp
,
1114 | FILE_WRITE_ATTRIBUTES
1115 | SEC_FILE_APPEND_DATA
);
1116 if (!NT_STATUS_IS_OK(status
)) {
1117 DBG_DEBUG("fname[%s] set[%u] "
1118 "access_mask[0x%08X] - access denied\n",
1119 smb_fname_str_dbg(fsp
->fsp_name
),
1125 if (fsp
->fsp_flags
.is_directory
) {
1126 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1127 (sparse
? "set" : "clear"),
1128 smb_fname_str_dbg(fsp
->fsp_name
)));
1129 return NT_STATUS_INVALID_PARAMETER
;
1132 if (IS_IPC(conn
) || IS_PRINT(conn
)) {
1133 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1134 (sparse
? "set" : "clear")));
1135 return NT_STATUS_INVALID_PARAMETER
;
1138 if (fsp_is_alternate_stream(fsp
)) {
1140 * MS-FSA 2.1.1.5 IsSparse
1142 * This is a per stream attribute, but our backends don't
1143 * support it a consistent way, therefore just pretend
1144 * success and ignore the request.
1146 DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
1147 "[%s]\n", fsp_str_dbg(fsp
));
1148 return NT_STATUS_OK
;
1151 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1152 sparse
, smb_fname_str_dbg(fsp
->fsp_name
)));
1154 if (!lp_store_dos_attributes(SNUM(conn
))) {
1155 return NT_STATUS_INVALID_DEVICE_REQUEST
;
1158 status
= vfs_stat_fsp(fsp
);
1159 if (!NT_STATUS_IS_OK(status
)) {
1163 old_dosmode
= fdos_mode(fsp
);
1165 if (sparse
&& !(old_dosmode
& FILE_ATTRIBUTE_SPARSE
)) {
1166 new_dosmode
= old_dosmode
| FILE_ATTRIBUTE_SPARSE
;
1167 } else if (!sparse
&& (old_dosmode
& FILE_ATTRIBUTE_SPARSE
)) {
1168 new_dosmode
= old_dosmode
& ~FILE_ATTRIBUTE_SPARSE
;
1170 return NT_STATUS_OK
;
1173 /* Store the DOS attributes in an EA. */
1174 status
= SMB_VFS_FSET_DOS_ATTRIBUTES(conn
, fsp
, new_dosmode
);
1175 if (!NT_STATUS_IS_OK(status
)) {
1180 NOTIFY_ACTION_MODIFIED
|
1181 NOTIFY_ACTION_DIRLEASE_BREAK
,
1182 FILE_NOTIFY_CHANGE_ATTRIBUTES
,
1184 fsp_get_smb2_lease(fsp
));
1186 fsp
->fsp_name
->st
.cached_dos_attributes
= new_dosmode
;
1187 fsp
->fsp_flags
.is_sparse
= sparse
;
1189 return NT_STATUS_OK
;
1192 /*******************************************************************
1193 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1195 *******************************************************************/
1197 int file_ntimes(connection_struct
*conn
,
1199 struct smb_file_time
*ft
)
1205 DBG_INFO("actime: %s",
1206 time_to_asc(convert_timespec_to_time_t(ft
->atime
)));
1207 DBG_INFO("modtime: %s",
1208 time_to_asc(convert_timespec_to_time_t(ft
->mtime
)));
1209 DBG_INFO("ctime: %s",
1210 time_to_asc(convert_timespec_to_time_t(ft
->ctime
)));
1211 DBG_INFO("createtime: %s",
1212 time_to_asc(convert_timespec_to_time_t(ft
->create_time
)));
1214 /* Don't update the time on read-only shares */
1215 /* We need this as set_filetime (which can be called on
1216 close and other paths) can end up calling this function
1217 without the NEED_WRITE protection. Found by :
1218 Leo Weppelman <leo@wau.mis.ah.nl>
1221 if (!CAN_WRITE(conn
)) {
1225 if (SMB_VFS_FNTIMES(fsp
, ft
) == 0) {
1230 if((errno
!= EPERM
) && (errno
!= EACCES
)) {
1234 if(!lp_dos_filetimes(SNUM(conn
))) {
1238 /* We have permission (given by the Samba admin) to
1239 break POSIX semantics and allow a user to change
1240 the time on a file they don't own but can write to
1244 /* Check if we have write access. */
1245 if (can_write_to_fsp(fsp
)) {
1246 /* We are allowed to become root and change the filetime. */
1248 ret
= SMB_VFS_FNTIMES(fsp
, ft
);
1254 copy_stat_ex_timestamps(&fsp
->fsp_name
->st
, ft
);
1260 /******************************************************************
1261 Force a "sticky" write time on an fsp. This will always be
1262 returned on all future write time queries and set on close.
1263 ******************************************************************/
1265 bool set_sticky_write_time_fsp(struct files_struct
*fsp
, struct timespec mtime
)
1269 if (is_omit_timespec(&mtime
)) {
1273 fsp
->fsp_flags
.write_time_forced
= true;
1274 TALLOC_FREE(fsp
->update_write_time_event
);
1276 ok
= set_sticky_write_time(fsp
->file_id
, mtime
);
1280 /******************************************************************
1281 Set a create time EA.
1282 ******************************************************************/
1284 NTSTATUS
set_create_timespec_ea(struct files_struct
*fsp
,
1285 struct timespec create_time
)
1290 if (!lp_store_dos_attributes(SNUM(fsp
->conn
))) {
1291 return NT_STATUS_OK
;
1294 dosmode
= fdos_mode(fsp
);
1296 fsp
->fsp_name
->st
.st_ex_btime
= create_time
;
1297 ret
= file_set_dosmode(fsp
->conn
, fsp
->fsp_name
, dosmode
, NULL
, false);
1299 return map_nt_error_from_unix(errno
);
1302 DBG_DEBUG("wrote create time EA for file %s\n",
1303 smb_fname_str_dbg(fsp
->fsp_name
));
1305 return NT_STATUS_OK
;
1308 /******************************************************************
1309 Return a create time.
1310 ******************************************************************/
1312 struct timespec
get_create_timespec(connection_struct
*conn
,
1313 struct files_struct
*fsp
,
1314 const struct smb_filename
*smb_fname
)
1317 struct files_struct
*meta_fsp
= metadata_fsp(fsp
);
1318 return meta_fsp
->fsp_name
->st
.st_ex_btime
;
1320 return smb_fname
->st
.st_ex_btime
;
1323 /******************************************************************
1324 Return a change time (may look at EA in future).
1325 ******************************************************************/
1327 struct timespec
get_change_timespec(connection_struct
*conn
,
1328 struct files_struct
*fsp
,
1329 const struct smb_filename
*smb_fname
)
1331 return smb_fname
->st
.st_ex_mtime
;