summaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorRonnie Sahlberg <lsahlber@redhat.com>2018-10-07 20:19:58 -0400
committerSteve French <stfrench@microsoft.com>2018-10-23 22:16:05 -0400
commitf5b05d622a3e99e6a97a189fe500414be802a05c (patch)
tree173d227ce653e9a9ee4fbaffb4a7f4c7e2ea715e /fs/cifs
parent8c1beb9801293b175cfa0341e5df89581a87dc02 (diff)
cifs: add IOCTL for QUERY_INFO passthrough to userspace
This allows userspace tools to query the raw info levels for cifs files and process the response in userspace. In particular this is useful for many of those data where there is no corresponding native data structure in linux. For example querying the security descriptor for a file and extract the SIDs. Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com> Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/cifs_ioctl.h11
-rw-r--r--fs/cifs/cifsglob.h4
-rw-r--r--fs/cifs/ioctl.c21
-rw-r--r--fs/cifs/smb2inode.c2
-rw-r--r--fs/cifs/smb2ops.c87
-rw-r--r--fs/cifs/smb2pdu.c17
-rw-r--r--fs/cifs/smb2proto.h3
7 files changed, 134 insertions, 11 deletions
diff --git a/fs/cifs/cifs_ioctl.h b/fs/cifs/cifs_ioctl.h
index 57ff0756e30c..d8bce2f862de 100644
--- a/fs/cifs/cifs_ioctl.h
+++ b/fs/cifs/cifs_ioctl.h
@@ -43,8 +43,19 @@ struct smb_snapshot_array {
43 /* snapshots[]; */ 43 /* snapshots[]; */
44} __packed; 44} __packed;
45 45
46struct smb_query_info {
47 __u32 info_type;
48 __u32 file_info_class;
49 __u32 additional_information;
50 __u32 flags;
51 __u32 input_buffer_length;
52 __u32 output_buffer_length;
53 /* char buffer[]; */
54} __packed;
55
46#define CIFS_IOCTL_MAGIC 0xCF 56#define CIFS_IOCTL_MAGIC 0xCF
47#define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int) 57#define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int)
48#define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4) 58#define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4)
49#define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info) 59#define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info)
50#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array) 60#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array)
61#define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info)
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 819ead41bbe2..73801254cc21 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -465,6 +465,10 @@ struct smb_version_operations {
465 enum securityEnum (*select_sectype)(struct TCP_Server_Info *, 465 enum securityEnum (*select_sectype)(struct TCP_Server_Info *,
466 enum securityEnum); 466 enum securityEnum);
467 int (*next_header)(char *); 467 int (*next_header)(char *);
468 /* ioctl passthrough for query_info */
469 int (*ioctl_query_info)(const unsigned int xid,
470 struct cifsFileInfo *file,
471 unsigned long p);
468}; 472};
469 473
470struct smb_version_values { 474struct smb_version_values {
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index 565cf7d6f81f..77c7a5796dfd 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -34,6 +34,24 @@
34#include "cifs_ioctl.h" 34#include "cifs_ioctl.h"
35#include <linux/btrfs.h> 35#include <linux/btrfs.h>
36 36
37static long cifs_ioctl_query_info(unsigned int xid, struct file *filep,
38 unsigned long p)
39{
40 struct cifsFileInfo *pSMBFile = filep->private_data;
41 struct cifs_tcon *tcon;
42
43 cifs_dbg(FYI, "%s %p\n", __func__, pSMBFile);
44 if (pSMBFile == NULL)
45 return -EISDIR;
46 tcon = tlink_tcon(pSMBFile->tlink);
47
48 if (tcon->ses->server->ops->ioctl_query_info)
49 return tcon->ses->server->ops->ioctl_query_info(
50 xid, pSMBFile, p);
51 else
52 return -EOPNOTSUPP;
53}
54
37static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file, 55static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
38 unsigned long srcfd) 56 unsigned long srcfd)
39{ 57{
@@ -194,6 +212,9 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
194 case CIFS_IOC_COPYCHUNK_FILE: 212 case CIFS_IOC_COPYCHUNK_FILE:
195 rc = cifs_ioctl_copychunk(xid, filep, arg); 213 rc = cifs_ioctl_copychunk(xid, filep, arg);
196 break; 214 break;
215 case CIFS_QUERY_INFO:
216 rc = cifs_ioctl_query_info(xid, filep, arg);
217 break;
197 case CIFS_IOC_SET_INTEGRITY: 218 case CIFS_IOC_SET_INTEGRITY:
198 if (pSMBFile == NULL) 219 if (pSMBFile == NULL)
199 break; 220 break;
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index c0b3622a7b53..50d4a9afb36a 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -110,7 +110,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
110 COMPOUND_FID, FILE_ALL_INFORMATION, 110 COMPOUND_FID, FILE_ALL_INFORMATION,
111 SMB2_O_INFO_FILE, 0, 111 SMB2_O_INFO_FILE, 0,
112 sizeof(struct smb2_file_all_info) + 112 sizeof(struct smb2_file_all_info) +
113 PATH_MAX * 2); 113 PATH_MAX * 2, 0, NULL);
114 smb2_set_next_command(server, &rqst[num_rqst]); 114 smb2_set_next_command(server, &rqst[num_rqst]);
115 smb2_set_related(&rqst[num_rqst++]); 115 smb2_set_related(&rqst[num_rqst++]);
116 break; 116 break;
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index b744221f685d..c6c6450d0f38 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -1118,6 +1118,86 @@ req_res_key_exit:
1118 return rc; 1118 return rc;
1119} 1119}
1120 1120
1121static int
1122smb2_ioctl_query_info(const unsigned int xid,
1123 struct cifsFileInfo *file,
1124 unsigned long p)
1125{
1126 struct cifs_tcon *tcon = tlink_tcon(file->tlink);
1127 struct cifs_ses *ses = tcon->ses;
1128 char __user *arg = (char __user *)p;
1129 struct smb_query_info qi;
1130 struct smb_query_info __user *pqi;
1131 int rc = 0;
1132 int flags = 0;
1133 struct smb_rqst rqst;
1134 struct kvec iov[1];
1135 struct kvec rsp_iov;
1136 int resp_buftype;
1137 struct smb2_query_info_rsp *rsp = NULL;
1138 void *buffer;
1139
1140 if (copy_from_user(&qi, arg, sizeof(struct smb_query_info)))
1141 return -EFAULT;
1142
1143 if (qi.output_buffer_length > 1024)
1144 return -EINVAL;
1145
1146 if (!ses || !(ses->server))
1147 return -EIO;
1148
1149 if (smb3_encryption_required(tcon))
1150 flags |= CIFS_TRANSFORM_REQ;
1151
1152 buffer = kmalloc(qi.output_buffer_length, GFP_KERNEL);
1153 if (buffer == NULL)
1154 return -ENOMEM;
1155
1156 if (copy_from_user(buffer, arg + sizeof(struct smb_query_info),
1157 qi.output_buffer_length)) {
1158 kfree(buffer);
1159 return -EFAULT;
1160 }
1161
1162 memset(&rqst, 0, sizeof(struct smb_rqst));
1163 memset(&iov, 0, sizeof(iov));
1164 rqst.rq_iov = iov;
1165 rqst.rq_nvec = 1;
1166
1167 rc = SMB2_query_info_init(tcon, &rqst, file->fid.persistent_fid,
1168 file->fid.volatile_fid,
1169 qi.file_info_class, qi.info_type,
1170 qi.additional_information,
1171 qi.input_buffer_length,
1172 qi.output_buffer_length, buffer);
1173 kfree(buffer);
1174 if (rc)
1175 goto iqinf_exit;
1176
1177 rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
1178 rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
1179 if (rc)
1180 goto iqinf_exit;
1181
1182 pqi = (struct smb_query_info __user *)arg;
1183 if (le32_to_cpu(rsp->OutputBufferLength) < qi.input_buffer_length)
1184 qi.input_buffer_length = le32_to_cpu(rsp->OutputBufferLength);
1185 if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length,
1186 sizeof(qi.input_buffer_length))) {
1187 rc = -EFAULT;
1188 goto iqinf_exit;
1189 }
1190 if (copy_to_user(pqi + 1, rsp->Buffer, qi.input_buffer_length)) {
1191 rc = -EFAULT;
1192 goto iqinf_exit;
1193 }
1194
1195 iqinf_exit:
1196 SMB2_query_info_free(&rqst);
1197 free_rsp_buf(resp_buftype, rsp);
1198 return rc;
1199}
1200
1121static ssize_t 1201static ssize_t
1122smb2_copychunk_range(const unsigned int xid, 1202smb2_copychunk_range(const unsigned int xid,
1123 struct cifsFileInfo *srcfile, 1203 struct cifsFileInfo *srcfile,
@@ -1697,7 +1777,8 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
1697 rc = SMB2_query_info_init(tcon, &rqst[1], COMPOUND_FID, COMPOUND_FID, 1777 rc = SMB2_query_info_init(tcon, &rqst[1], COMPOUND_FID, COMPOUND_FID,
1698 FS_FULL_SIZE_INFORMATION, 1778 FS_FULL_SIZE_INFORMATION,
1699 SMB2_O_INFO_FILESYSTEM, 0, 1779 SMB2_O_INFO_FILESYSTEM, 0,
1700 sizeof(struct smb2_fs_full_size_info)); 1780 sizeof(struct smb2_fs_full_size_info), 0,
1781 NULL);
1701 if (rc) 1782 if (rc)
1702 goto qfs_exit; 1783 goto qfs_exit;
1703 smb2_set_next_command(server, &rqst[1]); 1784 smb2_set_next_command(server, &rqst[1]);
@@ -3364,6 +3445,7 @@ struct smb_version_operations smb20_operations = {
3364 .set_acl = set_smb2_acl, 3445 .set_acl = set_smb2_acl,
3365#endif /* CIFS_ACL */ 3446#endif /* CIFS_ACL */
3366 .next_header = smb2_next_header, 3447 .next_header = smb2_next_header,
3448 .ioctl_query_info = smb2_ioctl_query_info,
3367}; 3449};
3368 3450
3369struct smb_version_operations smb21_operations = { 3451struct smb_version_operations smb21_operations = {
@@ -3459,6 +3541,7 @@ struct smb_version_operations smb21_operations = {
3459 .set_acl = set_smb2_acl, 3541 .set_acl = set_smb2_acl,
3460#endif /* CIFS_ACL */ 3542#endif /* CIFS_ACL */
3461 .next_header = smb2_next_header, 3543 .next_header = smb2_next_header,
3544 .ioctl_query_info = smb2_ioctl_query_info,
3462}; 3545};
3463 3546
3464struct smb_version_operations smb30_operations = { 3547struct smb_version_operations smb30_operations = {
@@ -3563,6 +3646,7 @@ struct smb_version_operations smb30_operations = {
3563 .set_acl = set_smb2_acl, 3646 .set_acl = set_smb2_acl,
3564#endif /* CIFS_ACL */ 3647#endif /* CIFS_ACL */
3565 .next_header = smb2_next_header, 3648 .next_header = smb2_next_header,
3649 .ioctl_query_info = smb2_ioctl_query_info,
3566}; 3650};
3567 3651
3568struct smb_version_operations smb311_operations = { 3652struct smb_version_operations smb311_operations = {
@@ -3668,6 +3752,7 @@ struct smb_version_operations smb311_operations = {
3668 .set_acl = set_smb2_acl, 3752 .set_acl = set_smb2_acl,
3669#endif /* CIFS_ACL */ 3753#endif /* CIFS_ACL */
3670 .next_header = smb2_next_header, 3754 .next_header = smb2_next_header,
3755 .ioctl_query_info = smb2_ioctl_query_info,
3671}; 3756};
3672 3757
3673struct smb_version_values smb20_values = { 3758struct smb_version_values smb20_values = {
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index dacc3400eac6..1310bb355e32 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -2651,7 +2651,7 @@ int
2651SMB2_query_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, 2651SMB2_query_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
2652 u64 persistent_fid, u64 volatile_fid, 2652 u64 persistent_fid, u64 volatile_fid,
2653 u8 info_class, u8 info_type, u32 additional_info, 2653 u8 info_class, u8 info_type, u32 additional_info,
2654 size_t output_len) 2654 size_t output_len, size_t input_len, void *input)
2655{ 2655{
2656 struct smb2_query_info_req *req; 2656 struct smb2_query_info_req *req;
2657 struct kvec *iov = rqst->rq_iov; 2657 struct kvec *iov = rqst->rq_iov;
@@ -2669,16 +2669,17 @@ SMB2_query_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
2669 req->VolatileFileId = volatile_fid; 2669 req->VolatileFileId = volatile_fid;
2670 req->AdditionalInformation = cpu_to_le32(additional_info); 2670 req->AdditionalInformation = cpu_to_le32(additional_info);
2671 2671
2672 /*
2673 * We do not use the input buffer (do not send extra byte)
2674 */
2675 req->InputBufferOffset = 0;
2676
2677 req->OutputBufferLength = cpu_to_le32(output_len); 2672 req->OutputBufferLength = cpu_to_le32(output_len);
2673 if (input_len) {
2674 req->InputBufferLength = cpu_to_le32(input_len);
2675 /* total_len for smb query request never close to le16 max */
2676 req->InputBufferOffset = cpu_to_le16(total_len - 1);
2677 memcpy(req->Buffer, input, input_len);
2678 }
2678 2679
2679 iov[0].iov_base = (char *)req; 2680 iov[0].iov_base = (char *)req;
2680 /* 1 for Buffer */ 2681 /* 1 for Buffer */
2681 iov[0].iov_len = total_len - 1; 2682 iov[0].iov_len = total_len - 1 + input_len;
2682 return 0; 2683 return 0;
2683} 2684}
2684 2685
@@ -2718,7 +2719,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
2718 2719
2719 rc = SMB2_query_info_init(tcon, &rqst, persistent_fid, volatile_fid, 2720 rc = SMB2_query_info_init(tcon, &rqst, persistent_fid, volatile_fid,
2720 info_class, info_type, additional_info, 2721 info_class, info_type, additional_info,
2721 output_len); 2722 output_len, 0, NULL);
2722 if (rc) 2723 if (rc)
2723 goto qinf_exit; 2724 goto qinf_exit;
2724 2725
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 48c5c61702f1..9f4e9ed9ce53 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -163,7 +163,8 @@ extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
163extern int SMB2_query_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, 163extern int SMB2_query_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
164 u64 persistent_fid, u64 volatile_fid, 164 u64 persistent_fid, u64 volatile_fid,
165 u8 info_class, u8 info_type, 165 u8 info_class, u8 info_type,
166 u32 additional_info, size_t output_len); 166 u32 additional_info, size_t output_len,
167 size_t input_len, void *input);
167extern void SMB2_query_info_free(struct smb_rqst *rqst); 168extern void SMB2_query_info_free(struct smb_rqst *rqst);
168extern int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, 169extern int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
169 u64 persistent_file_id, u64 volatile_file_id, 170 u64 persistent_file_id, u64 volatile_file_id,