6.6-stable review patch. If anyone has any objections, please let me know. ------------------ From: Paulo Alcantara <pc@xxxxxxxxxxxxx> [ Upstream commit 78e26bec4d6d3aef04276e28bed48a45fd00e116 ] Parse the extended attributes from WSL reparse points to correctly report uid, gid mode and dev from ther instantiated inodes. Signed-off-by: Paulo Alcantara <pc@xxxxxxxxxxxxx> Signed-off-by: Steve French <stfrench@xxxxxxxxxxxxx> Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx> --- fs/smb/client/inode.c | 5 ++- fs/smb/client/readdir.c | 2 ++ fs/smb/client/reparse.c | 78 +++++++++++++++++++++++++++++++++-------- fs/smb/client/reparse.h | 29 +++++++++++++++ 4 files changed, 97 insertions(+), 17 deletions(-) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 8aff8382cfb54..67bc1a1e54fde 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -759,6 +759,8 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, fattr->cf_bytes = le64_to_cpu(info->AllocationSize); fattr->cf_createtime = le64_to_cpu(info->CreationTime); fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); + fattr->cf_uid = cifs_sb->ctx->linux_uid; + fattr->cf_gid = cifs_sb->ctx->linux_gid; fattr->cf_mode = cifs_sb->ctx->file_mode; if (cifs_open_data_reparse(data) && @@ -801,9 +803,6 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, fattr->cf_symlink_target = data->symlink_target; data->symlink_target = NULL; } - - fattr->cf_uid = cifs_sb->ctx->linux_uid; - fattr->cf_gid = cifs_sb->ctx->linux_gid; } static int diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c index 3e5d22b356e92..06111d9f39500 100644 --- a/fs/smb/client/readdir.c +++ b/fs/smb/client/readdir.c @@ -125,6 +125,8 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name, if (likely(reparse_inode_match(inode, fattr))) { fattr->cf_mode = inode->i_mode; fattr->cf_rdev = inode->i_rdev; + fattr->cf_uid = inode->i_uid; + fattr->cf_gid = inode->i_gid; fattr->cf_eof = CIFS_I(inode)->server_eof; fattr->cf_symlink_target = NULL; } else { diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c index e8be756e6768c..29a47f20643b1 100644 --- a/fs/smb/client/reparse.c +++ b/fs/smb/client/reparse.c @@ -258,7 +258,9 @@ static int mknod_wsl(unsigned int xid, struct inode *inode, { struct cifs_open_info_data data; struct reparse_data_buffer buf; + struct smb2_create_ea_ctx *cc; struct inode *new; + unsigned int len; struct kvec reparse_iov, xattr_iov; int rc; @@ -275,6 +277,11 @@ static int mknod_wsl(unsigned int xid, struct inode *inode, .reparse = { .tag = le32_to_cpu(buf.ReparseTag), .buf = &buf, }, }; + cc = xattr_iov.iov_base; + len = le32_to_cpu(cc->ctx.DataLength); + memcpy(data.wsl.eas, &cc->ea, len); + data.wsl.eas_len = len; + new = smb2_get_reparse_inode(&data, inode->i_sb, xid, tcon, full_path, &reparse_iov, &xattr_iov); @@ -408,6 +415,62 @@ int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, return parse_reparse_point(buf, plen, cifs_sb, true, data); } +static void wsl_to_fattr(struct cifs_open_info_data *data, + struct cifs_sb_info *cifs_sb, + u32 tag, struct cifs_fattr *fattr) +{ + struct smb2_file_full_ea_info *ea; + u32 next = 0; + + switch (tag) { + case IO_REPARSE_TAG_LX_SYMLINK: + fattr->cf_mode |= S_IFLNK; + break; + case IO_REPARSE_TAG_LX_FIFO: + fattr->cf_mode |= S_IFIFO; + break; + case IO_REPARSE_TAG_AF_UNIX: + fattr->cf_mode |= S_IFSOCK; + break; + case IO_REPARSE_TAG_LX_CHR: + fattr->cf_mode |= S_IFCHR; + break; + case IO_REPARSE_TAG_LX_BLK: + fattr->cf_mode |= S_IFBLK; + break; + } + + if (!data->wsl.eas_len) + goto out; + + ea = (struct smb2_file_full_ea_info *)data->wsl.eas; + do { + const char *name; + void *v; + u8 nlen; + + ea = (void *)((u8 *)ea + next); + next = le32_to_cpu(ea->next_entry_offset); + if (!le16_to_cpu(ea->ea_value_length)) + continue; + + name = ea->ea_data; + nlen = ea->ea_name_length; + v = (void *)((u8 *)ea->ea_data + ea->ea_name_length + 1); + + if (!strncmp(name, SMB2_WSL_XATTR_UID, nlen)) + fattr->cf_uid = wsl_make_kuid(cifs_sb, v); + else if (!strncmp(name, SMB2_WSL_XATTR_GID, nlen)) + fattr->cf_gid = wsl_make_kgid(cifs_sb, v); + else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen)) + fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v); + else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen)) + fattr->cf_rdev = wsl_mkdev(v); + } while (next); +out: + fattr->cf_dtype = S_DT(fattr->cf_mode); +} + bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, struct cifs_open_info_data *data) @@ -448,24 +511,11 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, switch (tag) { case IO_REPARSE_TAG_LX_SYMLINK: - fattr->cf_mode |= S_IFLNK; - fattr->cf_dtype = DT_LNK; - break; case IO_REPARSE_TAG_LX_FIFO: - fattr->cf_mode |= S_IFIFO; - fattr->cf_dtype = DT_FIFO; - break; case IO_REPARSE_TAG_AF_UNIX: - fattr->cf_mode |= S_IFSOCK; - fattr->cf_dtype = DT_SOCK; - break; case IO_REPARSE_TAG_LX_CHR: - fattr->cf_mode |= S_IFCHR; - fattr->cf_dtype = DT_CHR; - break; case IO_REPARSE_TAG_LX_BLK: - fattr->cf_mode |= S_IFBLK; - fattr->cf_dtype = DT_BLK; + wsl_to_fattr(data, cifs_sb, tag, fattr); break; case 0: /* SMB1 symlink */ case IO_REPARSE_TAG_SYMLINK: diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h index 9816bac985525..6b55d1df9e2f8 100644 --- a/fs/smb/client/reparse.h +++ b/fs/smb/client/reparse.h @@ -8,6 +8,8 @@ #include <linux/fs.h> #include <linux/stat.h> +#include <linux/uidgid.h> +#include "fs_context.h" #include "cifsglob.h" static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf) @@ -17,6 +19,33 @@ static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf) return MKDEV(v >> 32, v & 0xffffffff); } +static inline dev_t wsl_mkdev(void *ptr) +{ + u64 v = le64_to_cpu(*(__le64 *)ptr); + + return MKDEV(v & 0xffffffff, v >> 32); +} + +static inline kuid_t wsl_make_kuid(struct cifs_sb_info *cifs_sb, + void *ptr) +{ + u32 uid = le32_to_cpu(*(__le32 *)ptr); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) + return cifs_sb->ctx->linux_uid; + return make_kuid(current_user_ns(), uid); +} + +static inline kgid_t wsl_make_kgid(struct cifs_sb_info *cifs_sb, + void *ptr) +{ + u32 gid = le32_to_cpu(*(__le32 *)ptr); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) + return cifs_sb->ctx->linux_gid; + return make_kgid(current_user_ns(), gid); +} + static inline u64 reparse_mode_nfs_type(mode_t mode) { switch (mode & S_IFMT) { -- 2.43.0