smb: client: fix renaming of reparse points
authorPaulo Alcantara <pc@manguebit.com>
Sun, 26 Nov 2023 02:55:06 +0000 (23:55 -0300)
committerSteve French <stfrench@microsoft.com>
Sun, 7 Jan 2024 21:46:06 +0000 (15:46 -0600)
The client was sending an SMB2_CREATE request without setting
OPEN_REPARSE_POINT flag thus failing the entire rename operation.

Fix this by setting OPEN_REPARSE_POINT in create options for
SMB2_CREATE request when the source inode is a repase point.

Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/cifsglob.h
fs/smb/client/cifsproto.h
fs/smb/client/cifssmb.c
fs/smb/client/inode.c
fs/smb/client/smb2inode.c
fs/smb/client/smb2proto.h

index 541e8b88b4c1d6df6ddaf25f90aab3cb0670368d..ad5cf3e31d7342cd7ed9911550a31f33d9672868 100644 (file)
@@ -210,9 +210,18 @@ struct cifs_open_info_data {
        };
 };
 
-#define cifs_open_data_reparse(d) \
-       ((d)->reparse_point || \
-        (le32_to_cpu((d)->fi.Attributes) & ATTR_REPARSE))
+static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
+{
+       struct smb2_file_all_info *fi = &data->fi;
+       u32 attrs = le32_to_cpu(fi->Attributes);
+       bool ret;
+
+       ret = data->reparse_point || (attrs & ATTR_REPARSE);
+       if (ret)
+               attrs |= ATTR_REPARSE;
+       fi->Attributes = cpu_to_le32(attrs);
+       return ret;
+}
 
 /*
  *****************************************************************
@@ -390,8 +399,11 @@ struct smb_version_operations {
        int (*rename_pending_delete)(const char *, struct dentry *,
                                     const unsigned int);
        /* send rename request */
-       int (*rename)(const unsigned int, struct cifs_tcon *, const char *,
-                     const char *, struct cifs_sb_info *);
+       int (*rename)(const unsigned int xid,
+                     struct cifs_tcon *tcon,
+                     struct dentry *source_dentry,
+                     const char *from_name, const char *to_name,
+                     struct cifs_sb_info *cifs_sb);
        /* send create hardlink request */
        int (*create_hardlink)(const unsigned int, struct cifs_tcon *,
                               const char *, const char *,
index 4c5d533d98a3c948316d857dc64d521eba40e126..e680fe46d4e86deea8c6ad527280214fdf9e77b5 100644 (file)
@@ -439,9 +439,10 @@ extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,
                        int remap_special_chars);
 extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon,
                          const char *name, struct cifs_sb_info *cifs_sb);
-extern int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
-                        const char *from_name, const char *to_name,
-                        struct cifs_sb_info *cifs_sb);
+int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
+                 struct dentry *source_dentry,
+                 const char *from_name, const char *to_name,
+                 struct cifs_sb_info *cifs_sb);
 extern int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *tcon,
                                 int netfid, const char *target_name,
                                 const struct nls_table *nls_codepage,
index 9ee348e6d1069caf4993d2fc84164e0ba832e2f2..5bdea01919e8bd693701e39fe197a402df589fc9 100644 (file)
@@ -2149,10 +2149,10 @@ CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id)
        return rc;
 }
 
-int
-CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
-             const char *from_name, const char *to_name,
-             struct cifs_sb_info *cifs_sb)
+int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
+                 struct dentry *source_dentry,
+                 const char *from_name, const char *to_name,
+                 struct cifs_sb_info *cifs_sb)
 {
        int rc = 0;
        RENAME_REQ *pSMB = NULL;
index 8d6282bbc7ed80ea5e876c5fd9a00a0769444fa9..cc4c144ea40ae215beabf0bae4aa0db0a04941f0 100644 (file)
@@ -2244,7 +2244,8 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry,
                return -ENOSYS;
 
        /* try path-based rename first */
-       rc = server->ops->rename(xid, tcon, from_path, to_path, cifs_sb);
+       rc = server->ops->rename(xid, tcon, from_dentry,
+                                from_path, to_path, cifs_sb);
 
        /*
         * Don't bother with rename by filehandle unless file is busy and
index 0f096fb41b3e8007e42dd22490096578b317e23f..2d9cdc1944031ca4e73a03915edeb7107026b0f6 100644 (file)
@@ -889,11 +889,11 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
                                NULL, NULL, NULL, NULL, NULL);
 }
 
-static int
-smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
-                  const char *from_name, const char *to_name,
-                  struct cifs_sb_info *cifs_sb, __u32 access, int command,
-                  struct cifsFileInfo *cfile)
+static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
+                             const char *from_name, const char *to_name,
+                             struct cifs_sb_info *cifs_sb,
+                             __u32 create_options, __u32 access,
+                             int command, struct cifsFileInfo *cfile)
 {
        struct kvec in_iov;
        __le16 *smb2_to_name = NULL;
@@ -907,25 +907,33 @@ smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
        in_iov.iov_base = smb2_to_name;
        in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX);
        rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
-                             FILE_OPEN, 0, ACL_NO_MODE, &in_iov,
+                             FILE_OPEN, create_options, ACL_NO_MODE, &in_iov,
                              &command, 1, cfile, NULL, NULL, NULL, NULL);
 smb2_rename_path:
        kfree(smb2_to_name);
        return rc;
 }
 
-int
-smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
-                const char *from_name, const char *to_name,
-                struct cifs_sb_info *cifs_sb)
+int smb2_rename_path(const unsigned int xid,
+                    struct cifs_tcon *tcon,
+                    struct dentry *source_dentry,
+                    const char *from_name, const char *to_name,
+                    struct cifs_sb_info *cifs_sb)
 {
+       struct cifsInodeInfo *ci;
        struct cifsFileInfo *cfile;
+       __u32 co = 0;
 
+       if (source_dentry) {
+               ci = CIFS_I(d_inode(source_dentry));
+               if (ci->cifsAttrs & ATTR_REPARSE)
+                       co |= OPEN_REPARSE_POINT;
+       }
        drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb);
        cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile);
 
-       return smb2_set_path_attr(xid, tcon, from_name, to_name,
-                                 cifs_sb, DELETE, SMB2_OP_RENAME, cfile);
+       return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
+                                 co, DELETE, SMB2_OP_RENAME, cfile);
 }
 
 int
@@ -933,9 +941,9 @@ smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
                     const char *from_name, const char *to_name,
                     struct cifs_sb_info *cifs_sb)
 {
-       return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
-                                 FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK,
-                                 NULL);
+       return smb2_set_path_attr(xid, tcon, from_name, to_name,
+                                 cifs_sb, 0, FILE_READ_ATTRIBUTES,
+                                 SMB2_OP_HARDLINK, NULL);
 }
 
 int
index 709324cff7129cd2d1bb41625f29eeebc6230857..aef50316670acdccb398a7812e7f07514de811de 100644 (file)
@@ -86,9 +86,11 @@ extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon,
                      const char *name, struct cifs_sb_info *cifs_sb);
 extern int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon,
                       const char *name, struct cifs_sb_info *cifs_sb);
-extern int smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
-                           const char *from_name, const char *to_name,
-                           struct cifs_sb_info *cifs_sb);
+int smb2_rename_path(const unsigned int xid,
+                    struct cifs_tcon *tcon,
+                    struct dentry *source_dentry,
+                    const char *from_name, const char *to_name,
+                    struct cifs_sb_info *cifs_sb);
 extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
                                const char *from_name, const char *to_name,
                                struct cifs_sb_info *cifs_sb);