]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fs/smb/client: Implement new SMB3 POSIX type
authorRalph Boehme <slow@samba.org>
Fri, 15 Nov 2024 18:21:04 +0000 (19:21 +0100)
committerSteve French <stfrench@microsoft.com>
Wed, 4 Dec 2024 23:39:48 +0000 (17:39 -0600)
Fixes special files against current Samba.

On the Samba server:

insgesamt 20
131958 brw-r--r--  1 root  root  0, 0 15. Nov 12:04 blockdev
131965 crw-r--r--  1 root  root  1, 1 15. Nov 12:04 chardev
131966 prw-r--r--  1 samba samba    0 15. Nov 12:05 fifo
131953 -rw-rwxrw-+ 2 samba samba    4 18. Nov 11:37 file
131953 -rw-rwxrw-+ 2 samba samba    4 18. Nov 11:37 hardlink
131957 lrwxrwxrwx  1 samba samba    4 15. Nov 12:03 symlink -> file
131954 -rwxrwxr-x+ 1 samba samba    0 18. Nov 15:28 symlinkoversmb

Before:

ls: cannot access '/mnt/smb3unix/posix/blockdev': No data available
ls: cannot access '/mnt/smb3unix/posix/chardev': No data available
ls: cannot access '/mnt/smb3unix/posix/symlinkoversmb': No data available
ls: cannot access '/mnt/smb3unix/posix/fifo': No data available
ls: cannot access '/mnt/smb3unix/posix/symlink': No data available
total 16
     ? -????????? ? ?    ?     ?            ? blockdev
     ? -????????? ? ?    ?     ?            ? chardev
     ? -????????? ? ?    ?     ?            ? fifo
131953 -rw-rwxrw- 2 root samba 4 Nov 18 11:37 file
131953 -rw-rwxrw- 2 root samba 4 Nov 18 11:37 hardlink
     ? -????????? ? ?    ?     ?            ? symlink
     ? -????????? ? ?    ?     ?            ? symlinkoversmb

After:

insgesamt 21
131958 brw-r--r-- 1 root root  0, 0 15. Nov 12:04 blockdev
131965 crw-r--r-- 1 root root  1, 1 15. Nov 12:04 chardev
131966 prw-r--r-- 1 root samba    0 15. Nov 12:05 fifo
131953 -rw-rwxrw- 2 root samba    4 18. Nov 11:37 file
131953 -rw-rwxrw- 2 root samba    4 18. Nov 11:37 hardlink
131957 lrwxrwxrwx 1 root samba    4 15. Nov 12:03 symlink -> file
131954 lrwxrwxr-x 1 root samba   23 18. Nov 15:28 symlinkoversmb -> mnt/smb3unix/posix/file

Cc: stable@vger.kernel.org
Acked-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
Signed-off-by: Ralph Boehme <slow@samba.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/cifsproto.h
fs/smb/client/inode.c
fs/smb/client/readdir.c
fs/smb/client/reparse.c

index bbaaf16af20fda1a321c71aa54d618d0aa669ddd..90e3297ea84799ef4c54458bfec922752f8408f7 100644 (file)
@@ -669,6 +669,7 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
 int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
                       struct dentry *dentry, struct cifs_tcon *tcon,
                       const char *full_path, umode_t mode, dev_t dev);
+umode_t wire_mode_to_posix(u32 wire);
 
 #ifdef CONFIG_CIFS_DFS_UPCALL
 static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
index 42c030687918d32875b7320808e08782a930c76d..ef913d65c67f2d8dc6ed11cd978cc9266c9a245c 100644 (file)
@@ -746,6 +746,84 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
 #endif
 }
 
