cifs: Add support for reading attributes on SMB2+
Subsystems (2)
PATCH 1/1 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 <[email protected]>
--- fs/cifs/smb2ops.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/smb2pdu.c | 12 +++++ fs/cifs/smb2pdu.h | 10 ++++ fs/cifs/smb2proto.h | 3 ++ 4 files changed, 166 insertions(+)
fs/cifs/smb2ops.c +141 -0
@@ -426,6 +426,135 @@ 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 ; + +while (src_size >0 ) { +char *name = &src->EaData[0 ]; +size_t name_len = (size_t )src->EaNameLength; +char *value = &src->EaData[src->EaNameLength +1 ]; +size_t value_len = (size_t )src->EaValueLength; + +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 QAllEAsOut; + } + +if (ea_name) { +if (ea_name_len == name_len && +memcmp (ea_name, name, name_len) ==0 ) { + rc = value_len; +if (dst_size ==0 ) + goto QAllEAsOut; +if (dst_size < value_len) { + rc = -ERANGE ; + goto QAllEAsOut; + } +memcpy (dst, value, value_len); + goto QAllEAsOut; + } + }else { +/* 'user.' plus a terminating null */ +size_t 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->EaData, 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->NextEntryOffset) +break ; + +if (src_size < src->NextEntryOffset) { +/* stop before overrun buffer */ + rc = -ERANGE ; +break ; + } + src_size -= src->NextEntryOffset; + src = (void *)((char *)src + src->NextEntryOffset); + } + +/* didn't find the named attribute */ +if (ea_name) + rc = -ENODATA ; + +QAllEAsOut: +return (ssize_t )rc; +} + +static ssize_t +smb2_query_eas (const unsigned int xid,struct cifs_tcon *tcon, +const unsigned char *searchName,const unsigned char *ea_name, +char *EAData,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 (searchName, cifs_sb); +if (!utf16_path) +return -ENOMEM ; + + 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, 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 (EAData, 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 +2701,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 +2794,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 +2897,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 +3001,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 */
fs/cifs/smb2pdu.c +12 -0
@@ -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) {
fs/cifs/smb2pdu.h +10 -0
@@ -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 NextEntryOffset; +__u8 Flags; +__u8 EaNameLength; +__u16 EaValueLength; +char EaData[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
fs/cifs/smb2proto.h +3 -0
@@ -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);
Signature · 1 line
2.13.3