diff mbox series

smb: client: instantiate when creating SFU files

Message ID 20240409142859.789709-1-pc@manguebit.com (mailing list archive)
State New, archived
Headers show
Series smb: client: instantiate when creating SFU files | expand

Commit Message

Paulo Alcantara April 9, 2024, 2:28 p.m. UTC
In cifs_sfu_make_node(), on success, instantiate rather than leave it
with dentry unhashed negative to support callers that expect mknod(2)
to always instantiate.

Fixes: 72bc63f5e23a ("smb3: fix creating FIFOs when mounting with "sfu" mount option")
Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
---
 fs/smb/client/smb2ops.c | 94 ++++++++++++++++++++++++-----------------
 1 file changed, 55 insertions(+), 39 deletions(-)

Comments

Steve French April 9, 2024, 4:51 p.m. UTC | #1
updated commit description with details of the repro scenario and
added to cifs-2.6.git for-next

On Tue, Apr 9, 2024 at 9:29 AM Paulo Alcantara <pc@manguebit.com> wrote:
>
> In cifs_sfu_make_node(), on success, instantiate rather than leave it
> with dentry unhashed negative to support callers that expect mknod(2)
> to always instantiate.
>
> Fixes: 72bc63f5e23a ("smb3: fix creating FIFOs when mounting with "sfu" mount option")
> Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
> ---
>  fs/smb/client/smb2ops.c | 94 ++++++++++++++++++++++++-----------------
>  1 file changed, 55 insertions(+), 39 deletions(-)
>
> diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
> index b156eefa75d7..78c94d0350fe 100644
> --- a/fs/smb/client/smb2ops.c
> +++ b/fs/smb/client/smb2ops.c
> @@ -4964,68 +4964,84 @@ static int smb2_next_header(struct TCP_Server_Info *server, char *buf,
>         return 0;
>  }
>
> -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)
> +static 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)
>  {
> -       struct cifs_open_info_data buf = {};
>         struct TCP_Server_Info *server = tcon->ses->server;
>         struct cifs_open_parms oparms;
>         struct cifs_io_parms io_parms = {};
>         struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
>         struct cifs_fid fid;
>         unsigned int bytes_written;
> -       struct win_dev *pdev;
> +       struct win_dev pdev = {};
>         struct kvec iov[2];
>         __u32 oplock = server->oplocks ? REQ_OPLOCK : 0;
>         int rc;
>
> -       if (!S_ISCHR(mode) && !S_ISBLK(mode) && !S_ISFIFO(mode))
> +       switch (mode & S_IFMT) {
> +       case S_IFCHR:
> +               strscpy(pdev.type, "IntxCHR");
> +               pdev.major = cpu_to_le64(MAJOR(dev));
> +               pdev.minor = cpu_to_le64(MINOR(dev));
> +               break;
> +       case S_IFBLK:
> +               strscpy(pdev.type, "IntxBLK");
> +               pdev.major = cpu_to_le64(MAJOR(dev));
> +               pdev.minor = cpu_to_le64(MINOR(dev));
> +               break;
> +       case S_IFIFO:
> +               strscpy(pdev.type, "LnxFIFO");
> +               break;
> +       default:
>                 return -EPERM;
> +       }
>
> -       oparms = (struct cifs_open_parms) {
> -               .tcon = tcon,
> -               .cifs_sb = cifs_sb,
> -               .desired_access = GENERIC_WRITE,
> -               .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR |
> -                                                     CREATE_OPTION_SPECIAL),
> -               .disposition = FILE_CREATE,
> -               .path = full_path,
> -               .fid = &fid,
> -       };
> +       oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, GENERIC_WRITE,
> +                            FILE_CREATE, CREATE_NOT_DIR |
> +                            CREATE_OPTION_SPECIAL, ACL_NO_MODE);
> +       oparms.fid = &fid;
>
> -       rc = server->ops->open(xid, &oparms, &oplock, &buf);
> +       rc = server->ops->open(xid, &oparms, &oplock, NULL);
>         if (rc)
>                 return rc;
>
> -       /*
> -        * BB Do not bother to decode buf since no local inode yet to put
> -        * timestamps in, but we can reuse it safely.
> -        */
> -       pdev = (struct win_dev *)&buf.fi;
>         io_parms.pid = current->tgid;
>         io_parms.tcon = tcon;
> -       io_parms.length = sizeof(*pdev);
> -       iov[1].iov_base = pdev;
> -       iov[1].iov_len = sizeof(*pdev);
> -       if (S_ISCHR(mode)) {
> -               memcpy(pdev->type, "IntxCHR", 8);
> -               pdev->major = cpu_to_le64(MAJOR(dev));
> -               pdev->minor = cpu_to_le64(MINOR(dev));
> -       } else if (S_ISBLK(mode)) {
> -               memcpy(pdev->type, "IntxBLK", 8);
> -               pdev->major = cpu_to_le64(MAJOR(dev));
> -               pdev->minor = cpu_to_le64(MINOR(dev));
> -       } else if (S_ISFIFO(mode)) {
> -               memcpy(pdev->type, "LnxFIFO", 8);
> -       }
> +       io_parms.length = sizeof(pdev);
> +       iov[1].iov_base = &pdev;
> +       iov[1].iov_len = sizeof(pdev);
>
>         rc = server->ops->sync_write(xid, &fid, &io_parms,
>                                      &bytes_written, iov, 1);
>         server->ops->close(xid, tcon, &fid);
> -       d_drop(dentry);
> -       /* FIXME: add code here to set EAs */
> -       cifs_free_open_info(&buf);
> +       return rc;
> +}
> +
> +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)
> +{
> +       struct inode *new = NULL;
> +       int rc;
> +
> +       rc = __cifs_sfu_make_node(xid, inode, dentry, tcon,
> +                                 full_path, mode, dev);
> +       if (rc)
> +               return rc;
> +
> +       if (tcon->posix_extensions) {
> +               rc = smb311_posix_get_inode_info(&new, full_path, NULL,
> +                                                inode->i_sb, xid);
> +       } else if (tcon->unix_ext) {
> +               rc = cifs_get_inode_info_unix(&new, full_path,
> +                                             inode->i_sb, xid);
> +       } else {
> +               rc = cifs_get_inode_info(&new, full_path, NULL,
> +                                        inode->i_sb, xid, NULL);
> +       }
> +       if (!rc)
> +               d_instantiate(dentry, new);
>         return rc;
>  }
>
> --
> 2.44.0
>
diff mbox series

