]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
smb: client: parse uid, gid, mode and dev from WSL reparse points
authorPaulo Alcantara <pc@manguebit.com>
Mon, 29 Jan 2024 00:52:03 +0000 (21:52 -0300)
committerSteve French <stfrench@microsoft.com>
Mon, 11 Mar 2024 00:33:58 +0000 (19:33 -0500)
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@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/inode.c
fs/smb/client/readdir.c
fs/smb/client/reparse.c
fs/smb/client/reparse.h

index 540dca85fae0cd6b7d562c999fdb99fe1580c551..8177ec59afeef6bfbb9df666836bb9496de4b6c4 100644 (file)
@@ -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 @@ out_reparse:
                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
index 88c88d50297c034a8ed802fd48486d487c1a8f75..ebe1cb30e18ed4119bbd8100ca6a8b8f852aeff8 100644 (file)
@@ -125,6 +125,8 @@ retry:
                                        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)->netfs.remote_i_size;
                                                fattr->cf_symlink_target = NULL;
                                        } else {
index e8be756e6768c2c3e53a5d33db9b091679f902be..29a47f20643b111ca9e5bc333871706383b7a0ff 100644 (file)
@@ -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:
index 9816bac98552575e1da5a2c37cb3aed02feebf09..6b55d1df9e2f844f04b8469823ca5d370589c871 100644 (file)
@@ -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) {