diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 08f9dfb1a894402eec24579717f6c48d4d92f695..d40bd77a2392e6857aa1795fb9d07bbd429552bc 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -2215,6 +2215,9 @@ typedef struct { __le32 DeviceCharacteristics; } __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */ +/* minimum includes first three fields, and empty FS Name */ +#define MIN_FS_ATTR_INFO_SIZE 12 + typedef struct { __le32 Attributes; __le32 MaxPathNameComponentLength; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index b96bacce955a550bd9190332a2d532e002e6e691..a53e2053ae9df9699ebfc1e85cf9ed5c69f4197c 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -209,6 +209,31 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) return rsize; } +static void +smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) +{ + int rc; + __le16 srch_path = 0; /* Null - open root of share */ + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + + oparms.tcon = tcon; + oparms.desired_access = FILE_READ_ATTRIBUTES; + oparms.disposition = FILE_OPEN; + oparms.create_options = 0; + oparms.fid = &fid; + oparms.reconnect = false; + + rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL); + if (rc) + return; + + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid); + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + return; +} + static int smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const char *full_path) @@ -873,6 +898,7 @@ struct smb_version_operations smb20_operations = { .logoff = SMB2_logoff, .tree_connect = SMB2_tcon, .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb2_qfs_tcon, .is_path_accessible = smb2_is_path_accessible, .can_echo = smb2_can_echo, .echo = SMB2_echo, @@ -945,6 +971,7 @@ struct smb_version_operations smb21_operations = { .logoff = SMB2_logoff, .tree_connect = SMB2_tcon, .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb2_qfs_tcon, .is_path_accessible = smb2_is_path_accessible, .can_echo = smb2_can_echo, .echo = SMB2_echo, @@ -1018,6 +1045,7 @@ struct smb_version_operations smb30_operations = { .logoff = SMB2_logoff, .tree_connect = SMB2_tcon, .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb2_qfs_tcon, .is_path_accessible = smb2_is_path_accessible, .can_echo = smb2_can_echo, .echo = SMB2_echo, diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index bbafa12e83b20cfa70b70bba917a15fbecc5c288..df12cf8bd979719d8d53bd17d937a0b9dcdd31e4 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2339,7 +2339,7 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0); if (rc) { cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); - goto qinf_exit; + goto qfsinf_exit; } rsp = (struct smb2_query_info_rsp *)iov.iov_base; @@ -2351,7 +2351,45 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, if (!rc) copy_fs_info_to_kstatfs(info, fsdata); -qinf_exit: +qfsinf_exit: + free_rsp_buf(resp_buftype, iov.iov_base); + return rc; +} + +int +SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid) +{ + struct smb2_query_info_rsp *rsp = NULL; + struct kvec iov; + int rc = 0; + int resp_buftype; + struct cifs_ses *ses = tcon->ses; + unsigned int rsp_len, offset; + + rc = build_qfs_info_req(&iov, tcon, SMB_QUERY_FS_ATTRIBUTE_INFO, + sizeof(FILE_SYSTEM_ATTRIBUTE_INFO), + persistent_fid, volatile_fid); + if (rc) + return rc; + + rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); + goto qfsattr_exit; + } + rsp = (struct smb2_query_info_rsp *)iov.iov_base; + + rsp_len = le32_to_cpu(rsp->OutputBufferLength); + offset = le16_to_cpu(rsp->OutputBufferOffset); + rc = validate_buf(offset, rsp_len, &rsp->hdr, MIN_FS_ATTR_INFO_SIZE); + if (!rc) { + memcpy(&tcon->fsAttrInfo, 4 /* RFC1001 len */ + offset + + (char *)&rsp->hdr, min_t(unsigned int, + rsp_len, sizeof(FILE_SYSTEM_ATTRIBUTE_INFO))); + } + +qfsattr_exit: free_rsp_buf(resp_buftype, iov.iov_base); return rc; } diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 3cf22e39420d0f4540696fc5a715d2a1f15a057d..68dc00d5fb126fb3c8ea218724a1a8c56d4aa06e 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -150,6 +150,8 @@ extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_file_id, u64 volatile_file_id, struct kstatfs *FSData); +extern int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id); extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, const __u64 length, const __u64 offset,