Patch

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index b156eefa75d7..78c94d0350fe 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -4964,68 +4964,84 @@  static int smb2_next_header(struct TCP_Server_Info *server, char *buf,
 	return 0;
 }
 
-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)
+static 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)
 {
-	struct cifs_open_info_data buf = {};
 	struct TCP_Server_Info *server = tcon->ses->server;
 	struct cifs_open_parms oparms;
 	struct cifs_io_parms io_parms = {};
 	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
 	struct cifs_fid fid;
 	unsigned int bytes_written;
-	struct win_dev *pdev;
+	struct win_dev pdev = {};
 	struct kvec iov[2];
 	__u32 oplock = server->oplocks ? REQ_OPLOCK : 0;
 	int rc;
 
-	if (!S_ISCHR(mode) && !S_ISBLK(mode) && !S_ISFIFO(mode))
+	switch (mode & S_IFMT) {
+	case S_IFCHR:
+		strscpy(pdev.type, "IntxCHR");
+		pdev.major = cpu_to_le64(MAJOR(dev));
+		pdev.minor = cpu_to_le64(MINOR(dev));
+		break;
+	case S_IFBLK:
+		strscpy(pdev.type, "IntxBLK");
+		pdev.major = cpu_to_le64(MAJOR(dev));
+		pdev.minor = cpu_to_le64(MINOR(dev));
+		break;
+	case S_IFIFO:
+		strscpy(pdev.type, "LnxFIFO");
+		break;
+	default:
 		return -EPERM;
+	}
 
-	oparms = (struct cifs_open_parms) {
-		.tcon = tcon,
-		.cifs_sb = cifs_sb,
-		.desired_access = GENERIC_WRITE,
-		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR |
-						      CREATE_OPTION_SPECIAL),
-		.disposition = FILE_CREATE,
-		.path = full_path,
-		.fid = &fid,
-	};
+	oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, GENERIC_WRITE,
+			     FILE_CREATE, CREATE_NOT_DIR |
+			     CREATE_OPTION_SPECIAL, ACL_NO_MODE);
+	oparms.fid = &fid;
 
-	rc = server->ops->open(xid, &oparms, &oplock, &buf);
+	rc = server->ops->open(xid, &oparms, &oplock, NULL);
 	if (rc)
 		return rc;
 
-	/*
-	 * BB Do not bother to decode buf since no local inode yet to put
-	 * timestamps in, but we can reuse it safely.
-	 */
-	pdev = (struct win_dev *)&buf.fi;
 	io_parms.pid = current->tgid;
 	io_parms.tcon = tcon;
-	io_parms.length = sizeof(*pdev);
-	iov[1].iov_base = pdev;
-	iov[1].iov_len = sizeof(*pdev);
-	if (S_ISCHR(mode)) {
-		memcpy(pdev->type, "IntxCHR", 8);
-		pdev->major = cpu_to_le64(MAJOR(dev));
-		pdev->minor = cpu_to_le64(MINOR(dev));
-	} else if (S_ISBLK(mode)) {
-		memcpy(pdev->type, "IntxBLK", 8);
-		pdev->major = cpu_to_le64(MAJOR(dev));
-		pdev->minor = cpu_to_le64(MINOR(dev));
-	} else if (S_ISFIFO(mode)) {
-		memcpy(pdev->type, "LnxFIFO", 8);
-	}
+	io_parms.length = sizeof(pdev);
+	iov[1].iov_base = &pdev;
+	iov[1].iov_len = sizeof(pdev);
 
 	rc = server->ops->sync_write(xid, &fid, &io_parms,
 				     &bytes_written, iov, 1);
 	server->ops->close(xid, tcon, &fid);
-	d_drop(dentry);
-	/* FIXME: add code here to set EAs */
-	cifs_free_open_info(&buf);
+	return rc;
+}
+
+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)
+{
+	struct inode *new = NULL;
+	int rc;
+
+	rc = __cifs_sfu_make_node(xid, inode, dentry, tcon,
+				  full_path, mode, dev);
+	if (rc)
+		return rc;
+
+	if (tcon->posix_extensions) {
+		rc = smb311_posix_get_inode_info(&new, full_path, NULL,
+						 inode->i_sb, xid);
+	} else if (tcon->unix_ext) {
+		rc = cifs_get_inode_info_unix(&new, full_path,
+					      inode->i_sb, xid);
+	} else {
+		rc = cifs_get_inode_info(&new, full_path, NULL,
+					 inode->i_sb, xid, NULL);
+	}
+	if (!rc)
+		d_instantiate(dentry, new);
 	return rc;
 }