diff mbox series

cifs: add support for ioctl on directories

Message ID 20181016194758.18103-1-lsahlber@redhat.com
State New
Headers show
Series cifs: add support for ioctl on directories | expand

Commit Message

Ronnie Sahlberg Oct. 16, 2018, 7:47 p.m. UTC
We do not call cifs_open_file() for directories and thus we do not have a
pSMBFile we can extract the FIDs from.

Solve this by instead always using a compounded open/query/close for
the passthrough ioctl.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
---
 fs/cifs/cifsglob.h |  4 ++-
 fs/cifs/ioctl.c    | 43 +++++++++++++++++++++------
 fs/cifs/smb2ops.c  | 85 +++++++++++++++++++++++++++++++++++++++++-------------
 3 files changed, 102 insertions(+), 30 deletions(-)

Comments

Steve French Oct. 19, 2018, 7:27 a.m. UTC | #1
Merged into cifs-2.6.git for-next after cleaning up some minor sparse
endian problems with the create options.  Updated patch attached


On Tue, Oct 16, 2018 at 2:48 PM Ronnie Sahlberg <lsahlber@redhat.com> wrote:
>
> We do not call cifs_open_file() for directories and thus we do not have a
> pSMBFile we can extract the FIDs from.
>
> Solve this by instead always using a compounded open/query/close for
> the passthrough ioctl.
>
> Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> ---
>  fs/cifs/cifsglob.h |  4 ++-
>  fs/cifs/ioctl.c    | 43 +++++++++++++++++++++------
>  fs/cifs/smb2ops.c  | 85 +++++++++++++++++++++++++++++++++++++++++-------------
>  3 files changed, 102 insertions(+), 30 deletions(-)
>
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index 73801254cc21..26f497bd97df 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -33,6 +33,7 @@
>
>  #define CIFS_MAGIC_NUMBER 0xFF534D42      /* the first four bytes of SMB PDUs */
>
> +#define SMB_PATH_MAX 260
>  #define CIFS_PORT 445
>  #define RFC1001_PORT 139
>
> @@ -467,7 +468,8 @@ struct smb_version_operations {
>         int (*next_header)(char *);
>         /* ioctl passthrough for query_info */
>         int (*ioctl_query_info)(const unsigned int xid,
> -                               struct cifsFileInfo *file,
> +                               struct cifs_tcon *tcon,
> +                               __le16 *path, int is_dir,
>                                 unsigned long p);
>  };
>
> diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
> index 77c7a5796dfd..76ddd98b6298 100644
> --- a/fs/cifs/ioctl.c
> +++ b/fs/cifs/ioctl.c
> @@ -32,24 +32,49 @@
>  #include "cifs_debug.h"
>  #include "cifsfs.h"
>  #include "cifs_ioctl.h"
> +#include "smb2proto.h"
>  #include <linux/btrfs.h>
>
>  static long cifs_ioctl_query_info(unsigned int xid, struct file *filep,
>                                   unsigned long p)
>  {
> -       struct cifsFileInfo *pSMBFile = filep->private_data;
> -       struct cifs_tcon *tcon;
> +       struct inode *inode = file_inode(filep);
> +       struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
> +       struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
> +       struct dentry *dentry = filep->f_path.dentry;
> +       unsigned char *path;
> +       __le16 *utf16_path = NULL, root_path;
> +       int rc = 0;
> +
> +       path = build_path_from_dentry(dentry);
> +       if (path == NULL)
> +               return -ENOMEM;
> +
> +       cifs_dbg(FYI, "%s %s\n", __func__, path);
>
> -       cifs_dbg(FYI, "%s %p\n", __func__, pSMBFile);
> -       if (pSMBFile == NULL)
> -               return -EISDIR;
> -       tcon = tlink_tcon(pSMBFile->tlink);
> +       if (!path[0]) {
> +               root_path = 0;
> +               utf16_path = &root_path;
> +       } else {
> +               utf16_path = cifs_convert_path_to_utf16(path + 1, cifs_sb);
> +               if (!utf16_path) {
> +                       rc = -ENOMEM;
> +                       goto ici_exit;
> +               }
> +       }
>
>         if (tcon->ses->server->ops->ioctl_query_info)
> -               return tcon->ses->server->ops->ioctl_query_info(
> -                               xid, pSMBFile, p);
> +               rc = tcon->ses->server->ops->ioctl_query_info(
> +                               xid, tcon, utf16_path,
> +                               filep->private_data ? 0 : 1, p);
>         else
> -               return -EOPNOTSUPP;
> +               rc = -EOPNOTSUPP;
> +
> + ici_exit:
> +       if (utf16_path != &root_path)
> +               kfree(utf16_path);
> +       kfree(path);
> +       return rc;
>  }
>
>  static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> index 8472cb0ae06c..ecdd43585f27 100644
> --- a/fs/cifs/smb2ops.c
> +++ b/fs/cifs/smb2ops.c
> @@ -1120,22 +1120,31 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
>
>  static int
>  smb2_ioctl_query_info(const unsigned int xid,
> -                     struct cifsFileInfo *file,
> +                     struct cifs_tcon *tcon,
> +                     __le16 *path, int is_dir,
>                       unsigned long p)
>  {
> -       struct cifs_tcon *tcon = tlink_tcon(file->tlink);
>         struct cifs_ses *ses = tcon->ses;
>         char __user *arg = (char __user *)p;
>         struct smb_query_info qi;
>         struct smb_query_info __user *pqi;
>         int rc = 0;
>         int flags = 0;
> -       struct smb_rqst rqst;
> -       struct kvec iov[1];
> -       struct kvec rsp_iov;
> -       int resp_buftype = CIFS_NO_BUFFER;
>         struct smb2_query_info_rsp *rsp = NULL;
> -       void *buffer;
> +       void *buffer = NULL;
> +       struct smb_rqst rqst[3];
> +       int resp_buftype[3];
> +       struct kvec rsp_iov[3];
> +       struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
> +       struct cifs_open_parms oparms;
> +       u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
> +       struct cifs_fid fid;
> +       struct kvec qi_iov[1];
> +       struct kvec close_iov[1];
> +
> +       memset(rqst, 0, sizeof(rqst));
> +       resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
> +       memset(rsp_iov, 0, sizeof(rsp_iov));
>
>         if (copy_from_user(&qi, arg, sizeof(struct smb_query_info)))
>                 return -EFAULT;
> @@ -1155,31 +1164,62 @@ smb2_ioctl_query_info(const unsigned int xid,
>
>         if (copy_from_user(buffer, arg + sizeof(struct smb_query_info),
>                            qi.output_buffer_length)) {
> -               kfree(buffer);
> -               return -EFAULT;
> +               rc = -EFAULT;
> +               goto iqinf_exit;
>         }
>
> -       memset(&rqst, 0, sizeof(struct smb_rqst));
> -       memset(&iov, 0, sizeof(iov));
> -       rqst.rq_iov = iov;
> -       rqst.rq_nvec = 1;
> +       /* Open */
> +       memset(&open_iov, 0, sizeof(open_iov));
> +       rqst[0].rq_iov = open_iov;
> +       rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
>
> -       rc = SMB2_query_info_init(tcon, &rqst, file->fid.persistent_fid,
> -                                 file->fid.volatile_fid,
> +       memset(&oparms, 0, sizeof(oparms));
> +       oparms.tcon = tcon;
> +       oparms.desired_access = FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE;
> +       oparms.disposition = FILE_OPEN_LE;
> +       if (is_dir)
> +               oparms.create_options = FILE_DIRECTORY_FILE_LE;
> +       else
> +               oparms.create_options = FILE_NON_DIRECTORY_FILE_LE;
> +       oparms.fid = &fid;
> +       oparms.reconnect = false;
> +
> +       rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, path);
> +       if (rc)
> +               goto iqinf_exit;
> +       smb2_set_next_command(ses->server, &rqst[0]);
> +
> +       /* Query */
> +       memset(&qi_iov, 0, sizeof(qi_iov));
> +       rqst[1].rq_iov = qi_iov;
> +       rqst[1].rq_nvec = 1;
> +
> +       rc = SMB2_query_info_init(tcon, &rqst[1], COMPOUND_FID, COMPOUND_FID,
>                                   qi.file_info_class, qi.info_type,
>                                   qi.additional_information,
>                                   qi.input_buffer_length,
>                                   qi.output_buffer_length, buffer);
> -       kfree(buffer);
>         if (rc)
>                 goto iqinf_exit;
> +       smb2_set_next_command(ses->server, &rqst[1]);
> +       smb2_set_related(&rqst[1]);
> +
> +       /* Close */
> +       memset(&close_iov, 0, sizeof(close_iov));
> +       rqst[2].rq_iov = close_iov;
> +       rqst[2].rq_nvec = 1;
>
> -       rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
> -       rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
> +       rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID);
>         if (rc)
>                 goto iqinf_exit;
> +       smb2_set_related(&rqst[2]);
>
> +       rc = compound_send_recv(xid, ses, flags, 3, rqst,
> +                               resp_buftype, rsp_iov);
> +       if (rc)
> +               goto iqinf_exit;
>         pqi = (struct smb_query_info __user *)arg;
> +       rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
>         if (le32_to_cpu(rsp->OutputBufferLength) < qi.input_buffer_length)
>                 qi.input_buffer_length = le32_to_cpu(rsp->OutputBufferLength);
>         if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length,
> @@ -1193,8 +1233,13 @@ smb2_ioctl_query_info(const unsigned int xid,
>         }
>
>   iqinf_exit:
> -       SMB2_query_info_free(&rqst);
> -       free_rsp_buf(resp_buftype, rsp);
> +       kfree(buffer);
> +       SMB2_open_free(&rqst[0]);
> +       SMB2_query_info_free(&rqst[1]);
> +       SMB2_close_free(&rqst[2]);
> +       free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
> +       free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
> +       free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
>         return rc;
>  }
>
> --
> 2.13.6
>
diff mbox series

