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/>.
22 #include "system/filesys.h"
23 #include "librpc/gen_ndr/ndr_xattr.h"
24 #include "librpc/gen_ndr/ioctl.h"
25 #include "../libcli/security/security.h"
26 #include "smbd/smbd.h"
27 #include "lib/param/loadparm.h"
29 static NTSTATUS
get_file_handle_for_metadata(connection_struct
*conn
,
30 struct smb_filename
*smb_fname
,
31 files_struct
**ret_fsp
,
34 static void dos_mode_debug_print(const char *func
, uint32_t mode
)
38 if (DEBUGLEVEL
< DBGLVL_INFO
) {
44 if (mode
& FILE_ATTRIBUTE_HIDDEN
) {
45 fstrcat(modestr
, "h");
47 if (mode
& FILE_ATTRIBUTE_READONLY
) {
48 fstrcat(modestr
, "r");
50 if (mode
& FILE_ATTRIBUTE_SYSTEM
) {
51 fstrcat(modestr
, "s");
53 if (mode
& FILE_ATTRIBUTE_DIRECTORY
) {
54 fstrcat(modestr
, "d");
56 if (mode
& FILE_ATTRIBUTE_ARCHIVE
) {
57 fstrcat(modestr
, "a");
59 if (mode
& FILE_ATTRIBUTE_SPARSE
) {
60 fstrcat(modestr
, "[sparse]");
62 if (mode
& FILE_ATTRIBUTE_OFFLINE
) {
63 fstrcat(modestr
, "[offline]");
65 if (mode
& FILE_ATTRIBUTE_COMPRESSED
) {
66 fstrcat(modestr
, "[compressed]");
69 DBG_INFO("%s returning (0x%x): \"%s\"\n", func
, (unsigned)mode
,
73 static uint32_t filter_mode_by_protocol(uint32_t mode
)
75 if (get_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 static int set_link_read_only_flag(const SMB_STRUCT_STAT
*const sbuf
)
89 if (S_ISLNK(sbuf
->st_mode
) && S_ISDIR(sbuf
->st_mode
))
90 return FILE_ATTRIBUTE_READONLY
;
96 /****************************************************************************
97 Change a dos mode to a unix mode.
98 Base permission for files:
99 if creating file and inheriting (i.e. parent_dir != NULL)
100 apply read/write bits from parent directory.
102 everybody gets read bit set
103 dos readonly is represented in unix by removing everyone's write bit
104 dos archive is represented in unix by the user's execute bit
105 dos system is represented in unix by the group's execute bit
106 dos hidden is represented in unix by the other's execute bit
108 Then apply create mask,
111 Base permission for directories:
112 dos directory is represented in unix by unix's dir bit and the exec bit
114 Then apply create mask,
117 ****************************************************************************/
119 mode_t
unix_mode(connection_struct
*conn
, int dosmode
,
120 const struct smb_filename
*smb_fname
,
121 const char *inherit_from_dir
)
123 mode_t result
= (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IWGRP
| S_IWOTH
);
124 mode_t dir_mode
= 0; /* Mode of the inherit_from directory if
127 if (!lp_store_dos_attributes(SNUM(conn
)) && IS_DOS_READONLY(dosmode
)) {
128 result
&= ~(S_IWUSR
| S_IWGRP
| S_IWOTH
);
131 if ((inherit_from_dir
!= NULL
) && lp_inherit_permissions(SNUM(conn
))) {
132 struct smb_filename
*smb_fname_parent
;
134 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
135 smb_fname_str_dbg(smb_fname
),
138 smb_fname_parent
= synthetic_smb_fname(
139 talloc_tos(), inherit_from_dir
, NULL
, NULL
);
140 if (smb_fname_parent
== NULL
) {
141 DEBUG(1,("unix_mode(%s) failed, [dir %s]: No memory\n",
142 smb_fname_str_dbg(smb_fname
),
147 if (SMB_VFS_STAT(conn
, smb_fname_parent
) != 0) {
148 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
149 smb_fname_str_dbg(smb_fname
),
150 inherit_from_dir
, strerror(errno
)));
151 TALLOC_FREE(smb_fname_parent
);
152 return(0); /* *** shouldn't happen! *** */
155 /* Save for later - but explicitly remove setuid bit for safety. */
156 dir_mode
= smb_fname_parent
->st
.st_ex_mode
& ~S_ISUID
;
157 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
158 smb_fname_str_dbg(smb_fname
), (int)dir_mode
));
161 TALLOC_FREE(smb_fname_parent
);
164 if (IS_DOS_DIR(dosmode
)) {
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
);
176 /* Apply directory mask */
177 result
&= lp_directory_mask(SNUM(conn
));
178 /* Add in force bits */
179 result
|= lp_force_directory_mode(SNUM(conn
));
182 if (lp_map_archive(SNUM(conn
)) && IS_DOS_ARCHIVE(dosmode
))
185 if (lp_map_system(SNUM(conn
)) && IS_DOS_SYSTEM(dosmode
))
188 if (lp_map_hidden(SNUM(conn
)) && IS_DOS_HIDDEN(dosmode
))
192 /* Inherit 666 component of parent directory mode */
193 result
|= dir_mode
& (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IWGRP
| S_IWOTH
);
195 /* Apply mode mask */
196 result
&= lp_create_mask(SNUM(conn
));
197 /* Add in force bits */
198 result
|= lp_force_create_mode(SNUM(conn
));
202 DBG_INFO("unix_mode(%s) returning 0%o\n",
203 smb_fname_str_dbg(smb_fname
), (int)result
);
208 /****************************************************************************
209 Change a unix mode to a dos mode.
210 ****************************************************************************/
212 static uint32_t dos_mode_from_sbuf(connection_struct
*conn
,
213 const struct smb_filename
*smb_fname
)
216 enum mapreadonly_options ro_opts
= (enum mapreadonly_options
)lp_map_readonly(SNUM(conn
));
218 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
219 /* if we can find out if a file is immutable we should report it r/o */
220 if (smb_fname
->st
.st_ex_flags
& (UF_IMMUTABLE
| SF_IMMUTABLE
)) {
221 result
|= FILE_ATTRIBUTE_READONLY
;
224 if (ro_opts
== MAP_READONLY_YES
) {
225 /* Original Samba method - map inverse of user "w" bit. */
226 if ((smb_fname
->st
.st_ex_mode
& S_IWUSR
) == 0) {
227 result
|= FILE_ATTRIBUTE_READONLY
;
229 } else if (ro_opts
== MAP_READONLY_PERMISSIONS
) {
230 /* Check actual permissions for read-only. */
231 if (!can_write_to_file(conn
, smb_fname
)) {
232 result
|= FILE_ATTRIBUTE_READONLY
;
234 } /* Else never set the readonly bit. */
236 if (MAP_ARCHIVE(conn
) && ((smb_fname
->st
.st_ex_mode
& S_IXUSR
) != 0))
237 result
|= FILE_ATTRIBUTE_ARCHIVE
;
239 if (MAP_SYSTEM(conn
) && ((smb_fname
->st
.st_ex_mode
& S_IXGRP
) != 0))
240 result
|= FILE_ATTRIBUTE_SYSTEM
;
242 if (MAP_HIDDEN(conn
) && ((smb_fname
->st
.st_ex_mode
& S_IXOTH
) != 0))
243 result
|= FILE_ATTRIBUTE_HIDDEN
;
245 if (S_ISDIR(smb_fname
->st
.st_ex_mode
))
246 result
= FILE_ATTRIBUTE_DIRECTORY
| (result
& FILE_ATTRIBUTE_READONLY
);
248 result
|= set_link_read_only_flag(&smb_fname
->st
);
250 dos_mode_debug_print(__func__
, result
);
255 /****************************************************************************
256 Get DOS attributes from an EA.
257 This can also pull the create time into the stat struct inside smb_fname.
258 ****************************************************************************/
260 static bool get_ea_dos_attribute(connection_struct
*conn
,
261 struct smb_filename
*smb_fname
,
264 struct xattr_DOSATTRIB dosattrib
;
265 enum ndr_err_code ndr_err
;
271 if (!lp_store_dos_attributes(SNUM(conn
))) {
275 /* Don't reset pattr to zero as we may already have filename-based attributes we
278 sizeret
= SMB_VFS_GETXATTR(conn
, smb_fname
->base_name
,
279 SAMBA_XATTR_DOS_ATTRIB
, attrstr
,
284 || errno
== ENOTSUP
) {
288 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
289 "from EA on file %s: Error = %s\n",
290 smb_fname_str_dbg(smb_fname
),
292 set_store_dos_attributes(SNUM(conn
), False
);
297 blob
.data
= (uint8_t *)attrstr
;
298 blob
.length
= sizeret
;
300 ndr_err
= ndr_pull_struct_blob(&blob
, talloc_tos(), &dosattrib
,
301 (ndr_pull_flags_fn_t
)ndr_pull_xattr_DOSATTRIB
);
303 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
304 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
305 "from EA on file %s: Error = %s\n",
306 smb_fname_str_dbg(smb_fname
),
307 ndr_errstr(ndr_err
)));
311 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
312 smb_fname_str_dbg(smb_fname
), dosattrib
.attrib_hex
));
314 switch (dosattrib
.version
) {
316 dosattr
= dosattrib
.info
.compatinfoFFFF
.attrib
;
319 dosattr
= dosattrib
.info
.info1
.attrib
;
320 if (!null_nttime(dosattrib
.info
.info1
.create_time
)) {
321 struct timespec create_time
=
322 nt_time_to_unix_timespec(
323 dosattrib
.info
.info1
.create_time
);
325 update_stat_ex_create_time(&smb_fname
->st
,
328 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
330 smb_fname_str_dbg(smb_fname
),
331 time_to_asc(convert_timespec_to_time_t(
336 dosattr
= dosattrib
.info
.oldinfo2
.attrib
;
337 /* Don't know what flags to check for this case. */
340 dosattr
= dosattrib
.info
.info3
.attrib
;
341 if ((dosattrib
.info
.info3
.valid_flags
& XATTR_DOSINFO_CREATE_TIME
) &&
342 !null_nttime(dosattrib
.info
.info3
.create_time
)) {
343 struct timespec create_time
=
344 nt_time_to_unix_timespec(
345 dosattrib
.info
.info3
.create_time
);
347 update_stat_ex_create_time(&smb_fname
->st
,
350 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
352 smb_fname_str_dbg(smb_fname
),
353 time_to_asc(convert_timespec_to_time_t(
358 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
359 "file %s - %s\n", smb_fname_str_dbg(smb_fname
),
364 if (S_ISDIR(smb_fname
->st
.st_ex_mode
)) {
365 dosattr
|= FILE_ATTRIBUTE_DIRECTORY
;
367 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
368 *pattr
|= (uint32_t)(dosattr
& (SAMBA_ATTRIBUTES_MASK
|FILE_ATTRIBUTE_SPARSE
));
370 dos_mode_debug_print(__func__
, *pattr
);
375 /****************************************************************************
376 Set DOS attributes in an EA.
377 Also sets the create time.
378 ****************************************************************************/
380 static bool set_ea_dos_attribute(connection_struct
*conn
,
381 struct smb_filename
*smb_fname
,
384 struct xattr_DOSATTRIB dosattrib
;
385 enum ndr_err_code ndr_err
;
388 ZERO_STRUCT(dosattrib
);
391 dosattrib
.version
= 3;
392 dosattrib
.info
.info3
.valid_flags
= XATTR_DOSINFO_ATTRIB
|
393 XATTR_DOSINFO_CREATE_TIME
;
394 dosattrib
.info
.info3
.attrib
= dosmode
;
395 dosattrib
.info
.info3
.create_time
= unix_timespec_to_nt_time(
396 smb_fname
->st
.st_ex_btime
);
398 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
399 (unsigned int)dosmode
,
400 time_to_asc(convert_timespec_to_time_t(smb_fname
->st
.st_ex_btime
)),
401 smb_fname_str_dbg(smb_fname
) ));
403 ndr_err
= ndr_push_struct_blob(
404 &blob
, talloc_tos(), &dosattrib
,
405 (ndr_push_flags_fn_t
)ndr_push_xattr_DOSATTRIB
);
407 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
408 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
409 ndr_errstr(ndr_err
)));
413 if (blob
.data
== NULL
|| blob
.length
== 0) {
417 if (SMB_VFS_SETXATTR(conn
, smb_fname
->base_name
,
418 SAMBA_XATTR_DOS_ATTRIB
, blob
.data
, blob
.length
,
421 bool need_close
= false;
422 files_struct
*fsp
= NULL
;
424 if((errno
!= EPERM
) && (errno
!= EACCES
)) {
427 || errno
== ENOTSUP
) {
431 DEBUG(1,("set_ea_dos_attributes: Cannot set "
432 "attribute EA on file %s: Error = %s\n",
433 smb_fname_str_dbg(smb_fname
),
435 set_store_dos_attributes(SNUM(conn
), False
);
440 /* We want DOS semantics, ie allow non owner with write permission to change the
441 bits on a file. Just like file_ntimes below.
444 /* Check if we have write access. */
445 if(!CAN_WRITE(conn
) || !lp_dos_filemode(SNUM(conn
)))
448 if (!can_write_to_file(conn
, smb_fname
)) {
453 * We need to get an open file handle to do the
454 * metadata operation under root.
457 if (!NT_STATUS_IS_OK(get_file_handle_for_metadata(conn
,
465 if (SMB_VFS_FSETXATTR(fsp
,
466 SAMBA_XATTR_DOS_ATTRIB
, blob
.data
,
467 blob
.length
, 0) == 0) {
472 close_file(NULL
, fsp
, NORMAL_CLOSE
);
476 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
477 (unsigned int)dosmode
,
478 smb_fname_str_dbg(smb_fname
)));
482 /****************************************************************************
483 Change a unix mode to a dos mode for an ms dfs link.
484 ****************************************************************************/
486 uint32_t dos_mode_msdfs(connection_struct
*conn
,
487 const struct smb_filename
*smb_fname
)
491 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname
)));
493 if (!VALID_STAT(smb_fname
->st
)) {
497 /* First do any modifications that depend on the path name. */
498 /* hide files with a name starting with a . */
499 if (lp_hide_dot_files(SNUM(conn
))) {
500 const char *p
= strrchr_m(smb_fname
->base_name
, '/');
504 p
= smb_fname
->base_name
;
507 /* Only . and .. are not hidden. */
508 if (p
[0] == '.' && !((p
[1] == '\0') ||
509 (p
[1] == '.' && p
[2] == '\0'))) {
510 result
|= FILE_ATTRIBUTE_HIDDEN
;
514 result
|= dos_mode_from_sbuf(conn
, smb_fname
);
516 /* Optimization : Only call is_hidden_path if it's not already
518 if (!(result
& FILE_ATTRIBUTE_HIDDEN
) &&
519 IS_HIDDEN_PATH(conn
, smb_fname
->base_name
)) {
520 result
|= FILE_ATTRIBUTE_HIDDEN
;
524 result
= FILE_ATTRIBUTE_NORMAL
;
527 result
= filter_mode_by_protocol(result
);
530 * Add in that it is a reparse point
532 result
|= FILE_ATTRIBUTE_REPARSE_POINT
;
534 dos_mode_debug_print(__func__
, result
);
540 * check whether a file or directory is flagged as compressed.
542 static NTSTATUS
dos_mode_check_compressed(connection_struct
*conn
,
543 struct smb_filename
*smb_fname
,
547 uint16_t compression_fmt
;
548 TALLOC_CTX
*tmp_ctx
= talloc_new(NULL
);
549 if (tmp_ctx
== NULL
) {
550 status
= NT_STATUS_NO_MEMORY
;
554 status
= SMB_VFS_GET_COMPRESSION(conn
, tmp_ctx
, NULL
, smb_fname
,
556 if (!NT_STATUS_IS_OK(status
)) {
560 if (compression_fmt
== COMPRESSION_FORMAT_LZNT1
) {
561 *is_compressed
= true;
563 *is_compressed
= false;
565 status
= NT_STATUS_OK
;
568 talloc_free(tmp_ctx
);
573 /****************************************************************************
574 Change a unix mode to a dos mode.
575 May also read the create timespec into the stat struct in smb_fname
576 if "store dos attributes" is true.
577 ****************************************************************************/
579 uint32_t dos_mode(connection_struct
*conn
, struct smb_filename
*smb_fname
)
584 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname
)));
586 if (!VALID_STAT(smb_fname
->st
)) {
590 /* First do any modifications that depend on the path name. */
591 /* hide files with a name starting with a . */
592 if (lp_hide_dot_files(SNUM(conn
))) {
593 const char *p
= strrchr_m(smb_fname
->base_name
,'/');
597 p
= smb_fname
->base_name
;
600 /* Only . and .. are not hidden. */
601 if (p
[0] == '.' && !((p
[1] == '\0') ||
602 (p
[1] == '.' && p
[2] == '\0'))) {
603 result
|= FILE_ATTRIBUTE_HIDDEN
;
607 /* Get the DOS attributes from an EA by preference. */
608 if (!get_ea_dos_attribute(conn
, smb_fname
, &result
)) {
609 result
|= dos_mode_from_sbuf(conn
, smb_fname
);
612 offline
= SMB_VFS_IS_OFFLINE(conn
, smb_fname
, &smb_fname
->st
);
613 if (S_ISREG(smb_fname
->st
.st_ex_mode
) && offline
) {
614 result
|= FILE_ATTRIBUTE_OFFLINE
;
617 if (conn
->fs_capabilities
& FILE_FILE_COMPRESSION
) {
618 bool compressed
= false;
619 NTSTATUS status
= dos_mode_check_compressed(conn
, smb_fname
,
621 if (NT_STATUS_IS_OK(status
) && compressed
) {
622 result
|= FILE_ATTRIBUTE_COMPRESSED
;
626 /* Optimization : Only call is_hidden_path if it's not already
628 if (!(result
& FILE_ATTRIBUTE_HIDDEN
) &&
629 IS_HIDDEN_PATH(conn
, smb_fname
->base_name
)) {
630 result
|= FILE_ATTRIBUTE_HIDDEN
;
634 result
= FILE_ATTRIBUTE_NORMAL
;
637 result
= filter_mode_by_protocol(result
);
639 dos_mode_debug_print(__func__
, result
);
644 /*******************************************************************
645 chmod a file - but preserve some bits.
646 If "store dos attributes" is also set it will store the create time
647 from the stat struct in smb_fname (in NTTIME format) in the EA
649 ********************************************************************/
651 int file_set_dosmode(connection_struct
*conn
, struct smb_filename
*smb_fname
,
652 uint32_t dosmode
, const char *parent_dir
, bool newfile
)
657 int ret
= -1, lret
= -1;
659 struct timespec new_create_timespec
;
660 files_struct
*fsp
= NULL
;
661 bool need_close
= false;
664 if (!CAN_WRITE(conn
)) {
669 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
670 dosmode
&= (SAMBA_ATTRIBUTES_MASK
| FILE_ATTRIBUTE_OFFLINE
);
672 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
673 dosmode
, smb_fname_str_dbg(smb_fname
)));
675 unixmode
= smb_fname
->st
.st_ex_mode
;
677 get_acl_group_bits(conn
, smb_fname
->base_name
,
678 &smb_fname
->st
.st_ex_mode
);
680 if (S_ISDIR(smb_fname
->st
.st_ex_mode
))
681 dosmode
|= FILE_ATTRIBUTE_DIRECTORY
;
683 dosmode
&= ~FILE_ATTRIBUTE_DIRECTORY
;
685 new_create_timespec
= smb_fname
->st
.st_ex_btime
;
687 old_mode
= dos_mode(conn
, smb_fname
);
689 if ((dosmode
& FILE_ATTRIBUTE_OFFLINE
) &&
690 !(old_mode
& FILE_ATTRIBUTE_OFFLINE
)) {
691 lret
= SMB_VFS_SET_OFFLINE(conn
, smb_fname
);
693 if (errno
== ENOTSUP
) {
694 DEBUG(10, ("Setting FILE_ATTRIBUTE_OFFLINE for "
695 "%s/%s is not supported.\n",
697 smb_fname_str_dbg(smb_fname
)));
699 DEBUG(0, ("An error occurred while setting "
700 "FILE_ATTRIBUTE_OFFLINE for "
701 "%s/%s: %s", parent_dir
,
702 smb_fname_str_dbg(smb_fname
),
708 dosmode
&= ~FILE_ATTRIBUTE_OFFLINE
;
709 old_mode
&= ~FILE_ATTRIBUTE_OFFLINE
;
711 smb_fname
->st
.st_ex_btime
= new_create_timespec
;
713 /* Store the DOS attributes in an EA by preference. */
714 if (lp_store_dos_attributes(SNUM(conn
))) {
716 * Don't fall back to using UNIX modes. Finally
717 * follow the smb.conf manpage.
719 if (!set_ea_dos_attribute(conn
, smb_fname
, dosmode
)) {
723 notify_fname(conn
, NOTIFY_ACTION_MODIFIED
,
724 FILE_NOTIFY_CHANGE_ATTRIBUTES
,
725 smb_fname
->base_name
);
727 smb_fname
->st
.st_ex_mode
= unixmode
;
731 unixmode
= unix_mode(conn
, dosmode
, smb_fname
, parent_dir
);
733 /* preserve the file type bits */
736 /* preserve the s bits */
737 mask
|= (S_ISUID
| S_ISGID
);
739 /* preserve the t bit */
744 /* possibly preserve the x bits */
745 if (!MAP_ARCHIVE(conn
))
747 if (!MAP_SYSTEM(conn
))
749 if (!MAP_HIDDEN(conn
))
752 unixmode
|= (smb_fname
->st
.st_ex_mode
& mask
);
754 /* if we previously had any r bits set then leave them alone */
755 if ((tmp
= smb_fname
->st
.st_ex_mode
& (S_IRUSR
|S_IRGRP
|S_IROTH
))) {
756 unixmode
&= ~(S_IRUSR
|S_IRGRP
|S_IROTH
);
760 /* if we previously had any w bits set then leave them alone
761 whilst adding in the new w bits, if the new mode is not rdonly */
762 if (!IS_DOS_READONLY(dosmode
)) {
763 unixmode
|= (smb_fname
->st
.st_ex_mode
& (S_IWUSR
|S_IWGRP
|S_IWOTH
));
767 * From the chmod 2 man page:
769 * "If the calling process is not privileged, and the group of the file
770 * does not match the effective group ID of the process or one of its
771 * supplementary group IDs, the S_ISGID bit will be turned off, but
772 * this will not cause an error to be returned."
774 * Simply refuse to do the chmod in this case.
777 if (S_ISDIR(smb_fname
->st
.st_ex_mode
) && (unixmode
& S_ISGID
) &&
778 geteuid() != sec_initial_uid() &&
779 !current_user_in_group(conn
, smb_fname
->st
.st_ex_gid
)) {
780 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
781 "set for directory %s\n",
782 smb_fname_str_dbg(smb_fname
)));
787 ret
= SMB_VFS_CHMOD(conn
, smb_fname
->base_name
, unixmode
);
789 if(!newfile
|| (lret
!= -1)) {
790 notify_fname(conn
, NOTIFY_ACTION_MODIFIED
,
791 FILE_NOTIFY_CHANGE_ATTRIBUTES
,
792 smb_fname
->base_name
);
794 smb_fname
->st
.st_ex_mode
= unixmode
;
798 if((errno
!= EPERM
) && (errno
!= EACCES
))
801 if(!lp_dos_filemode(SNUM(conn
)))
804 /* We want DOS semantics, ie allow non owner with write permission to change the
805 bits on a file. Just like file_ntimes below.
808 if (!can_write_to_file(conn
, smb_fname
)) {
814 * We need to get an open file handle to do the
815 * metadata operation under root.
818 status
= get_file_handle_for_metadata(conn
,
822 if (!NT_STATUS_IS_OK(status
)) {
823 errno
= map_errno_from_nt_status(status
);
828 ret
= SMB_VFS_FCHMOD(fsp
, unixmode
);
831 close_file(NULL
, fsp
, NORMAL_CLOSE
);
834 notify_fname(conn
, NOTIFY_ACTION_MODIFIED
,
835 FILE_NOTIFY_CHANGE_ATTRIBUTES
,
836 smb_fname
->base_name
);
839 smb_fname
->st
.st_ex_mode
= unixmode
;
846 NTSTATUS
file_set_sparse(connection_struct
*conn
,
850 uint32_t old_dosmode
;
851 uint32_t new_dosmode
;
854 if (!CAN_WRITE(conn
)) {
855 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
856 "on readonly share[%s]\n",
857 smb_fname_str_dbg(fsp
->fsp_name
),
859 lp_servicename(talloc_tos(), SNUM(conn
))));
860 return NT_STATUS_MEDIA_WRITE_PROTECTED
;
864 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
865 * following access flags are granted.
867 if ((fsp
->access_mask
& (FILE_WRITE_DATA
868 | FILE_WRITE_ATTRIBUTES
869 | SEC_FILE_APPEND_DATA
)) == 0) {
870 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
871 "access_mask[0x%08X] - access denied\n",
872 smb_fname_str_dbg(fsp
->fsp_name
),
875 return NT_STATUS_ACCESS_DENIED
;
878 if (fsp
->is_directory
) {
879 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
880 (sparse
? "set" : "clear"),
881 smb_fname_str_dbg(fsp
->fsp_name
)));
882 return NT_STATUS_INVALID_PARAMETER
;
885 if (IS_IPC(conn
) || IS_PRINT(conn
)) {
886 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
887 (sparse
? "set" : "clear")));
888 return NT_STATUS_INVALID_PARAMETER
;
891 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
892 sparse
, smb_fname_str_dbg(fsp
->fsp_name
)));
894 if (!lp_store_dos_attributes(SNUM(conn
))) {
895 return NT_STATUS_INVALID_DEVICE_REQUEST
;
898 status
= vfs_stat_fsp(fsp
);
899 if (!NT_STATUS_IS_OK(status
)) {
903 old_dosmode
= dos_mode(conn
, fsp
->fsp_name
);
905 if (sparse
&& !(old_dosmode
& FILE_ATTRIBUTE_SPARSE
)) {
906 new_dosmode
= old_dosmode
| FILE_ATTRIBUTE_SPARSE
;
907 } else if (!sparse
&& (old_dosmode
& FILE_ATTRIBUTE_SPARSE
)) {
908 new_dosmode
= old_dosmode
& ~FILE_ATTRIBUTE_SPARSE
;
913 /* Store the DOS attributes in an EA. */
914 if (!set_ea_dos_attribute(conn
, fsp
->fsp_name
,
919 return map_nt_error_from_unix(errno
);
922 notify_fname(conn
, NOTIFY_ACTION_MODIFIED
,
923 FILE_NOTIFY_CHANGE_ATTRIBUTES
,
924 fsp
->fsp_name
->base_name
);
926 fsp
->is_sparse
= sparse
;
931 /*******************************************************************
932 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
934 *******************************************************************/
936 int file_ntimes(connection_struct
*conn
, const struct smb_filename
*smb_fname
,
937 struct smb_file_time
*ft
)
943 DEBUG(6, ("file_ntime: actime: %s",
944 time_to_asc(convert_timespec_to_time_t(ft
->atime
))));
945 DEBUG(6, ("file_ntime: modtime: %s",
946 time_to_asc(convert_timespec_to_time_t(ft
->mtime
))));
947 DEBUG(6, ("file_ntime: ctime: %s",
948 time_to_asc(convert_timespec_to_time_t(ft
->ctime
))));
949 DEBUG(6, ("file_ntime: createtime: %s",
950 time_to_asc(convert_timespec_to_time_t(ft
->create_time
))));
952 /* Don't update the time on read-only shares */
953 /* We need this as set_filetime (which can be called on
954 close and other paths) can end up calling this function
955 without the NEED_WRITE protection. Found by :
956 Leo Weppelman <leo@wau.mis.ah.nl>
959 if (!CAN_WRITE(conn
)) {
963 if(SMB_VFS_NTIMES(conn
, smb_fname
, ft
) == 0) {
967 if((errno
!= EPERM
) && (errno
!= EACCES
)) {
971 if(!lp_dos_filetimes(SNUM(conn
))) {
975 /* We have permission (given by the Samba admin) to
976 break POSIX semantics and allow a user to change
977 the time on a file they don't own but can write to
981 /* Check if we have write access. */
982 if (can_write_to_file(conn
, smb_fname
)) {
983 /* We are allowed to become root and change the filetime. */
985 ret
= SMB_VFS_NTIMES(conn
, smb_fname
, ft
);
992 /******************************************************************
993 Force a "sticky" write time on a pathname. This will always be
994 returned on all future write time queries and set on close.
995 ******************************************************************/
997 bool set_sticky_write_time_path(struct file_id fileid
, struct timespec mtime
)
999 if (null_timespec(mtime
)) {
1003 if (!set_sticky_write_time(fileid
, mtime
)) {
1010 /******************************************************************
1011 Force a "sticky" write time on an fsp. This will always be
1012 returned on all future write time queries and set on close.
1013 ******************************************************************/
1015 bool set_sticky_write_time_fsp(struct files_struct
*fsp
, struct timespec mtime
)
1017 if (null_timespec(mtime
)) {
1021 fsp
->write_time_forced
= true;
1022 TALLOC_FREE(fsp
->update_write_time_event
);
1024 return set_sticky_write_time_path(fsp
->file_id
, mtime
);
1027 /******************************************************************
1028 Set a create time EA.
1029 ******************************************************************/
1031 NTSTATUS
set_create_timespec_ea(connection_struct
*conn
,
1032 const struct smb_filename
*psmb_fname
,
1033 struct timespec create_time
)
1035 struct smb_filename
*smb_fname
;
1039 if (!lp_store_dos_attributes(SNUM(conn
))) {
1040 return NT_STATUS_OK
;
1043 smb_fname
= synthetic_smb_fname(talloc_tos(), psmb_fname
->base_name
,
1044 NULL
, &psmb_fname
->st
);
1046 if (smb_fname
== NULL
) {
1047 return NT_STATUS_NO_MEMORY
;
1050 dosmode
= dos_mode(conn
, smb_fname
);
1052 smb_fname
->st
.st_ex_btime
= create_time
;
1054 ret
= file_set_dosmode(conn
, smb_fname
, dosmode
, NULL
, false);
1056 map_nt_error_from_unix(errno
);
1059 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1060 smb_fname_str_dbg(smb_fname
)));
1062 return NT_STATUS_OK
;
1065 /******************************************************************
1066 Return a create time.
1067 ******************************************************************/
1069 struct timespec
get_create_timespec(connection_struct
*conn
,
1070 struct files_struct
*fsp
,
1071 const struct smb_filename
*smb_fname
)
1073 return smb_fname
->st
.st_ex_btime
;
1076 /******************************************************************
1077 Return a change time (may look at EA in future).
1078 ******************************************************************/
1080 struct timespec
get_change_timespec(connection_struct
*conn
,
1081 struct files_struct
*fsp
,
1082 const struct smb_filename
*smb_fname
)
1084 return smb_fname
->st
.st_ex_mtime
;
1087 /****************************************************************************
1088 Get a real open file handle we can do meta-data operations on. As it's
1089 going to be used under root access only on meta-data we should look for
1090 any existing open file handle first, and use that in preference (also to
1091 avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1092 ****************************************************************************/
1094 static NTSTATUS
get_file_handle_for_metadata(connection_struct
*conn
,
1095 struct smb_filename
*smb_fname
,
1096 files_struct
**ret_fsp
,
1101 struct file_id file_id
;
1103 *need_close
= false;
1105 if (!VALID_STAT(smb_fname
->st
)) {
1106 return NT_STATUS_INVALID_PARAMETER
;
1109 file_id
= vfs_file_id_from_sbuf(conn
, &smb_fname
->st
);
1111 for(fsp
= file_find_di_first(conn
->sconn
, file_id
);
1113 fsp
= file_find_di_next(fsp
)) {
1114 if (fsp
->fh
->fd
!= -1) {
1116 return NT_STATUS_OK
;
1120 /* Opens an INTERNAL_OPEN_ONLY write handle. */
1121 status
= SMB_VFS_CREATE_FILE(
1124 0, /* root_dir_fid */
1125 smb_fname
, /* fname */
1126 FILE_WRITE_DATA
, /* access_mask */
1127 (FILE_SHARE_READ
| FILE_SHARE_WRITE
| /* share_access */
1129 FILE_OPEN
, /* create_disposition*/
1130 0, /* create_options */
1131 0, /* file_attributes */
1132 INTERNAL_OPEN_ONLY
, /* oplock_request */
1134 0, /* allocation_size */
1135 0, /* private_flags */
1138 ret_fsp
, /* result */
1140 NULL
, NULL
); /* create context */
1142 if (NT_STATUS_IS_OK(status
)) {