diff mbox series

cifs: add support for ioctl on directories

Message ID 20181016035544.31101-2-lsahlber@redhat.com (mailing list archive)
State New, archived
Headers show
Series cifs: add support for ioctl on directories | expand

Commit Message

Ronnie Sahlberg Oct. 16, 2018, 3:55 a.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(-)
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;
 }