diff options
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/smb2misc.c | 4 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 116 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 23 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 4 |
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 | */ | ||
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 | |||
1000 | int | 1116 | int |
1001 | SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, | 1117 | SMB2_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 */ | ||
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 | |||
500 | struct smb2_ioctl_req { | 523 | struct 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); |
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 */); | ||
114 | extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, | 118 | extern 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); |
116 | extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, | 120 | extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, |