From 8ee52a8ad1dabc3e4802cfb064a0025c2cd0eec6 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 16 Jul 2019 00:25:16 -0500 Subject: [PATCH 5/5] smb3: add mount option to encode xattr names as hexadecimal We store xattrs as Extended Attributes for SMB. Some servers treat the EA names as case-insensitive as well as converting them to all upper-case. This addresses that issue by adding a new mount option to convert all EA names to/from hexadecimal representation. Fixes xfstest generic/533 to Windows servers Signed-off-by: Ronnie Sahlberg --- fs/cifs/cifs_fs_sb.h | 1 + fs/cifs/cifsfs.c | 3 +++ fs/cifs/cifsglob.h | 4 +++- fs/cifs/connect.c | 8 ++++++- fs/cifs/smb2ops.c | 56 ++++++++++++++++++++++++++++++++++---------- 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index b326d2ca3765..90e2192daeff 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -53,6 +53,7 @@ #define CIFS_MOUNT_NO_HANDLE_CACHE 0x4000000 /* disable caching dir handles */ #define CIFS_MOUNT_NO_DFS 0x8000000 /* disable DFS resolving */ #define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from special ACE */ +#define CIFS_MOUNT_ENCODED_XATTR 0x20000000 /* encode the xattr names */ struct cifs_sb_info { struct rb_root tlink_tree; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 0ee63ac4ef72..320c7a6fd318 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -471,6 +471,9 @@ cifs_show_options(struct seq_file *s, struct dentry *root) else seq_puts(s, ",noforcegid"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_ENCODED_XATTR) + seq_puts(s, ",endodedxattr"); + cifs_show_address(s, tcon->ses->server); if (!tcon->unix_ext) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index fe610e7e3670..8c7ea733947b 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -586,6 +586,7 @@ struct smb_vol { bool resilient:1; /* noresilient not required since not fored for CA */ bool domainauto:1; bool rdma:1; + bool encoded_xattr:1; unsigned int bsize; unsigned int rsize; unsigned int wsize; @@ -620,7 +621,8 @@ struct smb_vol { CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \ CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID | \ CIFS_MOUNT_UID_FROM_ACL | CIFS_MOUNT_NO_HANDLE_CACHE | \ - CIFS_MOUNT_NO_DFS | CIFS_MOUNT_MODE_FROM_SID) + CIFS_MOUNT_NO_DFS | CIFS_MOUNT_MODE_FROM_SID | \ + CIFS_MOUNT_ENCODED_XATTR) /** * Generic VFS superblock mount flags (s_flags) to consider when diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 6331c4f93b59..35dfc412370b 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -97,7 +97,7 @@ enum { Opt_persistent, Opt_nopersistent, Opt_resilient, Opt_noresilient, Opt_domainauto, Opt_rdma, Opt_modesid, - Opt_compress, + Opt_compress, Opt_encodedxattr, /* Mount options which take numeric value */ Opt_backupuid, Opt_backupgid, Opt_uid, @@ -196,6 +196,7 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_noresilient, "noresilienthandles"}, { Opt_domainauto, "domainauto"}, { Opt_rdma, "rdma"}, + { Opt_encodedxattr, "encodedxattr" }, { Opt_backupuid, "backupuid=%s" }, { Opt_backupgid, "backupgid=%s" }, @@ -1922,6 +1923,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, cifs_dbg(VFS, "SMB3 compression support is experimental\n"); break; + case Opt_encodedxattr: + vol->encoded_xattr = 1; + break; /* Numeric Values */ case Opt_backupuid: @@ -4004,6 +4008,8 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info, cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; if (pvolume_info->override_gid) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; + if (pvolume_info->encoded_xattr) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_ENCODED_XATTR; if (pvolume_info->dynperm) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; if (pvolume_info->fsc) diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 0cdc4e47ca87..6cb4def11ebe 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -878,7 +878,8 @@ smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, 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) + const unsigned char *ea_name, + struct cifs_sb_info *cifs_sb) { int rc = 0; unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0; @@ -892,6 +893,18 @@ move_smb2_ea_to_cifs(char *dst, size_t dst_size, value = &src->ea_data[src->ea_name_length + 1]; value_len = (size_t)le16_to_cpu(src->ea_value_length); + if (cifs_sb->mnt_cifs_flags | CIFS_MOUNT_ENCODED_XATTR) { + if (name_len & 0x01) { + rc = -EINVAL; + goto out; + } + name_len = name_len / 2; + if (hex2bin(name, name, name_len)) { + rc = -EINVAL; + goto out; + } + } + if (name_len == 0) break; @@ -1005,7 +1018,8 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, info = (struct smb2_file_full_ea_info *)( le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, - le32_to_cpu(rsp->OutputBufferLength), ea_name); + le32_to_cpu(rsp->OutputBufferLength), + ea_name, cifs_sb); qeas_exit: kfree(utf16_path); @@ -1016,13 +1030,14 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, static int smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, - const char *path, const char *ea_name, const void *ea_value, + const char *path, const char *name, const void *ea_value, const __u16 ea_value_len, const struct nls_table *nls_codepage, struct cifs_sb_info *cifs_sb) { struct cifs_ses *ses = tcon->ses; __le16 *utf16_path = NULL; - int ea_name_len = strlen(ea_name); + char *ea_name = (char *)name; + int ea_name_len = strlen(name); int flags = 0; int len; struct smb_rqst rqst[3]; @@ -1039,19 +1054,34 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, struct kvec close_iov[1]; int rc; + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); + if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; - if (ea_name_len > 255) - return -EINVAL; + /* Do we need to encode the name in hex ? */ + if (cifs_sb->mnt_cifs_flags | CIFS_MOUNT_ENCODED_XATTR) { + ea_name = kzalloc(ea_name_len * 2 + 1, GFP_KERNEL); + if (ea_name == NULL) { + rc = -ENOMEM; + goto sea_exit; + } + bin2hex(ea_name, name, ea_name_len); + ea_name_len = 2 * ea_name_len; + } - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) - return -ENOMEM; + if (ea_name_len > 255) { + rc = -EINVAL; + goto sea_exit; + } - memset(rqst, 0, sizeof(rqst)); - resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; - memset(rsp_iov, 0, sizeof(rsp_iov)); + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) { + rc = -ENOMEM; + goto sea_exit; + } if (ses->server->ops->query_all_EAs) { if (!ea_value) { @@ -1124,6 +1154,8 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, resp_buftype, rsp_iov); sea_exit: + if (cifs_sb->mnt_cifs_flags | CIFS_MOUNT_ENCODED_XATTR) + kfree(ea_name); kfree(ea); kfree(utf16_path); SMB2_open_free(&rqst[0]); -- 2.20.1