aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve French <smfrench@gmail.com>2013-06-25 01:20:49 -0400
committerSteve French <smfrench@gmail.com>2013-06-26 18:31:29 -0400
commit4a72dafa19ba77a2fb77ae676f8e3a0d6077c37c (patch)
tree036805a2dd4080dd833fdfa41a79e18562fd5042
parent2b80d049eb6dd08431f63fc0c5ce78567648a033 (diff)
SMB2 FSCTL and IOCTL worker function
This worker function is needed to send SMB2 fsctl (and ioctl) requests including: validating negotiation info (secure negotiate) querying the servers network interfaces copy offload (refcopy) Followon patches for the above three will use this. This patch also does general validation of the response. In the future, as David Disseldorp notes, for the copychunk ioctl case, we will want to enhance the response processing to allow returning the chunk request limits to the caller (even though the server returns an error, in that case we would return data that the caller could use - see 2.2.32.1). See MS-SMB2 Section 2.2.31 for more details on format of fsctl. Acked-by: Pavel Shilovsky <piastry@etersoft.ru> Signed-off-by: Steve French <smfrench@gmail.com>
-rw-r--r--fs/cifs/smb2misc.c4
-rw-r--r--fs/cifs/smb2pdu.c116
-rw-r--r--fs/cifs/smb2pdu.h23
-rw-r--r--fs/cifs/smb2proto.h4
4 files changed, 147 insertions, 0 deletions
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 10383d8c015b..b0c43345cd98 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -266,6 +266,10 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
266 ((struct smb2_query_directory_rsp *)hdr)->OutputBufferLength); 266 ((struct smb2_query_directory_rsp *)hdr)->OutputBufferLength);
267 break; 267 break;
268 case SMB2_IOCTL: 268 case SMB2_IOCTL:
269 *off = le32_to_cpu(
270 ((struct smb2_ioctl_rsp *)hdr)->OutputOffset);
271 *len = le32_to_cpu(((struct smb2_ioctl_rsp *)hdr)->OutputCount);
272 break;
269 case SMB2_CHANGE_NOTIFY: 273 case SMB2_CHANGE_NOTIFY:
270 default: 274 default:
271 /* BB FIXME for unimplemented cases above */ 275 /* BB FIXME for unimplemented cases above */
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 0de6a82e2465..c0d102615d0a 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -997,6 +997,122 @@ creat_exit:
997 return rc; 997 return rc;
998} 998}
999 999
1000/*
1001 * SMB2 IOCTL is used for both IOCTLs and FSCTLs
1002 */
1003int
1004SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
1005 u64 volatile_fid, u32 opcode, bool is_fsctl, char *in_data,
1006 u32 indatalen, char **out_data, u32 *plen /* returned data len */)
1007{
1008 struct smb2_ioctl_req *req;
1009 struct smb2_ioctl_rsp *rsp;
1010 struct TCP_Server_Info *server;
1011 struct cifs_ses *ses = tcon->ses;
1012 struct kvec iov[2];
1013 int resp_buftype;
1014 int num_iovecs;
1015 int rc = 0;
1016
1017 cifs_dbg(FYI, "SMB2 IOCTL\n");
1018
1019 /* zero out returned data len, in case of error */
1020 if (plen)
1021 *plen = 0;
1022
1023 if (ses && (ses->server))
1024 server = ses->server;
1025 else
1026 return -EIO;
1027
1028 rc = small_smb2_init(SMB2_IOCTL, tcon, (void **) &req);
1029 if (rc)
1030 return rc;
1031
1032 req->CtlCode = cpu_to_le32(opcode);
1033 req->PersistentFileId = persistent_fid;
1034 req->VolatileFileId = volatile_fid;
1035
1036 if (indatalen) {
1037 req->InputCount = cpu_to_le32(indatalen);
1038 /* do not set InputOffset if no input data */
1039 req->InputOffset =
1040 cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer) - 4);
1041 iov[1].iov_base = in_data;
1042 iov[1].iov_len = indatalen;
1043 num_iovecs = 2;
1044 } else
1045 num_iovecs = 1;
1046
1047 req->OutputOffset = 0;
1048 req->OutputCount = 0; /* MBZ */
1049
1050 /*
1051 * Could increase MaxOutputResponse, but that would require more
1052 * than one credit. Windows typically sets this smaller, but for some
1053 * ioctls it may be useful to allow server to send more. No point
1054 * limiting what the server can send as long as fits in one credit
1055 */
1056 req->MaxOutputResponse = cpu_to_le32(0xFF00); /* < 64K uses 1 credit */
1057
1058 if (is_fsctl)
1059 req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL);
1060 else
1061 req->Flags = 0;
1062
1063 iov[0].iov_base = (char *)req;
1064 /* 4 for rfc1002 length field */
1065 iov[0].iov_len = get_rfc1002_length(req) + 4;
1066
1067 if (indatalen)
1068 inc_rfc1001_len(req, indatalen);
1069
1070 rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
1071 rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base;
1072
1073 if (rc != 0) {
1074 if (tcon)
1075 cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
1076 goto ioctl_exit;
1077 }
1078
1079 /* check if caller wants to look at return data or just return rc */
1080 if ((plen == NULL) || (out_data == NULL))
1081 goto ioctl_exit;
1082
1083 *plen = le32_to_cpu(rsp->OutputCount);
1084
1085 /* We check for obvious errors in the output buffer length and offset */
1086 if (*plen == 0)
1087 goto ioctl_exit; /* server returned no data */
1088 else if (*plen > 0xFF00) {
1089 cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen);
1090 *plen = 0;
1091 rc = -EIO;
1092 goto ioctl_exit;
1093 }
1094
1095 if (get_rfc1002_length(rsp) < le32_to_cpu(rsp->OutputOffset) + *plen) {
1096 cifs_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen,
1097 le32_to_cpu(rsp->OutputOffset));
1098 *plen = 0;
1099 rc = -EIO;
1100 goto ioctl_exit;
1101 }
1102
1103 *out_data = kmalloc(*plen, GFP_KERNEL);
1104 if (*out_data == NULL) {
1105 rc = -ENOMEM;
1106 goto ioctl_exit;
1107 }
1108
1109 memcpy(*out_data, rsp->hdr.ProtocolId + le32_to_cpu(rsp->OutputOffset),
1110 *plen);
1111ioctl_exit:
1112 free_rsp_buf(resp_buftype, rsp);
1113 return rc;
1114}
1115
1000int 1116int
1001SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, 1117SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
1002 u64 persistent_fid, u64 volatile_fid) 1118 u64 persistent_fid, u64 volatile_fid)
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 0ef06ec24a06..f31043b26bd3 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -497,6 +497,29 @@ struct copychunk_ioctl {
497 __u32 Reserved2; 497 __u32 Reserved2;
498} __packed; 498} __packed;
499 499
500/* Response and Request are the same format */
501struct validate_negotiate_info {
502 __le32 Capabilities;
503 __u8 Guid[SMB2_CLIENT_GUID_SIZE];
504 __le16 SecurityMode;
505 __le16 DialectCount;
506 __le16 Dialect[1];
507} __packed;
508
509#define RSS_CAPABLE 0x00000001
510#define RDMA_CAPABLE 0x00000002
511
512struct network_interface_info_ioctl_rsp {
513 __le32 Next; /* next interface. zero if this is last one */
514 __le32 IfIndex;
515 __le32 Capability; /* RSS or RDMA Capable */
516 __le32 Reserved;
517 __le64 LinkSpeed;
518 char SockAddr_Storage[128];
519} __packed;
520
521#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */
522
500struct smb2_ioctl_req { 523struct smb2_ioctl_req {
501 struct smb2_hdr hdr; 524 struct smb2_hdr hdr;
502 __le16 StructureSize; /* Must be 57 */ 525 __le16 StructureSize; /* Must be 57 */
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 2aa3535e38ce..d4e1eb807457 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -111,6 +111,10 @@ extern int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon,
111 __u32 desired_access, __u32 create_disposition, 111 __u32 desired_access, __u32 create_disposition,
112 __u32 file_attributes, __u32 create_options, 112 __u32 file_attributes, __u32 create_options,
113 __u8 *oplock, struct smb2_file_all_info *buf); 113 __u8 *oplock, struct smb2_file_all_info *buf);
114extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
115 u64 persistent_fid, u64 volatile_fid, u32 opcode,
116 bool is_fsctl, char *in_data, u32 indatalen,
117 char **out_data, u32 *plen /* returned data len */);
114extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, 118extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
115 u64 persistent_file_id, u64 volatile_file_id); 119 u64 persistent_file_id, u64 volatile_file_id);
116extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, 120extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,