Patch

diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 73801254cc21..26f497bd97df 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -33,6 +33,7 @@ 
 
 #define CIFS_MAGIC_NUMBER 0xFF534D42      /* the first four bytes of SMB PDUs */
 
+#define SMB_PATH_MAX 260
 #define CIFS_PORT 445
 #define RFC1001_PORT 139
 
@@ -467,7 +468,8 @@  struct smb_version_operations {
 	int (*next_header)(char *);
 	/* ioctl passthrough for query_info */
 	int (*ioctl_query_info)(const unsigned int xid,
-				struct cifsFileInfo *file,
+				struct cifs_tcon *tcon,
+				__le16 *path, int is_dir,
 				unsigned long p);
 };
 
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index 77c7a5796dfd..76ddd98b6298 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -32,24 +32,49 @@ 
 #include "cifs_debug.h"
 #include "cifsfs.h"
 #include "cifs_ioctl.h"
+#include "smb2proto.h"
 #include <linux/btrfs.h>
 
 static long cifs_ioctl_query_info(unsigned int xid, struct file *filep,
 				  unsigned long p)
 {
-	struct cifsFileInfo *pSMBFile = filep->private_data;
-	struct cifs_tcon *tcon;
+	struct inode *inode = file_inode(filep);
+	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+	struct dentry *dentry = filep->f_path.dentry;
+	unsigned char *path;
+	__le16 *utf16_path = NULL, root_path;
+	int rc = 0;
+
+	path = build_path_from_dentry(dentry);
+	if (path == NULL)
+		return -ENOMEM;
+
+	cifs_dbg(FYI, "%s %s\n", __func__, path);
 
-	cifs_dbg(FYI, "%s %p\n", __func__, pSMBFile);
-	if (pSMBFile == NULL)
-		return -EISDIR;
-	tcon = tlink_tcon(pSMBFile->tlink);
+	if (!path[0]) {
+		root_path = 0;
+		utf16_path = &root_path;
+	} else {
+		utf16_path = cifs_convert_path_to_utf16(path + 1, cifs_sb);
+		if (!utf16_path) {
+			rc = -ENOMEM;
+			goto ici_exit;
+		}
+	}
 
 	if (tcon->ses->server->ops->ioctl_query_info)
-		return tcon->ses->server->ops->ioctl_query_info(
-				xid, pSMBFile, p);
+		rc = tcon->ses->server->ops->ioctl_query_info(
+				xid, tcon, utf16_path,
+				filep->private_data ? 0 : 1, p);
 	else
-		return -EOPNOTSUPP;
+		rc = -EOPNOTSUPP;
+
+ ici_exit:
+	if (utf16_path != &root_path)
+		kfree(utf16_path);
+	kfree(path);
+	return rc;
 }
 
 static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 8472cb0ae06c..ecdd43585f27 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -1120,22 +1120,31 @@  SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
 
 static int
 smb2_ioctl_query_info(const unsigned int xid,
-		      struct cifsFileInfo *file,
+		      struct cifs_tcon *tcon,
+		      __le16 *path, int is_dir,
 		      unsigned long p)
 {
-	struct cifs_tcon *tcon = tlink_tcon(file->tlink);
 	struct cifs_ses *ses = tcon->ses;
 	char __user *arg = (char __user *)p;
 	struct smb_query_info qi;
 	struct smb_query_info __user *pqi;
 	int rc = 0;
 	int flags = 0;
-	struct smb_rqst rqst;
-	struct kvec iov[1];
-	struct kvec rsp_iov;
-	int resp_buftype = CIFS_NO_BUFFER;
 	struct smb2_query_info_rsp *rsp = NULL;
-	void *buffer;
+	void *buffer = NULL;
+	struct smb_rqst rqst[3];
+	int resp_buftype[3];
+	struct kvec rsp_iov[3];
+	struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
+	struct cifs_open_parms oparms;
+	u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+	struct cifs_fid fid;
+	struct kvec qi_iov[1];
+	struct kvec close_iov[1];
+
+	memset(rqst, 0, sizeof(rqst));
+	resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
+	memset(rsp_iov, 0, sizeof(rsp_iov));
 
 	if (copy_from_user(&qi, arg, sizeof(struct smb_query_info)))
 		return -EFAULT;
@@ -1155,31 +1164,62 @@  smb2_ioctl_query_info(const unsigned int xid,
 
 	if (copy_from_user(buffer, arg + sizeof(struct smb_query_info),
 			   qi.output_buffer_length)) {
-		kfree(buffer);
-		return -EFAULT;
+		rc = -EFAULT;
+		goto iqinf_exit;
 	}
 
-	memset(&rqst, 0, sizeof(struct smb_rqst));
-	memset(&iov, 0, sizeof(iov));
-	rqst.rq_iov = iov;
-	rqst.rq_nvec = 1;
+	/* Open */
+	memset(&open_iov, 0, sizeof(open_iov));
+	rqst[0].rq_iov = open_iov;
+	rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
 
-	rc = SMB2_query_info_init(tcon, &rqst, file->fid.persistent_fid,
-				  file->fid.volatile_fid,
+	memset(&oparms, 0, sizeof(oparms));
+	oparms.tcon = tcon;
+	oparms.desired_access = FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE;
+	oparms.disposition = FILE_OPEN_LE;
+	if (is_dir)
+		oparms.create_options = FILE_DIRECTORY_FILE_LE;
+	else
+		oparms.create_options = FILE_NON_DIRECTORY_FILE_LE;
+	oparms.fid = &fid;
+	oparms.reconnect = false;
+
+	rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, path);
+	if (rc)
+		goto iqinf_exit;
+	smb2_set_next_command(ses->server, &rqst[0]);
+
+	/* Query */
+	memset(&qi_iov, 0, sizeof(qi_iov));
+	rqst[1].rq_iov = qi_iov;
+	rqst[1].rq_nvec = 1;
+
+	rc = SMB2_query_info_init(tcon, &rqst[1], COMPOUND_FID, COMPOUND_FID,
 				  qi.file_info_class, qi.info_type,
 				  qi.additional_information,
 				  qi.input_buffer_length,
 				  qi.output_buffer_length, buffer);
-	kfree(buffer);
 	if (rc)
 		goto iqinf_exit;
+	smb2_set_next_command(ses->server, &rqst[1]);
+	smb2_set_related(&rqst[1]);
+
+	/* Close */
+	memset(&close_iov, 0, sizeof(close_iov));
+	rqst[2].rq_iov = close_iov;
+	rqst[2].rq_nvec = 1;
 
-	rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
-	rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
+	rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID);
 	if (rc)
 		goto iqinf_exit;
+	smb2_set_related(&rqst[2]);
 
+	rc = compound_send_recv(xid, ses, flags, 3, rqst,
+				resp_buftype, rsp_iov);
+	if (rc)
+		goto iqinf_exit;
 	pqi = (struct smb_query_info __user *)arg;
+	rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
 	if (le32_to_cpu(rsp->OutputBufferLength) < qi.input_buffer_length)
 		qi.input_buffer_length = le32_to_cpu(rsp->OutputBufferLength);
 	if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length,
@@ -1193,8 +1233,13 @@  smb2_ioctl_query_info(const unsigned int xid,
 	}
 
  iqinf_exit:
-	SMB2_query_info_free(&rqst);
-	free_rsp_buf(resp_buftype, rsp);
+	kfree(buffer);
+	SMB2_open_free(&rqst[0]);
+	SMB2_query_info_free(&rqst[1]);
+	SMB2_close_free(&rqst[2]);
+	free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
+	free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
+	free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
 	return rc;
 }