+#define POSIX_TYPE_FILE    0
+#define POSIX_TYPE_DIR     1
+#define POSIX_TYPE_SYMLINK 2
+#define POSIX_TYPE_CHARDEV 3
+#define POSIX_TYPE_BLKDEV  4
+#define POSIX_TYPE_FIFO    5
+#define POSIX_TYPE_SOCKET  6
+
+#define POSIX_X_OTH      0000001
+#define POSIX_W_OTH      0000002
+#define POSIX_R_OTH      0000004
+#define POSIX_X_GRP      0000010
+#define POSIX_W_GRP      0000020
+#define POSIX_R_GRP      0000040
+#define POSIX_X_USR      0000100
+#define POSIX_W_USR      0000200
+#define POSIX_R_USR      0000400
+#define POSIX_STICKY     0001000
+#define POSIX_SET_GID    0002000
+#define POSIX_SET_UID    0004000
+
+#define POSIX_OTH_MASK      0000007
+#define POSIX_GRP_MASK      0000070
+#define POSIX_USR_MASK      0000700
+#define POSIX_PERM_MASK     0000777
+#define POSIX_FILETYPE_MASK 0070000
+
+#define POSIX_FILETYPE_SHIFT 12
+
+static u32 wire_perms_to_posix(u32 wire)
+{
+       u32 mode = 0;
+
+       mode |= (wire & POSIX_X_OTH) ? S_IXOTH : 0;
+       mode |= (wire & POSIX_W_OTH) ? S_IWOTH : 0;
+       mode |= (wire & POSIX_R_OTH) ? S_IROTH : 0;
+       mode |= (wire & POSIX_X_GRP) ? S_IXGRP : 0;
+       mode |= (wire & POSIX_W_GRP) ? S_IWGRP : 0;
+       mode |= (wire & POSIX_R_GRP) ? S_IRGRP : 0;
+       mode |= (wire & POSIX_X_USR) ? S_IXUSR : 0;
+       mode |= (wire & POSIX_W_USR) ? S_IWUSR : 0;
+       mode |= (wire & POSIX_R_USR) ? S_IRUSR : 0;
+       mode |= (wire & POSIX_STICKY) ? S_ISVTX : 0;
+       mode |= (wire & POSIX_SET_GID) ? S_ISGID : 0;
+       mode |= (wire & POSIX_SET_UID) ? S_ISUID : 0;
+
+       return mode;
+}
+
+static u32 posix_filetypes[] = {
+       S_IFREG,
+       S_IFDIR,
+       S_IFLNK,
+       S_IFCHR,
+       S_IFBLK,
+       S_IFIFO,
+       S_IFSOCK
+};
+
+static u32 wire_filetype_to_posix(u32 wire_type)
+{
+       if (wire_type >= ARRAY_SIZE(posix_filetypes)) {
+               pr_warn("Unexpected type %u", wire_type);
+               return 0;
+       }
+       return posix_filetypes[wire_type];
+}
+
+umode_t wire_mode_to_posix(u32 wire)
+{
+       u32 wire_type;
+       u32 mode;
+
+       wire_type = (wire & POSIX_FILETYPE_MASK) >> POSIX_FILETYPE_SHIFT;
+       mode = (wire_perms_to_posix(wire) | wire_filetype_to_posix(wire_type));
+       return (umode_t)mode;
+}
+
 /* Fill a cifs_fattr struct with info from POSIX info struct */
 static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
                                       struct cifs_open_info_data *data,
@@ -782,20 +860,13 @@ static void smb311_posix_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->HardLinks);
-       fattr->cf_mode = (umode_t) le32_to_cpu(info->Mode);
+       fattr->cf_mode = wire_mode_to_posix(le32_to_cpu(info->Mode));
 
        if (cifs_open_data_reparse(data) &&
            cifs_reparse_point_to_fattr(cifs_sb, fattr, data))
                goto out_reparse;
 
-       fattr->cf_mode &= ~S_IFMT;
-       if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
-               fattr->cf_mode |= S_IFDIR;
-               fattr->cf_dtype = DT_DIR;
-       } else { /* file */
-               fattr->cf_mode |= S_IFREG;
-               fattr->cf_dtype = DT_REG;
-       }
+       fattr->cf_dtype = S_DT(fattr->cf_mode);
 
 out_reparse:
        if (S_ISLNK(fattr->cf_mode)) {
index b3a8f9c6fcff6f42621d24285152e65746f6a896..f878bcdbd319836ba071cce22a74a0efb67bae95 100644 (file)
@@ -241,31 +241,28 @@ cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info,
        fattr->cf_nlink = le32_to_cpu(info->HardLinks);
        fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes);
 
-       /*
-        * Since we set the inode type below we need to mask off
-        * to avoid strange results if bits set above.
-        * XXX: why not make server&client use the type bits?
-        */
-       fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT;
+       if (fattr->cf_cifsattrs & ATTR_REPARSE)
+               fattr->cf_cifstag = le32_to_cpu(info->ReparseTag);
+
+       /* The Mode field in the response can now include the file type as well */
+       fattr->cf_mode = wire_mode_to_posix(le32_to_cpu(info->Mode));
+       fattr->cf_dtype = S_DT(le32_to_cpu(info->Mode));
+
+       switch (fattr->cf_mode & S_IFMT) {
+       case S_IFLNK:
+       case S_IFBLK:
+       case S_IFCHR:
+               fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
+               break;
+       default:
+               break;
+       }
 
        cifs_dbg(FYI, "posix fattr: dev %d, reparse %d, mode %o\n",
                 le32_to_cpu(info->DeviceId),
                 le32_to_cpu(info->ReparseTag),
                 le32_to_cpu(info->Mode));
 
