Skip to content

Commit 4a72daf

Browse files
committed
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 <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 2b80d04 commit 4a72daf

File tree

4 files changed

+147
-0
lines changed

4 files changed

+147
-0
lines changed

fs/cifs/smb2misc.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,10 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
266266
((struct smb2_query_directory_rsp *)hdr)->OutputBufferLength);
267267
break;
268268
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;
269273
case SMB2_CHANGE_NOTIFY:
270274
default:
271275
/* BB FIXME for unimplemented cases above */

fs/cifs/smb2pdu.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,122 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
997997
return rc;
998998
}
999999

1000+
/*
1001+
* SMB2 IOCTL is used for both IOCTLs and FSCTLs
1002+
*/
1003+
int
1004+
SMB2_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);
1111+
ioctl_exit:
1112+
free_rsp_buf(resp_buftype, rsp);
1113+
return rc;
1114+
}
1115+
10001116
int
10011117
SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
10021118
u64 persistent_fid, u64 volatile_fid)

fs/cifs/smb2pdu.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,29 @@ struct copychunk_ioctl {
497497
__u32 Reserved2;
498498
} __packed;
499499

500+
/* Response and Request are the same format */
501+
struct 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+
512+
struct 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+
500523
struct smb2_ioctl_req {
501524
struct smb2_hdr hdr;
502525
__le16 StructureSize; /* Must be 57 */

fs/cifs/smb2proto.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ extern int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon,
111111
__u32 desired_access, __u32 create_disposition,
112112
__u32 file_attributes, __u32 create_options,
113113
__u8 *oplock, struct smb2_file_all_info *buf);
114+
extern 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 */);
114118
extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
115119
u64 persistent_file_id, u64 volatile_file_id);
116120
extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,

0 commit comments

Comments
 (0)