Message ID | 20181016194758.18103-1-lsahlber@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | cifs: add support for ioctl on directories | expand |
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 --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; }
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(-)