-       if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
-               fattr->cf_mode |= S_IFDIR;
-               fattr->cf_dtype = DT_DIR;
-       } else {
-               /*
-                * mark anything that is not a dir as regular
-                * file. special files should have the REPARSE
-                * attribute and will be marked as needing revaluation
-                */
-               fattr->cf_mode |= S_IFREG;
-               fattr->cf_dtype = DT_REG;
-       }
-
        sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER);
        sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP);
 }
index e81d2d78ddb7c0637eda1c3de5e85b300f078925..50b1aba2000899ebf830425330465abea1ecab79 100644 (file)
@@ -803,44 +803,60 @@ 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)
+static bool posix_reparse_to_fattr(struct cifs_sb_info *cifs_sb,
+                                  struct cifs_fattr *fattr,
+                                  struct cifs_open_info_data *data)
 {
        struct reparse_posix_data *buf = data->reparse.posix;
-       u32 tag = data->reparse.tag;
 
-       if (tag == IO_REPARSE_TAG_NFS && buf) {
-               if (le16_to_cpu(buf->ReparseDataLength) < sizeof(buf->InodeType))
+
+       if (buf == NULL)
+               return true;
+
+       if (le16_to_cpu(buf->ReparseDataLength) < sizeof(buf->InodeType)) {
+               WARN_ON_ONCE(1);
+               return false;
+       }
+
+       switch (le64_to_cpu(buf->InodeType)) {
+       case NFS_SPECFILE_CHR:
+               if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8) {
+                       WARN_ON_ONCE(1);
                        return false;
-               switch (le64_to_cpu(buf->InodeType)) {
-               case NFS_SPECFILE_CHR:
-                       if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8)
-                               return false;
-                       fattr->cf_mode |= S_IFCHR;
-                       fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
-                       break;
-               case NFS_SPECFILE_BLK:
-                       if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8)
-                               return false;
-                       fattr->cf_mode |= S_IFBLK;
-                       fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
-                       break;
-               case NFS_SPECFILE_FIFO:
-                       fattr->cf_mode |= S_IFIFO;
-                       break;
-               case NFS_SPECFILE_SOCK:
-                       fattr->cf_mode |= S_IFSOCK;
-                       break;
-               case NFS_SPECFILE_LNK:
-                       fattr->cf_mode |= S_IFLNK;
-                       break;
-               default:
+               }
+               fattr->cf_mode |= S_IFCHR;
+               fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
+               break;
+       case NFS_SPECFILE_BLK:
+               if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8) {
                        WARN_ON_ONCE(1);
                        return false;
                }
-               goto out;
+               fattr->cf_mode |= S_IFBLK;
+               fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
+               break;
+       case NFS_SPECFILE_FIFO:
+               fattr->cf_mode |= S_IFIFO;
+               break;
+       case NFS_SPECFILE_SOCK:
+               fattr->cf_mode |= S_IFSOCK;
+               break;
+       case NFS_SPECFILE_LNK:
+               fattr->cf_mode |= S_IFLNK;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return false;
        }
+       return true;
+}
+
+bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+                                struct cifs_fattr *fattr,
+                                struct cifs_open_info_data *data)
+{
+       u32 tag = data->reparse.tag;
+       bool ok;
 
        switch (tag) {
        case IO_REPARSE_TAG_INTERNAL:
@@ -860,15 +876,19 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
        case IO_REPARSE_TAG_LX_BLK:
                wsl_to_fattr(data, cifs_sb, tag, fattr);
                break;
+       case IO_REPARSE_TAG_NFS:
+               ok = posix_reparse_to_fattr(cifs_sb, fattr, data);
+               if (!ok)
+                       return false;
+               break;
        case 0: /* SMB1 symlink */
        case IO_REPARSE_TAG_SYMLINK:
-       case IO_REPARSE_TAG_NFS:
                fattr->cf_mode |= S_IFLNK;
                break;
        default:
                return false;
        }
-out:
+
        fattr->cf_dtype = S_DT(fattr->cf_mode);
        return true;
 }