2018-10-16 17:30:29

by Leif Sahlberg

[permalink] [raw]
Subject: [PATCH] cifs: add support for ioctl on directories

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 <[email protected]>
---
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