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
;
358 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
359 *pattr
|= (uint32_t)(dosattr
& (SAMBA_ATTRIBUTES_MASK
|FILE_ATTRIBUTE_SPARSE
));
361 dos_mode_debug_print(__func__
, *pattr
);
366 NTSTATUS
fget_ea_dos_attribute(struct files_struct
*fsp
,
374 if (!lp_store_dos_attributes(SNUM(fsp
->conn
))) {
375 return NT_STATUS_NOT_IMPLEMENTED
;
378 /* Don't reset pattr to zero as we may already have filename-based attributes we
381 sizeret
= SMB_VFS_FGETXATTR(fsp
,
382 SAMBA_XATTR_DOS_ATTRIB
,
385 if (sizeret
== -1 && ( errno
== EPERM
|| errno
== EACCES
)) {
386 /* we may also retrieve dos attribs for unreadable files, this
387 is why we'll retry as root. We don't use root in the first
388 run because in cases like NFS, root might have even less
389 rights than the real user
392 sizeret
= SMB_VFS_FGETXATTR(fsp
,
393 SAMBA_XATTR_DOS_ATTRIB
,
399 DBG_INFO("Cannot get attribute "
400 "from EA on file %s: Error = %s\n",
401 fsp_str_dbg(fsp
), strerror(errno
));
402 return map_nt_error_from_unix(errno
);
405 blob
.data
= (uint8_t *)attrstr
;
406 blob
.length
= sizeret
;
408 status
= parse_dos_attribute_blob(fsp
->fsp_name
, blob
, pattr
);
409 if (!NT_STATUS_IS_OK(status
)) {
416 /****************************************************************************
417 Set DOS attributes in an EA.
418 Also sets the create time.
419 ****************************************************************************/
421 NTSTATUS
set_ea_dos_attribute(connection_struct
*conn
,
422 struct smb_filename
*smb_fname
,
425 struct xattr_DOSATTRIB dosattrib
= { .version
= 0, };
426 enum ndr_err_code ndr_err
;
427 DATA_BLOB blob
= { .data
= NULL
, };
428 struct timespec btime
;
431 if (!lp_store_dos_attributes(SNUM(conn
))) {
432 return NT_STATUS_NOT_IMPLEMENTED
;
435 if (smb_fname
->fsp
== NULL
) {
437 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
440 * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
441 * vfs_default via DMAPI if that is enabled.
443 dosmode
&= ~FILE_ATTRIBUTE_OFFLINE
;
445 dosattrib
.version
= 5;
446 dosattrib
.info
.info5
.valid_flags
= XATTR_DOSINFO_ATTRIB
|
447 XATTR_DOSINFO_CREATE_TIME
;
448 dosattrib
.info
.info5
.attrib
= dosmode
;
449 dosattrib
.info
.info5
.create_time
= full_timespec_to_nt_time(
450 &smb_fname
->st
.st_ex_btime
);
452 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
453 (unsigned int)dosmode
,
454 time_to_asc(convert_timespec_to_time_t(smb_fname
->st
.st_ex_btime
)),
455 smb_fname_str_dbg(smb_fname
) ));
457 ndr_err
= ndr_push_struct_blob(
458 &blob
, talloc_tos(), &dosattrib
,
459 (ndr_push_flags_fn_t
)ndr_push_xattr_DOSATTRIB
);
461 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
462 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
463 ndr_errstr(ndr_err
)));
464 return ndr_map_error2ntstatus(ndr_err
);
467 if (blob
.data
== NULL
|| blob
.length
== 0) {
468 /* Should this be INTERNAL_ERROR? */
469 return NT_STATUS_INVALID_PARAMETER
;
472 ret
= SMB_VFS_FSETXATTR(smb_fname
->fsp
,
473 SAMBA_XATTR_DOS_ATTRIB
,
474 blob
.data
, blob
.length
, 0);
476 NTSTATUS status
= NT_STATUS_OK
;
477 bool set_dosmode_ok
= false;
479 if ((errno
!= EPERM
) && (errno
!= EACCES
)) {
480 DBG_INFO("Cannot set "
481 "attribute EA on file %s: Error = %s\n",
482 smb_fname_str_dbg(smb_fname
), strerror(errno
));
483 return map_nt_error_from_unix(errno
);
486 /* We want DOS semantics, ie allow non owner with write permission to change the
487 bits on a file. Just like file_ntimes below.
490 /* Check if we have write access. */
491 if (!CAN_WRITE(conn
)) {
492 return NT_STATUS_ACCESS_DENIED
;
495 status
= smbd_check_access_rights_fsp(conn
->cwd_fsp
,
498 FILE_WRITE_ATTRIBUTES
);
499 if (NT_STATUS_IS_OK(status
)) {
500 set_dosmode_ok
= true;
503 if (!set_dosmode_ok
&& lp_dos_filemode(SNUM(conn
))) {
504 set_dosmode_ok
= can_write_to_fsp(smb_fname
->fsp
);
507 if (!set_dosmode_ok
) {
508 return NT_STATUS_ACCESS_DENIED
;
512 ret
= SMB_VFS_FSETXATTR(smb_fname
->fsp
,
513 SAMBA_XATTR_DOS_ATTRIB
,
514 blob
.data
, blob
.length
, 0);
516 status
= NT_STATUS_OK
;
519 if (!NT_STATUS_IS_OK(status
)) {
525 * We correctly stored the create time.
526 * We *always* set XATTR_DOSINFO_CREATE_TIME,
527 * so now it can no longer be considered
528 * calculated. Make sure to use the value rounded
529 * to NTTIME granularity we've stored in the xattr.
531 btime
= nt_time_to_full_timespec(dosattrib
.info
.info5
.create_time
);
532 update_stat_ex_create_time(&smb_fname
->st
, btime
);
534 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
535 (unsigned int)dosmode
,
536 smb_fname_str_dbg(smb_fname
)));
541 dos_mode_from_name(connection_struct
*conn
, const char *name
, uint32_t dosmode
)
543 const char *p
= NULL
;
544 uint32_t result
= dosmode
;
546 if (!(result
& FILE_ATTRIBUTE_HIDDEN
) &&
547 lp_hide_dot_files(SNUM(conn
)))
549 p
= strrchr_m(name
, '/');
556 /* Only . and .. are not hidden. */
557 if ((p
[0] == '.') && !(ISDOT(p
) || ISDOTDOT(p
))) {
558 result
|= FILE_ATTRIBUTE_HIDDEN
;
562 if (!(result
& FILE_ATTRIBUTE_HIDDEN
) && IS_HIDDEN_PATH(conn
, name
)) {
563 result
|= FILE_ATTRIBUTE_HIDDEN
;
569 /****************************************************************************
570 Change a unix mode to a dos mode for an ms dfs link.
571 ****************************************************************************/
573 uint32_t dos_mode_msdfs(connection_struct
*conn
,
575 const struct stat_ex
*st
)
579 DEBUG(8, ("dos_mode_msdfs: %s\n", name
));
581 if (!VALID_STAT(*st
)) {
585 result
= dos_mode_from_name(conn
, name
, result
);
586 result
|= dos_mode_from_sbuf(conn
, st
, NULL
);
589 result
= FILE_ATTRIBUTE_NORMAL
;
592 result
= filter_mode_by_protocol(conn_protocol(conn
->sconn
), result
);
595 * Add in that it is a reparse point
597 result
|= FILE_ATTRIBUTE_REPARSE_POINT
;
599 dos_mode_debug_print(__func__
, result
);
605 * check whether a file or directory is flagged as compressed.
607 static NTSTATUS
dos_mode_check_compressed(struct files_struct
*fsp
,
611 uint16_t compression_fmt
;
613 status
= SMB_VFS_FGET_COMPRESSION(
614 fsp
->conn
, talloc_tos(), fsp
, &compression_fmt
);
615 if (!NT_STATUS_IS_OK(status
)) {
619 if (compression_fmt
== COMPRESSION_FORMAT_LZNT1
) {
620 *is_compressed
= true;
622 *is_compressed
= false;
627 static uint32_t dos_mode_post(uint32_t dosmode
,
628 struct files_struct
*fsp
,
631 struct smb_filename
*smb_fname
= NULL
;
635 smb_fname
= fsp
->fsp_name
;
637 SMB_ASSERT(smb_fname
!= NULL
);
640 * According to MS-FSA a stream name does not have
641 * separate DOS attribute metadata, so we must return
642 * the DOS attribute from the base filename. With one caveat,
643 * a non-default stream name can never be a directory.
645 * As this is common to all streams data stores, we handle
646 * it here instead of inside all stream VFS modules.
648 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
651 if (is_named_stream(smb_fname
)) {
652 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
653 dosmode
&= ~(FILE_ATTRIBUTE_DIRECTORY
);
656 if (fsp
->conn
->fs_capabilities
& FILE_FILE_COMPRESSION
) {
657 bool compressed
= false;
659 status
= dos_mode_check_compressed(fsp
, &compressed
);
660 if (NT_STATUS_IS_OK(status
) && compressed
) {
661 dosmode
|= FILE_ATTRIBUTE_COMPRESSED
;
665 dosmode
|= dos_mode_from_name(fsp
->conn
, smb_fname
->base_name
, dosmode
);
667 if (S_ISDIR(smb_fname
->st
.st_ex_mode
)) {
668 dosmode
|= FILE_ATTRIBUTE_DIRECTORY
;
669 } else if (dosmode
== 0) {
670 dosmode
= FILE_ATTRIBUTE_NORMAL
;
673 dosmode
= filter_mode_by_protocol(conn_protocol(fsp
->conn
->sconn
),
676 dos_mode_debug_print(func
, dosmode
);
680 /****************************************************************************
681 Change a unix mode to a dos mode.
682 May also read the create timespec into the stat struct in smb_fname
683 if "store dos attributes" is true.
684 ****************************************************************************/
686 uint32_t fdos_mode(struct files_struct
*fsp
)
689 NTSTATUS status
= NT_STATUS_OK
;
691 DBG_DEBUG("%s\n", fsp_str_dbg(fsp
));
693 if (fsp
->fake_file_handle
!= NULL
) {
694 return dosmode_from_fake_filehandle(fsp
->fake_file_handle
);
697 if (!VALID_STAT(fsp
->fsp_name
->st
)) {
701 if (S_ISLNK(fsp
->fsp_name
->st
.st_ex_mode
)) {
702 return FILE_ATTRIBUTE_NORMAL
;
705 if (fsp
->fsp_name
->st
.cached_dos_attributes
!= FILE_ATTRIBUTE_INVALID
) {
706 return fsp
->fsp_name
->st
.cached_dos_attributes
;
709 /* Get the DOS attributes via the VFS if we can */
710 status
= SMB_VFS_FGET_DOS_ATTRIBUTES(fsp
->conn
,
713 if (!NT_STATUS_IS_OK(status
)) {
715 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
717 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_IMPLEMENTED
)) {
718 result
|= dos_mode_from_sbuf(fsp
->conn
,
724 fsp
->fsp_name
->st
.cached_dos_attributes
= dos_mode_post(result
, fsp
, __func__
);
725 return fsp
->fsp_name
->st
.cached_dos_attributes
;
728 struct dos_mode_at_state
{
729 files_struct
*dir_fsp
;
730 struct smb_filename
*smb_fname
;
734 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req
*subreq
);
736 struct tevent_req
*dos_mode_at_send(TALLOC_CTX
*mem_ctx
,
737 struct tevent_context
*ev
,
738 files_struct
*dir_fsp
,
739 struct smb_filename
*smb_fname
)
741 struct tevent_req
*req
= NULL
;
742 struct dos_mode_at_state
*state
= NULL
;
743 struct tevent_req
*subreq
= NULL
;
745 DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname
));
747 req
= tevent_req_create(mem_ctx
, &state
,
748 struct dos_mode_at_state
);
753 *state
= (struct dos_mode_at_state
) {
755 .smb_fname
= smb_fname
,
758 if (!VALID_STAT(smb_fname
->st
)) {
759 tevent_req_done(req
);
760 return tevent_req_post(req
, ev
);
763 if (smb_fname
->fsp
== NULL
) {
764 if (ISDOTDOT(smb_fname
->base_name
)) {
766 * smb_fname->fsp is explicitly closed
767 * for ".." to prevent meta-data leakage.
769 state
->dosmode
= FILE_ATTRIBUTE_DIRECTORY
;
772 * This is a symlink in POSIX context.
773 * FIXME ? Should we move to returning
774 * FILE_ATTRIBUTE_REPARSE_POINT here ?
776 state
->dosmode
= FILE_ATTRIBUTE_NORMAL
;
778 tevent_req_done(req
);
779 return tevent_req_post(req
, ev
);
782 subreq
= SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state
,
786 if (tevent_req_nomem(subreq
, req
)) {
787 return tevent_req_post(req
, ev
);
789 tevent_req_set_callback(subreq
, dos_mode_at_vfs_get_dosmode_done
, req
);
794 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req
*subreq
)
796 struct tevent_req
*req
=
797 tevent_req_callback_data(subreq
,
799 struct dos_mode_at_state
*state
=
801 struct dos_mode_at_state
);
802 struct vfs_aio_state aio_state
;
807 * Make sure we run as the user again
809 ok
= change_to_user_and_service_by_fsp(state
->dir_fsp
);
812 status
= SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq
,
816 if (!NT_STATUS_IS_OK(status
)) {
818 * Both the sync dos_mode() as well as the async
819 * dos_mode_at_[send|recv] have no real error return, the only
820 * unhandled error is when the stat info in smb_fname is not
821 * valid (cf the checks in dos_mode() and dos_mode_at_send().
823 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
824 * dos_mode_post() which also does the mapping of a last resort
825 * from S_IFMT(st_mode).
827 * Only if we get NT_STATUS_NOT_IMPLEMENTED or
828 * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
829 * fallback to sync processing.
831 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_IMPLEMENTED
) &&
832 !NT_STATUS_EQUAL(status
, NT_STATUS_NOT_SUPPORTED
))
835 * state->dosmode should still be 0, but reset
839 status
= NT_STATUS_OK
;
842 if (NT_STATUS_IS_OK(status
)) {
843 state
->dosmode
= dos_mode_post(state
->dosmode
,
844 state
->smb_fname
->fsp
,
846 tevent_req_done(req
);
851 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
854 state
->dosmode
= fdos_mode(state
->smb_fname
->fsp
);
855 tevent_req_done(req
);
859 NTSTATUS
dos_mode_at_recv(struct tevent_req
*req
, uint32_t *dosmode
)
861 struct dos_mode_at_state
*state
=
863 struct dos_mode_at_state
);
866 if (tevent_req_is_nterror(req
, &status
)) {
867 tevent_req_received(req
);
871 *dosmode
= state
->dosmode
;
872 tevent_req_received(req
);
876 /*******************************************************************
877 chmod a file - but preserve some bits.
878 If "store dos attributes" is also set it will store the create time
879 from the stat struct in smb_fname (in NTTIME format) in the EA
881 ********************************************************************/
883 int file_set_dosmode(connection_struct
*conn
,
884 struct smb_filename
*smb_fname
,
886 struct smb_filename
*parent_dir
,
895 if (!CAN_WRITE(conn
)) {
900 if (S_ISLNK(smb_fname
->st
.st_ex_mode
)) {
901 /* A symlink in POSIX context, ignore */
905 if ((S_ISDIR(smb_fname
->st
.st_ex_mode
)) &&
906 (dosmode
& FILE_ATTRIBUTE_TEMPORARY
))
912 dosmode
&= SAMBA_ATTRIBUTES_MASK
;
914 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
915 dosmode
, smb_fname_str_dbg(smb_fname
)));
917 if (smb_fname
->fsp
== NULL
) {
922 if ((smb_fname
->fsp
->posix_flags
& FSP_POSIX_FLAGS_OPEN
) &&
923 !lp_store_dos_attributes(SNUM(conn
)))
928 unixmode
= smb_fname
->st
.st_ex_mode
;
930 get_acl_group_bits(conn
, smb_fname
->fsp
, &smb_fname
->st
.st_ex_mode
);
932 if (S_ISDIR(smb_fname
->st
.st_ex_mode
))
933 dosmode
|= FILE_ATTRIBUTE_DIRECTORY
;
935 dosmode
&= ~FILE_ATTRIBUTE_DIRECTORY
;
937 /* Store the DOS attributes in an EA by preference. */
938 status
= SMB_VFS_FSET_DOS_ATTRIBUTES(conn
,
939 metadata_fsp(smb_fname
->fsp
),
941 if (NT_STATUS_IS_OK(status
)) {
942 smb_fname
->st
.cached_dos_attributes
= dosmode
;
948 * Only fall back to using UNIX modes if
949 * we get NOT_IMPLEMENTED.
951 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_IMPLEMENTED
)) {
952 errno
= map_errno_from_nt_status(status
);
956 /* Fall back to UNIX modes. */
957 unixmode
= unix_mode(
961 parent_dir
!= NULL
? parent_dir
->fsp
: NULL
);
963 /* preserve the file type bits */
966 /* preserve the s bits */
967 mask
|= (S_ISUID
| S_ISGID
);
969 /* preserve the t bit */
974 /* possibly preserve the x bits */
975 if (!MAP_ARCHIVE(conn
))
977 if (!MAP_SYSTEM(conn
))
979 if (!MAP_HIDDEN(conn
))
982 unixmode
|= (smb_fname
->st
.st_ex_mode
& mask
);
984 /* if we previously had any r bits set then leave them alone */
985 if ((tmp
= smb_fname
->st
.st_ex_mode
& (S_IRUSR
|S_IRGRP
|S_IROTH
))) {
986 unixmode
&= ~(S_IRUSR
|S_IRGRP
|S_IROTH
);
990 /* if we previously had any w bits set then leave them alone
991 whilst adding in the new w bits, if the new mode is not rdonly */
992 if (!(dosmode
& FILE_ATTRIBUTE_READONLY
)) {
993 unixmode
|= (smb_fname
->st
.st_ex_mode
& (S_IWUSR
|S_IWGRP
|S_IWOTH
));
997 * From the chmod 2 man page:
999 * "If the calling process is not privileged, and the group of the file
1000 * does not match the effective group ID of the process or one of its
1001 * supplementary group IDs, the S_ISGID bit will be turned off, but
1002 * this will not cause an error to be returned."
1004 * Simply refuse to do the chmod in this case.
1007 if (S_ISDIR(smb_fname
->st
.st_ex_mode
) &&
1008 (unixmode
& S_ISGID
) &&
1009 geteuid() != sec_initial_uid() &&
1010 !current_user_in_group(conn
, smb_fname
->st
.st_ex_gid
))
1012 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1013 "set for directory %s\n",
1014 smb_fname_str_dbg(smb_fname
)));
1019 ret
= SMB_VFS_FCHMOD(smb_fname
->fsp
, unixmode
);
1024 if((errno
!= EPERM
) && (errno
!= EACCES
))
1027 if(!lp_dos_filemode(SNUM(conn
)))
1030 /* We want DOS semantics, ie allow non owner with write permission to change the
1031 bits on a file. Just like file_ntimes below.
1034 if (!can_write_to_fsp(smb_fname
->fsp
))
1041 ret
= SMB_VFS_FCHMOD(smb_fname
->fsp
, unixmode
);
1046 notify_fname(conn
, NOTIFY_ACTION_MODIFIED
,
1047 FILE_NOTIFY_CHANGE_ATTRIBUTES
,
1048 smb_fname
->base_name
);
1051 smb_fname
->st
.st_ex_mode
= unixmode
;
1058 NTSTATUS
file_set_sparse(connection_struct
*conn
,
1062 const struct loadparm_substitution
*lp_sub
=
1063 loadparm_s3_global_substitution();
1064 uint32_t old_dosmode
;
1065 uint32_t new_dosmode
;
1068 if (!CAN_WRITE(conn
)) {
1069 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1070 "on readonly share[%s]\n",
1071 smb_fname_str_dbg(fsp
->fsp_name
),
1073 lp_servicename(talloc_tos(), lp_sub
, SNUM(conn
))));
1074 return NT_STATUS_MEDIA_WRITE_PROTECTED
;
1078 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1079 * following access flags are granted.
1081 status
= check_any_access_fsp(fsp
,
1083 | FILE_WRITE_ATTRIBUTES
1084 | SEC_FILE_APPEND_DATA
);
1085 if (!NT_STATUS_IS_OK(status
)) {
1086 DBG_DEBUG("fname[%s] set[%u] "
1087 "access_mask[0x%08X] - access denied\n",
1088 smb_fname_str_dbg(fsp
->fsp_name
),
1094 if (fsp
->fsp_flags
.is_directory
) {
1095 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1096 (sparse
? "set" : "clear"),
1097 smb_fname_str_dbg(fsp
->fsp_name
)));
1098 return NT_STATUS_INVALID_PARAMETER
;
1101 if (IS_IPC(conn
) || IS_PRINT(conn
)) {
1102 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1103 (sparse
? "set" : "clear")));
1104 return NT_STATUS_INVALID_PARAMETER
;
1107 if (fsp_is_alternate_stream(fsp
)) {
1109 * MS-FSA 2.1.1.5 IsSparse
1111 * This is a per stream attribute, but our backends don't
1112 * support it a consistent way, therefore just pretend
1113 * success and ignore the request.
1115 DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
1116 "[%s]\n", fsp_str_dbg(fsp
));
1117 return NT_STATUS_OK
;
1120 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1121 sparse
, smb_fname_str_dbg(fsp
->fsp_name
)));
1123 if (!lp_store_dos_attributes(SNUM(conn
))) {
1124 return NT_STATUS_INVALID_DEVICE_REQUEST
;
1127 status
= vfs_stat_fsp(fsp
);
1128 if (!NT_STATUS_IS_OK(status
)) {
1132 old_dosmode
= fdos_mode(fsp
);
1134 if (sparse
&& !(old_dosmode
& FILE_ATTRIBUTE_SPARSE
)) {
1135 new_dosmode
= old_dosmode
| FILE_ATTRIBUTE_SPARSE
;
1136 } else if (!sparse
&& (old_dosmode
& FILE_ATTRIBUTE_SPARSE
)) {
1137 new_dosmode
= old_dosmode
& ~FILE_ATTRIBUTE_SPARSE
;
1139 return NT_STATUS_OK
;
1142 /* Store the DOS attributes in an EA. */
1143 status
= SMB_VFS_FSET_DOS_ATTRIBUTES(conn
, fsp
, new_dosmode
);
1144 if (!NT_STATUS_IS_OK(status
)) {
1148 notify_fname(conn
, NOTIFY_ACTION_MODIFIED
,
1149 FILE_NOTIFY_CHANGE_ATTRIBUTES
,
1150 fsp
->fsp_name
->base_name
);
1152 fsp
->fsp_name
->st
.cached_dos_attributes
= new_dosmode
;
1153 fsp
->fsp_flags
.is_sparse
= sparse
;
1155 return NT_STATUS_OK
;
1158 /*******************************************************************
1159 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1161 *******************************************************************/
1163 int file_ntimes(connection_struct
*conn
,
1165 struct smb_file_time
*ft
)
1171 DBG_INFO("actime: %s",
1172 time_to_asc(convert_timespec_to_time_t(ft
->atime
)));
1173 DBG_INFO("modtime: %s",
1174 time_to_asc(convert_timespec_to_time_t(ft
->mtime
)));
1175 DBG_INFO("ctime: %s",
1176 time_to_asc(convert_timespec_to_time_t(ft
->ctime
)));
1177 DBG_INFO("createtime: %s",
1178 time_to_asc(convert_timespec_to_time_t(ft
->create_time
)));
1180 /* Don't update the time on read-only shares */
1181 /* We need this as set_filetime (which can be called on
1182 close and other paths) can end up calling this function
1183 without the NEED_WRITE protection. Found by :
1184 Leo Weppelman <leo@wau.mis.ah.nl>
1187 if (!CAN_WRITE(conn
)) {
1191 if (SMB_VFS_FNTIMES(fsp
, ft
) == 0) {
1196 if((errno
!= EPERM
) && (errno
!= EACCES
)) {
1200 if(!lp_dos_filetimes(SNUM(conn
))) {
1204 /* We have permission (given by the Samba admin) to
1205 break POSIX semantics and allow a user to change
1206 the time on a file they don't own but can write to
1210 /* Check if we have write access. */
1211 if (can_write_to_fsp(fsp
)) {
1212 /* We are allowed to become root and change the filetime. */
1214 ret
= SMB_VFS_FNTIMES(fsp
, ft
);
1220 copy_stat_ex_timestamps(fsp
, ft
);
1226 /******************************************************************
1227 Force a "sticky" write time on a pathname. This will always be
1228 returned on all future write time queries and set on close.
1229 ******************************************************************/
1231 bool set_sticky_write_time_path(struct file_id fileid
, struct timespec mtime
)
1233 if (is_omit_timespec(&mtime
)) {
1237 if (!set_sticky_write_time(fileid
, mtime
)) {
1244 /******************************************************************
1245 Force a "sticky" write time on an fsp. This will always be
1246 returned on all future write time queries and set on close.
1247 ******************************************************************/
1249 bool set_sticky_write_time_fsp(struct files_struct
*fsp
, struct timespec mtime
)
1251 if (is_omit_timespec(&mtime
)) {
1255 fsp
->fsp_flags
.write_time_forced
= true;
1256 TALLOC_FREE(fsp
->update_write_time_event
);
1258 return set_sticky_write_time_path(fsp
->file_id
, mtime
);
1261 /******************************************************************
1262 Set a create time EA.
1263 ******************************************************************/
1265 NTSTATUS
set_create_timespec_ea(struct files_struct
*fsp
,
1266 struct timespec create_time
)
1271 if (!lp_store_dos_attributes(SNUM(fsp
->conn
))) {
1272 return NT_STATUS_OK
;
1275 dosmode
= fdos_mode(fsp
);
1277 fsp
->fsp_name
->st
.st_ex_btime
= create_time
;
1278 ret
= file_set_dosmode(fsp
->conn
, fsp
->fsp_name
, dosmode
, NULL
, false);
1280 return map_nt_error_from_unix(errno
);
1283 DBG_DEBUG("wrote create time EA for file %s\n",
1284 smb_fname_str_dbg(fsp
->fsp_name
));
1286 return NT_STATUS_OK
;
1289 /******************************************************************
1290 Return a create time.
1291 ******************************************************************/
1293 struct timespec
get_create_timespec(connection_struct
*conn
,
1294 struct files_struct
*fsp
,
1295 const struct smb_filename
*smb_fname
)
1298 struct files_struct
*meta_fsp
= metadata_fsp(fsp
);
1299 return meta_fsp
->fsp_name
->st
.st_ex_btime
;
1301 return smb_fname
->st
.st_ex_btime
;
1304 /******************************************************************
1305 Return a change time (may look at EA in future).
1306 ******************************************************************/
1308 struct timespec
get_change_timespec(connection_struct
*conn
,
1309 struct files_struct
*fsp
,
1310 const struct smb_filename
*smb_fname
)
1312 return smb_fname
->st
.st_ex_mtime
;