This can also pull the create time into the stat struct inside smb_fname.
****************************************************************************/
+NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
+ DATA_BLOB blob,
+ uint32_t *pattr)
+{
+ struct xattr_DOSATTRIB dosattrib;
+ enum ndr_err_code ndr_err;
+ uint32_t dosattr;
+
+ ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
+ (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_WARNING("bad ndr decode "
+ "from EA on file %s: Error = %s\n",
+ smb_fname_str_dbg(smb_fname),
+ ndr_errstr(ndr_err));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ DBG_DEBUG("%s attr = %s\n",
+ smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex);
+
+ switch (dosattrib.version) {
+ case 0xFFFF:
+ dosattr = dosattrib.info.compatinfoFFFF.attrib;
+ break;
+ case 1:
+ dosattr = dosattrib.info.info1.attrib;
+ if (!null_nttime(dosattrib.info.info1.create_time)) {
+ struct timespec create_time =
+ nt_time_to_unix_timespec(
+ dosattrib.info.info1.create_time);
+
+ update_stat_ex_create_time(&smb_fname->st,
+ create_time);
+
+ DBG_DEBUG("file %s case 1 set btime %s\n",
+ smb_fname_str_dbg(smb_fname),
+ time_to_asc(convert_timespec_to_time_t(
+ create_time)));
+ }
+ break;
+ case 2:
+ dosattr = dosattrib.info.oldinfo2.attrib;
+ /* Don't know what flags to check for this case. */
+ break;
+ case 3:
+ dosattr = dosattrib.info.info3.attrib;
+ if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
+ !null_nttime(dosattrib.info.info3.create_time)) {
+ struct timespec create_time =
+ nt_time_to_unix_timespec(
+ dosattrib.info.info3.create_time);
+
+ update_stat_ex_create_time(&smb_fname->st,
+ create_time);
+
+ DBG_DEBUG("file %s case 3 set btime %s\n",
+ smb_fname_str_dbg(smb_fname),
+ time_to_asc(convert_timespec_to_time_t(
+ create_time)));
+ }
+ break;
+ default:
+ DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
+ smb_fname_str_dbg(smb_fname), blob.data);
+ /* Should this be INTERNAL_ERROR? */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (S_ISDIR(smb_fname->st.st_ex_mode)) {
+ dosattr |= FILE_ATTRIBUTE_DIRECTORY;
+ }
+
+ /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
+ *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
+
+ dos_mode_debug_print(__func__, *pattr);
+
+ return NT_STATUS_OK;
+}
+
NTSTATUS get_ea_dos_attribute(connection_struct *conn,
struct smb_filename *smb_fname,
uint32_t *pattr)
{
- struct xattr_DOSATTRIB dosattrib;
- enum ndr_err_code ndr_err;
DATA_BLOB blob;
ssize_t sizeret;
fstring attrstr;
- uint32_t dosattr;
+ NTSTATUS status;
if (!lp_store_dos_attributes(SNUM(conn))) {
return NT_STATUS_NOT_IMPLEMENTED;
/* Don't reset pattr to zero as we may already have filename-based attributes we
need to preserve. */
- sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
+ sizeret = SMB_VFS_GETXATTR(conn, smb_fname,
SAMBA_XATTR_DOS_ATTRIB, attrstr,
sizeof(attrstr));
+ if (sizeret == -1 && errno == EACCES) {
+ int saved_errno = 0;
+
+ /*
+ * According to MS-FSA 2.1.5.1.2.1 "Algorithm to Check Access to
+ * an Existing File" FILE_LIST_DIRECTORY on a directory implies
+ * FILE_READ_ATTRIBUTES for directory entries. Being able to
+ * stat() a file implies FILE_LIST_DIRECTORY for the directory
+ * containing the file.
+ */
+
+ if (!VALID_STAT(smb_fname->st)) {
+ /*
+ * Safety net: dos_mode() already checks this, but as we
+ * become root based on this, add an additional layer of
+ * defense.
+ */
+ DBG_ERR("Rejecting root override, invalid stat [%s]\n",
+ smb_fname_str_dbg(smb_fname));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ become_root();
+ sizeret = SMB_VFS_GETXATTR(conn, smb_fname,
+ SAMBA_XATTR_DOS_ATTRIB,
+ attrstr,
+ sizeof(attrstr));
+ if (sizeret == -1) {
+ saved_errno = errno;
+ }
+ unbecome_root();
+
+ if (saved_errno != 0) {
+ errno = saved_errno;
+ }
+ }
if (sizeret == -1) {
DBG_INFO("Cannot get attribute "
"from EA on file %s: Error = %s\n",
blob.data = (uint8_t *)attrstr;
blob.length = sizeret;
- ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
- (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
-
- if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
- DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
- "from EA on file %s: Error = %s\n",
- smb_fname_str_dbg(smb_fname),
- ndr_errstr(ndr_err)));
- return ndr_map_error2ntstatus(ndr_err);
- }
-
- DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
- smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
-
- switch (dosattrib.version) {
- case 0xFFFF:
- dosattr = dosattrib.info.compatinfoFFFF.attrib;
- break;
- case 1:
- dosattr = dosattrib.info.info1.attrib;
- if (!null_nttime(dosattrib.info.info1.create_time)) {
- struct timespec create_time =
- nt_time_to_unix_timespec(
- dosattrib.info.info1.create_time);
-
- update_stat_ex_create_time(&smb_fname->st,
- create_time);
-
- DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
- "set btime %s\n",
- smb_fname_str_dbg(smb_fname),
- time_to_asc(convert_timespec_to_time_t(
- create_time)) ));
- }
- break;
- case 2:
- dosattr = dosattrib.info.oldinfo2.attrib;
- /* Don't know what flags to check for this case. */
- break;
- case 3:
- dosattr = dosattrib.info.info3.attrib;
- if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
- !null_nttime(dosattrib.info.info3.create_time)) {
- struct timespec create_time =
- nt_time_to_unix_timespec(
- dosattrib.info.info3.create_time);
-
- update_stat_ex_create_time(&smb_fname->st,
- create_time);
-
- DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
- "set btime %s\n",
- smb_fname_str_dbg(smb_fname),
- time_to_asc(convert_timespec_to_time_t(
- create_time)) ));
- }
- break;
- default:
- DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
- "file %s - %s\n", smb_fname_str_dbg(smb_fname),
- attrstr));
- /* Should this be INTERNAL_ERROR? */
- return NT_STATUS_INVALID_PARAMETER;
+ status = parse_dos_attribute_blob(smb_fname, blob, pattr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
- if (S_ISDIR(smb_fname->st.st_ex_mode)) {
- dosattr |= FILE_ATTRIBUTE_DIRECTORY;
- }
- /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
- *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
-
- dos_mode_debug_print(__func__, *pattr);
-
return NT_STATUS_OK;
}
struct xattr_DOSATTRIB dosattrib;
enum ndr_err_code ndr_err;
DATA_BLOB blob;
+ int ret;
if (!lp_store_dos_attributes(SNUM(conn))) {
return NT_STATUS_NOT_IMPLEMENTED;
return NT_STATUS_INVALID_PARAMETER;
}
- if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
- SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
- 0) == -1) {
+ ret = SMB_VFS_SETXATTR(conn, smb_fname,
+ SAMBA_XATTR_DOS_ATTRIB,
+ blob.data, blob.length, 0);
+ if (ret != 0) {
NTSTATUS status = NT_STATUS_OK;
bool need_close = false;
files_struct *fsp = NULL;
+ bool set_dosmode_ok = false;
- if((errno != EPERM) && (errno != EACCES)) {
+ if ((errno != EPERM) && (errno != EACCES)) {
DBG_INFO("Cannot set "
"attribute EA on file %s: Error = %s\n",
smb_fname_str_dbg(smb_fname), strerror(errno));
*/
/* Check if we have write access. */
- if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
+ if (!CAN_WRITE(conn)) {
return NT_STATUS_ACCESS_DENIED;
+ }
- if (!can_write_to_file(conn, smb_fname)) {
+ status = smbd_check_access_rights(conn, smb_fname, false,
+ FILE_WRITE_ATTRIBUTES);
+ if (NT_STATUS_IS_OK(status)) {
+ set_dosmode_ok = true;
+ }
+
+ if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
+ set_dosmode_ok = can_write_to_file(conn, smb_fname);
+ }
+
+ if (!set_dosmode_ok) {
return NT_STATUS_ACCESS_DENIED;
}
}
become_root();
- if (SMB_VFS_FSETXATTR(fsp,
- SAMBA_XATTR_DOS_ATTRIB, blob.data,
- blob.length, 0) == 0) {
+ ret = SMB_VFS_FSETXATTR(fsp,
+ SAMBA_XATTR_DOS_ATTRIB,
+ blob.data, blob.length, 0);
+ if (ret == 0) {
status = NT_STATUS_OK;
}
unbecome_root();
/* Get the DOS attributes via the VFS if we can */
status = SMB_VFS_GET_DOS_ATTRIBUTES(conn, smb_fname, &result);
if (!NT_STATUS_IS_OK(status)) {
- result |= dos_mode_from_sbuf(conn, smb_fname);
+ /*
+ * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
+ */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+ result |= dos_mode_from_sbuf(conn, smb_fname);
+ }
+ }
+
+ /*
+ * According to MS-FSA a stream name does not have
+ * separate DOS attribute metadata, so we must return
+ * the DOS attribute from the base filename. With one caveat,
+ * a non-default stream name can never be a directory.
+ *
+ * As this is common to all streams data stores, we handle
+ * it here instead of inside all stream VFS modules.
+ *
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
+ */
+
+ if (is_ntfs_stream_smb_fname(smb_fname)) {
+ /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
+ if (!is_ntfs_default_stream_smb_fname(smb_fname)) {
+ /*
+ * Non-default stream name, not a posix path.
+ */
+ result &= ~(FILE_ATTRIBUTE_DIRECTORY);
+ }
}
if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
NULL, /* req */
0, /* root_dir_fid */
smb_fname_cp, /* fname */
- FILE_WRITE_DATA, /* access_mask */
+ FILE_WRITE_ATTRIBUTES, /* access_mask */
(FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
FILE_SHARE_DELETE),
FILE_OPEN, /* create_disposition*/