smb: client: fix renaming of reparse points
authorPaulo Alcantara <pc@manguebit.com>
Sun, 26 Nov 2023 02:55:06 +0000 (23:55 -0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 5 Feb 2024 20:14:17 +0000 (20:14 +0000)
[ Upstream commit 7435d51b7ea2ab7801279c43ecd72063e9d5c92f ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
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 5e32c79..4d07b96 100644 (file)
@@ -205,9 +205,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;
+}
 
 static inline void cifs_free_open_info(struct cifs_open_info_data *data)
 {
@@ -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 c00f844..f89e6c2 100644 (file)
@@ -435,9 +435,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 9ee348e..5bdea01 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 09c5c0f..eb54e48 100644 (file)
@@ -2219,7 +2219,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 c94940a..c3e2867 100644 (file)
@@ -781,11 +781,11 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
                                ACL_NO_MODE, NULL, SMB2_OP_DELETE, 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)
 {
        __le16 *smb2_to_name = NULL;
        int rc;
@@ -796,25 +796,33 @@ smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
                goto smb2_rename_path;
        }
        rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
-                             FILE_OPEN, 0, ACL_NO_MODE, smb2_to_name,
+                             FILE_OPEN, create_options, ACL_NO_MODE, smb2_to_name,
                              command, 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
@@ -822,9 +830,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 0e371f7..7cbf1a7 100644 (file)
@@ -80,9 +80,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);