forked from Minki/linux
cifs: Add support for reading attributes on SMB2+
SMB1 already has support to read attributes. This adds similar support to SMB2+. With this patch, tools such as 'getfattr' will now work with SMB2+ shares. RH-bz: 1110709 Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com> Signed-off-by: Steve French <smfrench@gmail.com> Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
This commit is contained in:
parent
81a84ad3cb
commit
95907fea4f
@ -426,6 +426,138 @@ smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
move_smb2_ea_to_cifs(char *dst, size_t dst_size,
|
||||||
|
struct smb2_file_full_ea_info *src, size_t src_size,
|
||||||
|
const unsigned char *ea_name)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0;
|
||||||
|
char *name, *value;
|
||||||
|
size_t name_len, value_len, user_name_len;
|
||||||
|
|
||||||
|
while (src_size > 0) {
|
||||||
|
name = &src->ea_data[0];
|
||||||
|
name_len = (size_t)src->ea_name_length;
|
||||||
|
value = &src->ea_data[src->ea_name_length + 1];
|
||||||
|
value_len = (size_t)le16_to_cpu(src->ea_value_length);
|
||||||
|
|
||||||
|
if (name_len == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src_size < 8 + name_len + 1 + value_len) {
|
||||||
|
cifs_dbg(FYI, "EA entry goes beyond length of list\n");
|
||||||
|
rc = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ea_name) {
|
||||||
|
if (ea_name_len == name_len &&
|
||||||
|
memcmp(ea_name, name, name_len) == 0) {
|
||||||
|
rc = value_len;
|
||||||
|
if (dst_size == 0)
|
||||||
|
goto out;
|
||||||
|
if (dst_size < value_len) {
|
||||||
|
rc = -ERANGE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
memcpy(dst, value, value_len);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* 'user.' plus a terminating null */
|
||||||
|
user_name_len = 5 + 1 + name_len;
|
||||||
|
|
||||||
|
rc += user_name_len;
|
||||||
|
|
||||||
|
if (dst_size >= user_name_len) {
|
||||||
|
dst_size -= user_name_len;
|
||||||
|
memcpy(dst, "user.", 5);
|
||||||
|
dst += 5;
|
||||||
|
memcpy(dst, src->ea_data, name_len);
|
||||||
|
dst += name_len;
|
||||||
|
*dst = 0;
|
||||||
|
++dst;
|
||||||
|
} else if (dst_size == 0) {
|
||||||
|
/* skip copy - calc size only */
|
||||||
|
} else {
|
||||||
|
/* stop before overrun buffer */
|
||||||
|
rc = -ERANGE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!src->next_entry_offset)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (src_size < le32_to_cpu(src->next_entry_offset)) {
|
||||||
|
/* stop before overrun buffer */
|
||||||
|
rc = -ERANGE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
src_size -= le32_to_cpu(src->next_entry_offset);
|
||||||
|
src = (void *)((char *)src +
|
||||||
|
le32_to_cpu(src->next_entry_offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* didn't find the named attribute */
|
||||||
|
if (ea_name)
|
||||||
|
rc = -ENODATA;
|
||||||
|
|
||||||
|
out:
|
||||||
|
return (ssize_t)rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const unsigned char *path, const unsigned char *ea_name,
|
||||||
|
char *ea_data, size_t buf_size,
|
||||||
|
struct cifs_sb_info *cifs_sb)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
__le16 *utf16_path;
|
||||||
|
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
||||||
|
struct cifs_open_parms oparms;
|
||||||
|
struct cifs_fid fid;
|
||||||
|
struct smb2_file_full_ea_info *smb2_data;
|
||||||
|
|
||||||
|
utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
|
||||||
|
if (!utf16_path)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
oparms.tcon = tcon;
|
||||||
|
oparms.desired_access = FILE_READ_EA;
|
||||||
|
oparms.disposition = FILE_OPEN;
|
||||||
|
oparms.create_options = 0;
|
||||||
|
oparms.fid = &fid;
|
||||||
|
oparms.reconnect = false;
|
||||||
|
|
||||||
|
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
|
||||||
|
kfree(utf16_path);
|
||||||
|
if (rc) {
|
||||||
|
cifs_dbg(FYI, "open failed rc=%d\n", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
smb2_data = kzalloc(SMB2_MAX_EA_BUF, GFP_KERNEL);
|
||||||
|
if (smb2_data == NULL) {
|
||||||
|
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = SMB2_query_eas(xid, tcon, fid.persistent_fid, fid.volatile_fid,
|
||||||
|
smb2_data);
|
||||||
|
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
|
||||||
|
|
||||||
|
if (!rc)
|
||||||
|
rc = move_smb2_ea_to_cifs(ea_data, buf_size, smb2_data,
|
||||||
|
SMB2_MAX_EA_BUF, ea_name);
|
||||||
|
|
||||||
|
kfree(smb2_data);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
smb2_can_echo(struct TCP_Server_Info *server)
|
smb2_can_echo(struct TCP_Server_Info *server)
|
||||||
{
|
{
|
||||||
@ -2572,6 +2704,9 @@ struct smb_version_operations smb20_operations = {
|
|||||||
.dir_needs_close = smb2_dir_needs_close,
|
.dir_needs_close = smb2_dir_needs_close,
|
||||||
.get_dfs_refer = smb2_get_dfs_refer,
|
.get_dfs_refer = smb2_get_dfs_refer,
|
||||||
.select_sectype = smb2_select_sectype,
|
.select_sectype = smb2_select_sectype,
|
||||||
|
#ifdef CONFIG_CIFS_XATTR
|
||||||
|
.query_all_EAs = smb2_query_eas,
|
||||||
|
#endif /* CIFS_XATTR */
|
||||||
#ifdef CONFIG_CIFS_ACL
|
#ifdef CONFIG_CIFS_ACL
|
||||||
.get_acl = get_smb2_acl,
|
.get_acl = get_smb2_acl,
|
||||||
.get_acl_by_fid = get_smb2_acl_by_fid,
|
.get_acl_by_fid = get_smb2_acl_by_fid,
|
||||||
@ -2662,6 +2797,9 @@ struct smb_version_operations smb21_operations = {
|
|||||||
.enum_snapshots = smb3_enum_snapshots,
|
.enum_snapshots = smb3_enum_snapshots,
|
||||||
.get_dfs_refer = smb2_get_dfs_refer,
|
.get_dfs_refer = smb2_get_dfs_refer,
|
||||||
.select_sectype = smb2_select_sectype,
|
.select_sectype = smb2_select_sectype,
|
||||||
|
#ifdef CONFIG_CIFS_XATTR
|
||||||
|
.query_all_EAs = smb2_query_eas,
|
||||||
|
#endif /* CIFS_XATTR */
|
||||||
#ifdef CONFIG_CIFS_ACL
|
#ifdef CONFIG_CIFS_ACL
|
||||||
.get_acl = get_smb2_acl,
|
.get_acl = get_smb2_acl,
|
||||||
.get_acl_by_fid = get_smb2_acl_by_fid,
|
.get_acl_by_fid = get_smb2_acl_by_fid,
|
||||||
@ -2762,6 +2900,9 @@ struct smb_version_operations smb30_operations = {
|
|||||||
.receive_transform = smb3_receive_transform,
|
.receive_transform = smb3_receive_transform,
|
||||||
.get_dfs_refer = smb2_get_dfs_refer,
|
.get_dfs_refer = smb2_get_dfs_refer,
|
||||||
.select_sectype = smb2_select_sectype,
|
.select_sectype = smb2_select_sectype,
|
||||||
|
#ifdef CONFIG_CIFS_XATTR
|
||||||
|
.query_all_EAs = smb2_query_eas,
|
||||||
|
#endif /* CIFS_XATTR */
|
||||||
#ifdef CONFIG_CIFS_ACL
|
#ifdef CONFIG_CIFS_ACL
|
||||||
.get_acl = get_smb2_acl,
|
.get_acl = get_smb2_acl,
|
||||||
.get_acl_by_fid = get_smb2_acl_by_fid,
|
.get_acl_by_fid = get_smb2_acl_by_fid,
|
||||||
@ -2863,6 +3004,9 @@ struct smb_version_operations smb311_operations = {
|
|||||||
.receive_transform = smb3_receive_transform,
|
.receive_transform = smb3_receive_transform,
|
||||||
.get_dfs_refer = smb2_get_dfs_refer,
|
.get_dfs_refer = smb2_get_dfs_refer,
|
||||||
.select_sectype = smb2_select_sectype,
|
.select_sectype = smb2_select_sectype,
|
||||||
|
#ifdef CONFIG_CIFS_XATTR
|
||||||
|
.query_all_EAs = smb2_query_eas,
|
||||||
|
#endif /* CIFS_XATTR */
|
||||||
};
|
};
|
||||||
#endif /* CIFS_SMB311 */
|
#endif /* CIFS_SMB311 */
|
||||||
|
|
||||||
|
@ -2145,6 +2145,18 @@ qinf_exit:
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
u64 persistent_fid, u64 volatile_fid,
|
||||||
|
struct smb2_file_full_ea_info *data)
|
||||||
|
{
|
||||||
|
return query_info(xid, tcon, persistent_fid, volatile_fid,
|
||||||
|
FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, 0,
|
||||||
|
SMB2_MAX_EA_BUF,
|
||||||
|
sizeof(struct smb2_file_full_ea_info),
|
||||||
|
(void **)&data,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
|
int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data)
|
u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data)
|
||||||
{
|
{
|
||||||
|
@ -1178,6 +1178,16 @@ struct smb2_file_link_info { /* encoding of request for level 11 */
|
|||||||
char FileName[0]; /* Name to be assigned to new link */
|
char FileName[0]; /* Name to be assigned to new link */
|
||||||
} __packed; /* level 11 Set */
|
} __packed; /* level 11 Set */
|
||||||
|
|
||||||
|
#define SMB2_MAX_EA_BUF 2048
|
||||||
|
|
||||||
|
struct smb2_file_full_ea_info { /* encoding of response for level 15 */
|
||||||
|
__le32 next_entry_offset;
|
||||||
|
__u8 flags;
|
||||||
|
__u8 ea_name_length;
|
||||||
|
__le16 ea_value_length;
|
||||||
|
char ea_data[0]; /* \0 terminated name plus value */
|
||||||
|
} __packed; /* level 15 Set */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This level 18, although with struct with same name is different from cifs
|
* This level 18, although with struct with same name is different from cifs
|
||||||
* level 0x107. Level 0x107 has an extra u64 between AccessFlags and
|
* level 0x107. Level 0x107 has an extra u64 between AccessFlags and
|
||||||
|
@ -132,6 +132,9 @@ extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
|
|||||||
u64 persistent_file_id, u64 volatile_file_id);
|
u64 persistent_file_id, u64 volatile_file_id);
|
||||||
extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
u64 persistent_file_id, u64 volatile_file_id);
|
u64 persistent_file_id, u64 volatile_file_id);
|
||||||
|
extern int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
u64 persistent_file_id, u64 volatile_file_id,
|
||||||
|
struct smb2_file_full_ea_info *data);
|
||||||
extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
u64 persistent_file_id, u64 volatile_file_id,
|
u64 persistent_file_id, u64 volatile_file_id,
|
||||||
struct smb2_file_all_info *data);
|
struct smb2_file_all_info *data);
|
||||||
|
Loading…
Reference in New Issue
Block a user