cifs: add support for ioctl on directories
authorRonnie Sahlberg <lsahlber@redhat.com>
Tue, 16 Oct 2018 19:47:58 +0000 (05:47 +1000)
committerSteve French <stfrench@microsoft.com>
Wed, 24 Oct 2018 02:16:05 +0000 (21:16 -0500)
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>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/cifsglob.h
fs/cifs/ioctl.c
fs/cifs/smb2ops.c

index 7380125..26f497b 100644 (file)
@@ -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);
 };
 
index 77c7a57..76ddd98 100644 (file)
 #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,
index 8472cb0..e35a753 100644 (file)
@@ -1120,22 +1120,31 @@ req_res_key_exit:
 
 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;
+
+       memset(&oparms, 0, sizeof(oparms));
+       oparms.tcon = tcon;
+       oparms.desired_access = FILE_READ_ATTRIBUTES | READ_CONTROL;
+       oparms.disposition = FILE_OPEN;
+       if (is_dir)
+               oparms.create_options = CREATE_NOT_FILE;
+       else
+               oparms.create_options = CREATE_NOT_DIR;
+       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]);
 
-       rc = SMB2_query_info_init(tcon, &rqst, file->fid.persistent_fid,
-                                 file->fid.volatile_fid,
+       /* 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]);
 
-       rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
-       rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
+       /* Close */
+       memset(&close_iov, 0, sizeof(close_iov));
+       rqst[2].rq_iov = close_iov;
+       rqst[2].rq_nvec = 1;
+
+       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;
 }