aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/smb2pdu.c
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 /fs/cifs/smb2pdu.c
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>
Diffstat (limited to 'fs/cifs/smb2pdu.c')
-rw-r--r--fs/cifs/smb2pdu.c116
1 files changed, 116 insertions, 0 deletions
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)