2017-08-23 15:51 GMT-07:00 Ronnie Sahlberg <lsahlber@xxxxxxxxxx>: > 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@xxxxxxxxxx> > --- > fs/cifs/smb2ops.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++ > fs/cifs/smb2pdu.c | 12 +++++ > fs/cifs/smb2pdu.h | 10 ++++ > fs/cifs/smb2proto.h | 3 ++ > 4 files changed, 168 insertions(+) > > diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c > index cfacf2c97e94..ca0d15b37c39 100644 > --- a/fs/cifs/smb2ops.c > +++ b/fs/cifs/smb2ops.c > @@ -426,6 +426,137 @@ smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, > 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)src->ea_value_length; Use le32_to_cpu to covert ea_value_length to a host format before using it. > + > + 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 < src->next_entry_offset) { The same for next_entry_offset above and below. > + /* stop before overrun buffer */ > + rc = -ERANGE; > + break; > + } > + src_size -= src->next_entry_offset; > + src = (void *)((char *)src + 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 > smb2_can_echo(struct TCP_Server_Info *server) > { > @@ -2572,6 +2703,9 @@ struct smb_version_operations smb20_operations = { > .dir_needs_close = smb2_dir_needs_close, > .get_dfs_refer = smb2_get_dfs_refer, > .select_sectype = smb2_select_sectype, > +#ifdef CONFIG_CIFS_XATTR > + .query_all_EAs = smb2_query_eas, > +#endif /* CIFS_XATTR */ > #ifdef CONFIG_CIFS_ACL > .get_acl = get_smb2_acl, > .get_acl_by_fid = get_smb2_acl_by_fid, > @@ -2662,6 +2796,9 @@ struct smb_version_operations smb21_operations = { > .enum_snapshots = smb3_enum_snapshots, > .get_dfs_refer = smb2_get_dfs_refer, > .select_sectype = smb2_select_sectype, > +#ifdef CONFIG_CIFS_XATTR > + .query_all_EAs = smb2_query_eas, > +#endif /* CIFS_XATTR */ > #ifdef CONFIG_CIFS_ACL > .get_acl = get_smb2_acl, > .get_acl_by_fid = get_smb2_acl_by_fid, > @@ -2762,6 +2899,9 @@ struct smb_version_operations smb30_operations = { > .receive_transform = smb3_receive_transform, > .get_dfs_refer = smb2_get_dfs_refer, > .select_sectype = smb2_select_sectype, > +#ifdef CONFIG_CIFS_XATTR > + .query_all_EAs = smb2_query_eas, > +#endif /* CIFS_XATTR */ > #ifdef CONFIG_CIFS_ACL > .get_acl = get_smb2_acl, > .get_acl_by_fid = get_smb2_acl_by_fid, > @@ -2863,6 +3003,9 @@ struct smb_version_operations smb311_operations = { > .receive_transform = smb3_receive_transform, > .get_dfs_refer = smb2_get_dfs_refer, > .select_sectype = smb2_select_sectype, > +#ifdef CONFIG_CIFS_XATTR > + .query_all_EAs = smb2_query_eas, > +#endif /* CIFS_XATTR */ > }; > #endif /* CIFS_SMB311 */ > > diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c > index 5fb2fc2d0080..30ef93e459a9 100644 > --- a/fs/cifs/smb2pdu.c > +++ b/fs/cifs/smb2pdu.c > @@ -2140,6 +2140,18 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, > 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, > u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data) > { > diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h > index 18700fd25a0b..efe258c3b562 100644 > --- a/fs/cifs/smb2pdu.h > +++ b/fs/cifs/smb2pdu.h > @@ -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 */ > } __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 > * level 0x107. Level 0x107 has an extra u64 between AccessFlags and > diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h > index 1cadaf9f3c58..183389bfc8f6 100644 > --- a/fs/cifs/smb2proto.h > +++ b/fs/cifs/smb2proto.h > @@ -132,6 +132,9 @@ extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, > u64 persistent_file_id, u64 volatile_file_id); > extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, > 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, > u64 persistent_file_id, u64 volatile_file_id, > struct smb2_file_all_info *data); > -- > 2.13.3 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-cifs" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Best regards, Pavel Shilovsky -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html