| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 1 | /* | 
 | 2 |  *   fs/cifs/smb2misc.c | 
 | 3 |  * | 
 | 4 |  *   Copyright (C) International Business Machines  Corp., 2002,2011 | 
 | 5 |  *                 Etersoft, 2012 | 
 | 6 |  *   Author(s): Steve French (sfrench@us.ibm.com) | 
 | 7 |  *              Pavel Shilovsky (pshilovsky@samba.org) 2012 | 
 | 8 |  * | 
 | 9 |  *   This library is free software; you can redistribute it and/or modify | 
 | 10 |  *   it under the terms of the GNU Lesser General Public License as published | 
 | 11 |  *   by the Free Software Foundation; either version 2.1 of the License, or | 
 | 12 |  *   (at your option) any later version. | 
 | 13 |  * | 
 | 14 |  *   This library is distributed in the hope that it will be useful, | 
 | 15 |  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 16 |  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See | 
 | 17 |  *   the GNU Lesser General Public License for more details. | 
 | 18 |  * | 
 | 19 |  *   You should have received a copy of the GNU Lesser General Public License | 
 | 20 |  *   along with this library; if not, write to the Free Software | 
 | 21 |  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 
 | 22 |  */ | 
 | 23 | #include <linux/ctype.h> | 
 | 24 | #include "smb2pdu.h" | 
 | 25 | #include "cifsglob.h" | 
 | 26 | #include "cifsproto.h" | 
 | 27 | #include "smb2proto.h" | 
 | 28 | #include "cifs_debug.h" | 
 | 29 | #include "cifs_unicode.h" | 
 | 30 | #include "smb2status.h" | 
 | 31 |  | 
 | 32 | static int | 
 | 33 | check_smb2_hdr(struct smb2_hdr *hdr, __u64 mid) | 
 | 34 | { | 
 | 35 | 	/* | 
 | 36 | 	 * Make sure that this really is an SMB, that it is a response, | 
 | 37 | 	 * and that the message ids match. | 
 | 38 | 	 */ | 
 | 39 | 	if ((*(__le32 *)hdr->ProtocolId == SMB2_PROTO_NUMBER) && | 
 | 40 | 	    (mid == hdr->MessageId)) { | 
 | 41 | 		if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) | 
 | 42 | 			return 0; | 
 | 43 | 		else { | 
 | 44 | 			/* only one valid case where server sends us request */ | 
 | 45 | 			if (hdr->Command == SMB2_OPLOCK_BREAK) | 
 | 46 | 				return 0; | 
 | 47 | 			else | 
 | 48 | 				cERROR(1, "Received Request not response"); | 
 | 49 | 		} | 
 | 50 | 	} else { /* bad signature or mid */ | 
 | 51 | 		if (*(__le32 *)hdr->ProtocolId != SMB2_PROTO_NUMBER) | 
 | 52 | 			cERROR(1, "Bad protocol string signature header %x", | 
 | 53 | 				  *(unsigned int *) hdr->ProtocolId); | 
 | 54 | 		if (mid != hdr->MessageId) | 
| Pavel Shilovsky | 7411286 | 2012-07-27 01:20:41 +0400 | [diff] [blame] | 55 | 			cERROR(1, "Mids do not match: %llu and %llu", mid, | 
 | 56 | 				  hdr->MessageId); | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 57 | 	} | 
 | 58 | 	cERROR(1, "Bad SMB detected. The Mid=%llu", hdr->MessageId); | 
 | 59 | 	return 1; | 
 | 60 | } | 
 | 61 |  | 
 | 62 | /* | 
 | 63 |  *  The following table defines the expected "StructureSize" of SMB2 responses | 
 | 64 |  *  in order by SMB2 command.  This is similar to "wct" in SMB/CIFS responses. | 
 | 65 |  * | 
 | 66 |  *  Note that commands are defined in smb2pdu.h in le16 but the array below is | 
 | 67 |  *  indexed by command in host byte order | 
 | 68 |  */ | 
 | 69 | static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { | 
 | 70 | 	/* SMB2_NEGOTIATE */ __constant_cpu_to_le16(65), | 
 | 71 | 	/* SMB2_SESSION_SETUP */ __constant_cpu_to_le16(9), | 
 | 72 | 	/* SMB2_LOGOFF */ __constant_cpu_to_le16(4), | 
 | 73 | 	/* SMB2_TREE_CONNECT */ __constant_cpu_to_le16(16), | 
 | 74 | 	/* SMB2_TREE_DISCONNECT */ __constant_cpu_to_le16(4), | 
 | 75 | 	/* SMB2_CREATE */ __constant_cpu_to_le16(89), | 
 | 76 | 	/* SMB2_CLOSE */ __constant_cpu_to_le16(60), | 
 | 77 | 	/* SMB2_FLUSH */ __constant_cpu_to_le16(4), | 
 | 78 | 	/* SMB2_READ */ __constant_cpu_to_le16(17), | 
 | 79 | 	/* SMB2_WRITE */ __constant_cpu_to_le16(17), | 
 | 80 | 	/* SMB2_LOCK */ __constant_cpu_to_le16(4), | 
 | 81 | 	/* SMB2_IOCTL */ __constant_cpu_to_le16(49), | 
 | 82 | 	/* BB CHECK this ... not listed in documentation */ | 
 | 83 | 	/* SMB2_CANCEL */ __constant_cpu_to_le16(0), | 
 | 84 | 	/* SMB2_ECHO */ __constant_cpu_to_le16(4), | 
 | 85 | 	/* SMB2_QUERY_DIRECTORY */ __constant_cpu_to_le16(9), | 
 | 86 | 	/* SMB2_CHANGE_NOTIFY */ __constant_cpu_to_le16(9), | 
 | 87 | 	/* SMB2_QUERY_INFO */ __constant_cpu_to_le16(9), | 
 | 88 | 	/* SMB2_SET_INFO */ __constant_cpu_to_le16(2), | 
 | 89 | 	/* BB FIXME can also be 44 for lease break */ | 
 | 90 | 	/* SMB2_OPLOCK_BREAK */ __constant_cpu_to_le16(24) | 
 | 91 | }; | 
 | 92 |  | 
 | 93 | int | 
 | 94 | smb2_check_message(char *buf, unsigned int length) | 
 | 95 | { | 
 | 96 | 	struct smb2_hdr *hdr = (struct smb2_hdr *)buf; | 
 | 97 | 	struct smb2_pdu *pdu = (struct smb2_pdu *)hdr; | 
 | 98 | 	__u64 mid = hdr->MessageId; | 
 | 99 | 	__u32 len = get_rfc1002_length(buf); | 
 | 100 | 	__u32 clc_len;  /* calculated length */ | 
 | 101 | 	int command; | 
 | 102 |  | 
 | 103 | 	/* BB disable following printk later */ | 
 | 104 | 	cFYI(1, "%s length: 0x%x, smb_buf_length: 0x%x", __func__, length, len); | 
 | 105 |  | 
 | 106 | 	/* | 
 | 107 | 	 * Add function to do table lookup of StructureSize by command | 
 | 108 | 	 * ie Validate the wct via smb2_struct_sizes table above | 
 | 109 | 	 */ | 
 | 110 |  | 
| Pavel Shilovsky | 7411286 | 2012-07-27 01:20:41 +0400 | [diff] [blame] | 111 | 	if (length < sizeof(struct smb2_pdu)) { | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 112 | 		if ((length >= sizeof(struct smb2_hdr)) && (hdr->Status != 0)) { | 
 | 113 | 			pdu->StructureSize2 = 0; | 
 | 114 | 			/* | 
 | 115 | 			 * As with SMB/CIFS, on some error cases servers may | 
 | 116 | 			 * not return wct properly | 
 | 117 | 			 */ | 
 | 118 | 			return 0; | 
 | 119 | 		} else { | 
 | 120 | 			cERROR(1, "Length less than SMB header size"); | 
 | 121 | 		} | 
 | 122 | 		return 1; | 
 | 123 | 	} | 
 | 124 | 	if (len > CIFSMaxBufSize + MAX_SMB2_HDR_SIZE - 4) { | 
| Pavel Shilovsky | 7411286 | 2012-07-27 01:20:41 +0400 | [diff] [blame] | 125 | 		cERROR(1, "SMB length greater than maximum, mid=%llu", mid); | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 126 | 		return 1; | 
 | 127 | 	} | 
 | 128 |  | 
 | 129 | 	if (check_smb2_hdr(hdr, mid)) | 
 | 130 | 		return 1; | 
 | 131 |  | 
| Pavel Shilovsky | 7411286 | 2012-07-27 01:20:41 +0400 | [diff] [blame] | 132 | 	if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { | 
 | 133 | 		cERROR(1, "Illegal structure size %u", | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 134 | 			  le16_to_cpu(hdr->StructureSize)); | 
 | 135 | 		return 1; | 
 | 136 | 	} | 
 | 137 |  | 
 | 138 | 	command = le16_to_cpu(hdr->Command); | 
 | 139 | 	if (command >= NUMBER_OF_SMB2_COMMANDS) { | 
 | 140 | 		cERROR(1, "Illegal SMB2 command %d", command); | 
 | 141 | 		return 1; | 
 | 142 | 	} | 
 | 143 |  | 
 | 144 | 	if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) { | 
| Pavel Shilovsky | 983c88a | 2012-09-18 16:20:33 -0700 | [diff] [blame] | 145 | 		if (command != SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0 || | 
 | 146 | 		    pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2)) { | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 147 | 			/* error packets have 9 byte structure size */ | 
 | 148 | 			cERROR(1, "Illegal response size %u for command %d", | 
 | 149 | 				   le16_to_cpu(pdu->StructureSize2), command); | 
 | 150 | 			return 1; | 
| Pavel Shilovsky | 0822f51 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 151 | 		} else if (command == SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0) | 
 | 152 | 			   && (le16_to_cpu(pdu->StructureSize2) != 44) | 
 | 153 | 			   && (le16_to_cpu(pdu->StructureSize2) != 36)) { | 
 | 154 | 			/* special case for SMB2.1 lease break message */ | 
 | 155 | 			cERROR(1, "Illegal response size %d for oplock break", | 
 | 156 | 				   le16_to_cpu(pdu->StructureSize2)); | 
 | 157 | 			return 1; | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 158 | 		} | 
 | 159 | 	} | 
 | 160 |  | 
 | 161 | 	if (4 + len != length) { | 
 | 162 | 		cERROR(1, "Total length %u RFC1002 length %u mismatch mid %llu", | 
 | 163 | 			  length, 4 + len, mid); | 
 | 164 | 		return 1; | 
 | 165 | 	} | 
 | 166 |  | 
 | 167 | 	clc_len = smb2_calc_size(hdr); | 
 | 168 |  | 
 | 169 | 	if (4 + len != clc_len) { | 
 | 170 | 		cFYI(1, "Calculated size %u length %u mismatch mid %llu", | 
 | 171 | 			clc_len, 4 + len, mid); | 
| Pavel Shilovsky | 983c88a | 2012-09-18 16:20:33 -0700 | [diff] [blame] | 172 | 		/* Windows 7 server returns 24 bytes more */ | 
 | 173 | 		if (clc_len + 20 == len && command == SMB2_OPLOCK_BREAK_HE) | 
 | 174 | 			return 0; | 
| Pavel Shilovsky | 7411286 | 2012-07-27 01:20:41 +0400 | [diff] [blame] | 175 | 		/* server can return one byte more */ | 
 | 176 | 		if (clc_len == 4 + len + 1) | 
 | 177 | 			return 0; | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 178 | 		return 1; | 
 | 179 | 	} | 
 | 180 | 	return 0; | 
 | 181 | } | 
 | 182 |  | 
 | 183 | /* | 
 | 184 |  * The size of the variable area depends on the offset and length fields | 
 | 185 |  * located in different fields for various SMB2 responses. SMB2 responses | 
 | 186 |  * with no variable length info, show an offset of zero for the offset field. | 
 | 187 |  */ | 
 | 188 | static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { | 
 | 189 | 	/* SMB2_NEGOTIATE */ true, | 
 | 190 | 	/* SMB2_SESSION_SETUP */ true, | 
 | 191 | 	/* SMB2_LOGOFF */ false, | 
 | 192 | 	/* SMB2_TREE_CONNECT */	false, | 
 | 193 | 	/* SMB2_TREE_DISCONNECT */ false, | 
 | 194 | 	/* SMB2_CREATE */ true, | 
 | 195 | 	/* SMB2_CLOSE */ false, | 
 | 196 | 	/* SMB2_FLUSH */ false, | 
 | 197 | 	/* SMB2_READ */	true, | 
 | 198 | 	/* SMB2_WRITE */ false, | 
 | 199 | 	/* SMB2_LOCK */	false, | 
 | 200 | 	/* SMB2_IOCTL */ true, | 
 | 201 | 	/* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */ | 
 | 202 | 	/* SMB2_ECHO */ false, | 
 | 203 | 	/* SMB2_QUERY_DIRECTORY */ true, | 
 | 204 | 	/* SMB2_CHANGE_NOTIFY */ true, | 
 | 205 | 	/* SMB2_QUERY_INFO */ true, | 
 | 206 | 	/* SMB2_SET_INFO */ false, | 
 | 207 | 	/* SMB2_OPLOCK_BREAK */ false | 
 | 208 | }; | 
 | 209 |  | 
 | 210 | /* | 
 | 211 |  * Returns the pointer to the beginning of the data area. Length of the data | 
 | 212 |  * area and the offset to it (from the beginning of the smb are also returned. | 
 | 213 |  */ | 
| Pavel Shilovsky | ec2e452 | 2011-12-27 16:12:43 +0400 | [diff] [blame] | 214 | char * | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 215 | smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) | 
 | 216 | { | 
 | 217 | 	*off = 0; | 
 | 218 | 	*len = 0; | 
 | 219 |  | 
 | 220 | 	/* error responses do not have data area */ | 
 | 221 | 	if (hdr->Status && hdr->Status != STATUS_MORE_PROCESSING_REQUIRED && | 
 | 222 | 	    (((struct smb2_err_rsp *)hdr)->StructureSize) == | 
 | 223 | 						SMB2_ERROR_STRUCTURE_SIZE2) | 
 | 224 | 		return NULL; | 
 | 225 |  | 
 | 226 | 	/* | 
 | 227 | 	 * Following commands have data areas so we have to get the location | 
 | 228 | 	 * of the data buffer offset and data buffer length for the particular | 
 | 229 | 	 * command. | 
 | 230 | 	 */ | 
 | 231 | 	switch (hdr->Command) { | 
 | 232 | 	case SMB2_NEGOTIATE: | 
| Pavel Shilovsky | ec2e452 | 2011-12-27 16:12:43 +0400 | [diff] [blame] | 233 | 		*off = le16_to_cpu( | 
 | 234 | 		    ((struct smb2_negotiate_rsp *)hdr)->SecurityBufferOffset); | 
 | 235 | 		*len = le16_to_cpu( | 
 | 236 | 		    ((struct smb2_negotiate_rsp *)hdr)->SecurityBufferLength); | 
 | 237 | 		break; | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 238 | 	case SMB2_SESSION_SETUP: | 
| Pavel Shilovsky | 5478f9b | 2011-12-27 16:22:00 +0400 | [diff] [blame] | 239 | 		*off = le16_to_cpu( | 
 | 240 | 		    ((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferOffset); | 
 | 241 | 		*len = le16_to_cpu( | 
 | 242 | 		    ((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferLength); | 
 | 243 | 		break; | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 244 | 	case SMB2_CREATE: | 
| Pavel Shilovsky | 2503a0d | 2011-12-26 22:58:46 +0400 | [diff] [blame] | 245 | 		*off = le32_to_cpu( | 
 | 246 | 		    ((struct smb2_create_rsp *)hdr)->CreateContextsOffset); | 
 | 247 | 		*len = le32_to_cpu( | 
 | 248 | 		    ((struct smb2_create_rsp *)hdr)->CreateContextsLength); | 
 | 249 | 		break; | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 250 | 	case SMB2_QUERY_INFO: | 
| Pavel Shilovsky | be4cb9e | 2011-12-29 17:06:33 +0400 | [diff] [blame] | 251 | 		*off = le16_to_cpu( | 
 | 252 | 		    ((struct smb2_query_info_rsp *)hdr)->OutputBufferOffset); | 
 | 253 | 		*len = le32_to_cpu( | 
 | 254 | 		    ((struct smb2_query_info_rsp *)hdr)->OutputBufferLength); | 
 | 255 | 		break; | 
 | 256 | 	case SMB2_READ: | 
| Pavel Shilovsky | 09a4707 | 2012-09-18 16:20:29 -0700 | [diff] [blame] | 257 | 		*off = ((struct smb2_read_rsp *)hdr)->DataOffset; | 
 | 258 | 		*len = le32_to_cpu(((struct smb2_read_rsp *)hdr)->DataLength); | 
 | 259 | 		break; | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 260 | 	case SMB2_QUERY_DIRECTORY: | 
| Pavel Shilovsky | d324f08d | 2012-09-18 16:20:33 -0700 | [diff] [blame] | 261 | 		*off = le16_to_cpu( | 
 | 262 | 		  ((struct smb2_query_directory_rsp *)hdr)->OutputBufferOffset); | 
 | 263 | 		*len = le32_to_cpu( | 
 | 264 | 		  ((struct smb2_query_directory_rsp *)hdr)->OutputBufferLength); | 
 | 265 | 		break; | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 266 | 	case SMB2_IOCTL: | 
 | 267 | 	case SMB2_CHANGE_NOTIFY: | 
 | 268 | 	default: | 
 | 269 | 		/* BB FIXME for unimplemented cases above */ | 
 | 270 | 		cERROR(1, "no length check for command"); | 
 | 271 | 		break; | 
 | 272 | 	} | 
 | 273 |  | 
 | 274 | 	/* | 
 | 275 | 	 * Invalid length or offset probably means data area is invalid, but | 
 | 276 | 	 * we have little choice but to ignore the data area in this case. | 
 | 277 | 	 */ | 
 | 278 | 	if (*off > 4096) { | 
 | 279 | 		cERROR(1, "offset %d too large, data area ignored", *off); | 
 | 280 | 		*len = 0; | 
 | 281 | 		*off = 0; | 
 | 282 | 	} else if (*off < 0) { | 
 | 283 | 		cERROR(1, "negative offset %d to data invalid ignore data area", | 
 | 284 | 			  *off); | 
 | 285 | 		*off = 0; | 
 | 286 | 		*len = 0; | 
 | 287 | 	} else if (*len < 0) { | 
 | 288 | 		cERROR(1, "negative data length %d invalid, data area ignored", | 
 | 289 | 			  *len); | 
 | 290 | 		*len = 0; | 
 | 291 | 	} else if (*len > 128 * 1024) { | 
 | 292 | 		cERROR(1, "data area larger than 128K: %d", *len); | 
 | 293 | 		*len = 0; | 
 | 294 | 	} | 
 | 295 |  | 
 | 296 | 	/* return pointer to beginning of data area, ie offset from SMB start */ | 
 | 297 | 	if ((*off != 0) && (*len != 0)) | 
 | 298 | 		return hdr->ProtocolId + *off; | 
 | 299 | 	else | 
 | 300 | 		return NULL; | 
 | 301 | } | 
 | 302 |  | 
 | 303 | /* | 
 | 304 |  * Calculate the size of the SMB message based on the fixed header | 
 | 305 |  * portion, the number of word parameters and the data portion of the message. | 
 | 306 |  */ | 
 | 307 | unsigned int | 
| Pavel Shilovsky | d324f08d | 2012-09-18 16:20:33 -0700 | [diff] [blame] | 308 | smb2_calc_size(void *buf) | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 309 | { | 
| Pavel Shilovsky | d324f08d | 2012-09-18 16:20:33 -0700 | [diff] [blame] | 310 | 	struct smb2_hdr *hdr = (struct smb2_hdr *)buf; | 
| Pavel Shilovsky | 093b2bd | 2011-06-08 15:51:07 +0400 | [diff] [blame] | 311 | 	struct smb2_pdu *pdu = (struct smb2_pdu *)hdr; | 
 | 312 | 	int offset; /* the offset from the beginning of SMB to data area */ | 
 | 313 | 	int data_length; /* the length of the variable length data area */ | 
 | 314 | 	/* Structure Size has already been checked to make sure it is 64 */ | 
 | 315 | 	int len = 4 + le16_to_cpu(pdu->hdr.StructureSize); | 
 | 316 |  | 
 | 317 | 	/* | 
 | 318 | 	 * StructureSize2, ie length of fixed parameter area has already | 
 | 319 | 	 * been checked to make sure it is the correct length. | 
 | 320 | 	 */ | 
 | 321 | 	len += le16_to_cpu(pdu->StructureSize2); | 
 | 322 |  | 
 | 323 | 	if (has_smb2_data_area[le16_to_cpu(hdr->Command)] == false) | 
 | 324 | 		goto calc_size_exit; | 
 | 325 |  | 
 | 326 | 	smb2_get_data_area_len(&offset, &data_length, hdr); | 
 | 327 | 	cFYI(1, "SMB2 data length %d offset %d", data_length, offset); | 
 | 328 |  | 
 | 329 | 	if (data_length > 0) { | 
 | 330 | 		/* | 
 | 331 | 		 * Check to make sure that data area begins after fixed area, | 
 | 332 | 		 * Note that last byte of the fixed area is part of data area | 
 | 333 | 		 * for some commands, typically those with odd StructureSize, | 
 | 334 | 		 * so we must add one to the calculation (and 4 to account for | 
 | 335 | 		 * the size of the RFC1001 hdr. | 
 | 336 | 		 */ | 
 | 337 | 		if (offset + 4 + 1 < len) { | 
 | 338 | 			cERROR(1, "data area offset %d overlaps SMB2 header %d", | 
 | 339 | 				  offset + 4 + 1, len); | 
 | 340 | 			data_length = 0; | 
 | 341 | 		} else { | 
 | 342 | 			len = 4 + offset + data_length; | 
 | 343 | 		} | 
 | 344 | 	} | 
 | 345 | calc_size_exit: | 
 | 346 | 	cFYI(1, "SMB2 len %d", len); | 
 | 347 | 	return len; | 
 | 348 | } | 
| Pavel Shilovsky | 2503a0d | 2011-12-26 22:58:46 +0400 | [diff] [blame] | 349 |  | 
 | 350 | /* Note: caller must free return buffer */ | 
 | 351 | __le16 * | 
 | 352 | cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) | 
 | 353 | { | 
 | 354 | 	int len; | 
 | 355 | 	const char *start_of_path; | 
 | 356 | 	__le16 *to; | 
 | 357 |  | 
 | 358 | 	/* Windows doesn't allow paths beginning with \ */ | 
 | 359 | 	if (from[0] == '\\') | 
 | 360 | 		start_of_path = from + 1; | 
 | 361 | 	else | 
 | 362 | 		start_of_path = from; | 
 | 363 | 	to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len, | 
 | 364 | 				   cifs_sb->local_nls, | 
 | 365 | 				   cifs_sb->mnt_cifs_flags & | 
 | 366 | 					CIFS_MOUNT_MAP_SPECIAL_CHR); | 
 | 367 | 	return to; | 
 | 368 | } | 
| Pavel Shilovsky | 983c88a | 2012-09-18 16:20:33 -0700 | [diff] [blame] | 369 |  | 
| Pavel Shilovsky | 0822f51 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 370 | __le32 | 
 | 371 | smb2_get_lease_state(struct cifsInodeInfo *cinode) | 
 | 372 | { | 
 | 373 | 	if (cinode->clientCanCacheAll) | 
 | 374 | 		return SMB2_LEASE_WRITE_CACHING | SMB2_LEASE_READ_CACHING; | 
 | 375 | 	else if (cinode->clientCanCacheRead) | 
 | 376 | 		return SMB2_LEASE_READ_CACHING; | 
 | 377 | 	return 0; | 
 | 378 | } | 
 | 379 |  | 
 | 380 | __u8 smb2_map_lease_to_oplock(__le32 lease_state) | 
 | 381 | { | 
 | 382 | 	if (lease_state & SMB2_LEASE_WRITE_CACHING) { | 
 | 383 | 		if (lease_state & SMB2_LEASE_HANDLE_CACHING) | 
 | 384 | 			return SMB2_OPLOCK_LEVEL_BATCH; | 
 | 385 | 		else | 
 | 386 | 			return SMB2_OPLOCK_LEVEL_EXCLUSIVE; | 
 | 387 | 	} else if (lease_state & SMB2_LEASE_READ_CACHING) | 
 | 388 | 		return SMB2_OPLOCK_LEVEL_II; | 
 | 389 | 	return 0; | 
 | 390 | } | 
 | 391 |  | 
| Pavel Shilovsky | 233839b1 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 392 | struct smb2_lease_break_work { | 
 | 393 | 	struct work_struct lease_break; | 
 | 394 | 	struct tcon_link *tlink; | 
 | 395 | 	__u8 lease_key[16]; | 
 | 396 | 	__le32 lease_state; | 
 | 397 | }; | 
 | 398 |  | 
 | 399 | static void | 
 | 400 | cifs_ses_oplock_break(struct work_struct *work) | 
 | 401 | { | 
 | 402 | 	struct smb2_lease_break_work *lw = container_of(work, | 
 | 403 | 				struct smb2_lease_break_work, lease_break); | 
 | 404 | 	int rc; | 
 | 405 |  | 
 | 406 | 	rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key, | 
 | 407 | 			      lw->lease_state); | 
 | 408 | 	cFYI(1, "Lease release rc %d", rc); | 
 | 409 | 	cifs_put_tlink(lw->tlink); | 
 | 410 | 	kfree(lw); | 
 | 411 | } | 
 | 412 |  | 
| Pavel Shilovsky | 0822f51 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 413 | static bool | 
 | 414 | smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) | 
 | 415 | { | 
 | 416 | 	struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer; | 
 | 417 | 	struct list_head *tmp, *tmp1, *tmp2; | 
 | 418 | 	struct cifs_ses *ses; | 
 | 419 | 	struct cifs_tcon *tcon; | 
 | 420 | 	struct cifsInodeInfo *cinode; | 
 | 421 | 	struct cifsFileInfo *cfile; | 
| Pavel Shilovsky | 233839b1 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 422 | 	struct cifs_pending_open *open; | 
 | 423 | 	struct smb2_lease_break_work *lw; | 
 | 424 | 	bool found; | 
| Steve French | 12e8a20 | 2012-09-19 09:19:39 -0700 | [diff] [blame] | 425 | 	int ack_req = le32_to_cpu(rsp->Flags & | 
 | 426 | 				  SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); | 
| Pavel Shilovsky | 233839b1 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 427 |  | 
 | 428 | 	lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); | 
 | 429 | 	if (!lw) { | 
 | 430 | 		cERROR(1, "Memory allocation failed during lease break check"); | 
 | 431 | 		return false; | 
 | 432 | 	} | 
 | 433 |  | 
 | 434 | 	INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); | 
 | 435 | 	lw->lease_state = rsp->NewLeaseState; | 
| Pavel Shilovsky | 0822f51 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 436 |  | 
 | 437 | 	cFYI(1, "Checking for lease break"); | 
 | 438 |  | 
 | 439 | 	/* look up tcon based on tid & uid */ | 
 | 440 | 	spin_lock(&cifs_tcp_ses_lock); | 
 | 441 | 	list_for_each(tmp, &server->smb_ses_list) { | 
 | 442 | 		ses = list_entry(tmp, struct cifs_ses, smb_ses_list); | 
| Pavel Shilovsky | 233839b1 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 443 |  | 
 | 444 | 		spin_lock(&cifs_file_list_lock); | 
| Pavel Shilovsky | 0822f51 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 445 | 		list_for_each(tmp1, &ses->tcon_list) { | 
 | 446 | 			tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); | 
 | 447 |  | 
 | 448 | 			cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); | 
| Pavel Shilovsky | 0822f51 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 449 | 			list_for_each(tmp2, &tcon->openFileList) { | 
 | 450 | 				cfile = list_entry(tmp2, struct cifsFileInfo, | 
| Pavel Shilovsky | 233839b1 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 451 | 						   tlist); | 
| Pavel Shilovsky | 0822f51 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 452 | 				cinode = CIFS_I(cfile->dentry->d_inode); | 
 | 453 |  | 
 | 454 | 				if (memcmp(cinode->lease_key, rsp->LeaseKey, | 
 | 455 | 					   SMB2_LEASE_KEY_SIZE)) | 
 | 456 | 					continue; | 
 | 457 |  | 
| Pavel Shilovsky | 233839b1 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 458 | 				cFYI(1, "found in the open list"); | 
| Pavel Shilovsky | 0822f51 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 459 | 				cFYI(1, "lease key match, lease break 0x%d", | 
 | 460 | 				     le32_to_cpu(rsp->NewLeaseState)); | 
 | 461 |  | 
 | 462 | 				smb2_set_oplock_level(cinode, | 
 | 463 | 				  smb2_map_lease_to_oplock(rsp->NewLeaseState)); | 
 | 464 |  | 
| Pavel Shilovsky | 233839b1 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 465 | 				if (ack_req) | 
| Pavel Shilovsky | 0822f51 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 466 | 					cfile->oplock_break_cancelled = false; | 
 | 467 | 				else | 
 | 468 | 					cfile->oplock_break_cancelled = true; | 
 | 469 |  | 
 | 470 | 				queue_work(cifsiod_wq, &cfile->oplock_break); | 
 | 471 |  | 
 | 472 | 				spin_unlock(&cifs_file_list_lock); | 
 | 473 | 				spin_unlock(&cifs_tcp_ses_lock); | 
 | 474 | 				return true; | 
 | 475 | 			} | 
| Pavel Shilovsky | 233839b1 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 476 |  | 
 | 477 | 			found = false; | 
 | 478 | 			list_for_each_entry(open, &tcon->pending_opens, olist) { | 
 | 479 | 				if (memcmp(open->lease_key, rsp->LeaseKey, | 
 | 480 | 					   SMB2_LEASE_KEY_SIZE)) | 
 | 481 | 					continue; | 
 | 482 |  | 
 | 483 | 				if (!found && ack_req) { | 
 | 484 | 					found = true; | 
 | 485 | 					memcpy(lw->lease_key, open->lease_key, | 
 | 486 | 					       SMB2_LEASE_KEY_SIZE); | 
 | 487 | 					lw->tlink = cifs_get_tlink(open->tlink); | 
 | 488 | 					queue_work(cifsiod_wq, | 
 | 489 | 						   &lw->lease_break); | 
 | 490 | 				} | 
 | 491 |  | 
 | 492 | 				cFYI(1, "found in the pending open list"); | 
 | 493 | 				cFYI(1, "lease key match, lease break 0x%d", | 
 | 494 | 				     le32_to_cpu(rsp->NewLeaseState)); | 
 | 495 |  | 
 | 496 | 				open->oplock = | 
 | 497 | 				  smb2_map_lease_to_oplock(rsp->NewLeaseState); | 
 | 498 | 			} | 
 | 499 | 			if (found) { | 
 | 500 | 				spin_unlock(&cifs_file_list_lock); | 
 | 501 | 				spin_unlock(&cifs_tcp_ses_lock); | 
 | 502 | 				return true; | 
 | 503 | 			} | 
| Pavel Shilovsky | 0822f51 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 504 | 		} | 
| Pavel Shilovsky | 233839b1 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 505 | 		spin_unlock(&cifs_file_list_lock); | 
| Pavel Shilovsky | 0822f51 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 506 | 	} | 
 | 507 | 	spin_unlock(&cifs_tcp_ses_lock); | 
| Pavel Shilovsky | 233839b1 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 508 | 	kfree(lw); | 
| Pavel Shilovsky | 0822f51 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 509 | 	cFYI(1, "Can not process lease break - no lease matched"); | 
 | 510 | 	return false; | 
 | 511 | } | 
 | 512 |  | 
| Pavel Shilovsky | 983c88a | 2012-09-18 16:20:33 -0700 | [diff] [blame] | 513 | bool | 
 | 514 | smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) | 
 | 515 | { | 
 | 516 | 	struct smb2_oplock_break *rsp = (struct smb2_oplock_break *)buffer; | 
 | 517 | 	struct list_head *tmp, *tmp1, *tmp2; | 
 | 518 | 	struct cifs_ses *ses; | 
 | 519 | 	struct cifs_tcon *tcon; | 
 | 520 | 	struct cifsInodeInfo *cinode; | 
 | 521 | 	struct cifsFileInfo *cfile; | 
 | 522 |  | 
 | 523 | 	cFYI(1, "Checking for oplock break"); | 
 | 524 |  | 
 | 525 | 	if (rsp->hdr.Command != SMB2_OPLOCK_BREAK) | 
 | 526 | 		return false; | 
 | 527 |  | 
| Steve French | 12e8a20 | 2012-09-19 09:19:39 -0700 | [diff] [blame] | 528 | 	if (rsp->StructureSize != | 
| Pavel Shilovsky | 983c88a | 2012-09-18 16:20:33 -0700 | [diff] [blame] | 529 | 				smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { | 
| Pavel Shilovsky | 0822f51 | 2012-09-19 06:22:45 -0700 | [diff] [blame] | 530 | 		if (le16_to_cpu(rsp->StructureSize) == 44) | 
 | 531 | 			return smb2_is_valid_lease_break(buffer, server); | 
 | 532 | 		else | 
 | 533 | 			return false; | 
| Pavel Shilovsky | 983c88a | 2012-09-18 16:20:33 -0700 | [diff] [blame] | 534 | 	} | 
 | 535 |  | 
 | 536 | 	cFYI(1, "oplock level 0x%d", rsp->OplockLevel); | 
 | 537 |  | 
 | 538 | 	/* look up tcon based on tid & uid */ | 
 | 539 | 	spin_lock(&cifs_tcp_ses_lock); | 
 | 540 | 	list_for_each(tmp, &server->smb_ses_list) { | 
 | 541 | 		ses = list_entry(tmp, struct cifs_ses, smb_ses_list); | 
 | 542 | 		list_for_each(tmp1, &ses->tcon_list) { | 
 | 543 | 			tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); | 
 | 544 |  | 
 | 545 | 			cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); | 
 | 546 | 			spin_lock(&cifs_file_list_lock); | 
 | 547 | 			list_for_each(tmp2, &tcon->openFileList) { | 
 | 548 | 				cfile = list_entry(tmp2, struct cifsFileInfo, | 
 | 549 | 						     tlist); | 
 | 550 | 				if (rsp->PersistentFid != | 
 | 551 | 				    cfile->fid.persistent_fid || | 
 | 552 | 				    rsp->VolatileFid != | 
 | 553 | 				    cfile->fid.volatile_fid) | 
 | 554 | 					continue; | 
 | 555 |  | 
 | 556 | 				cFYI(1, "file id match, oplock break"); | 
 | 557 | 				cinode = CIFS_I(cfile->dentry->d_inode); | 
 | 558 |  | 
 | 559 | 				if (!cinode->clientCanCacheAll && | 
 | 560 | 				    rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE) | 
 | 561 | 					cfile->oplock_break_cancelled = true; | 
 | 562 | 				else | 
 | 563 | 					cfile->oplock_break_cancelled = false; | 
 | 564 |  | 
 | 565 | 				smb2_set_oplock_level(cinode, | 
 | 566 | 				  rsp->OplockLevel ? SMB2_OPLOCK_LEVEL_II : 0); | 
 | 567 |  | 
 | 568 | 				queue_work(cifsiod_wq, &cfile->oplock_break); | 
 | 569 |  | 
 | 570 | 				spin_unlock(&cifs_file_list_lock); | 
 | 571 | 				spin_unlock(&cifs_tcp_ses_lock); | 
 | 572 | 				return true; | 
 | 573 | 			} | 
 | 574 | 			spin_unlock(&cifs_file_list_lock); | 
 | 575 | 			spin_unlock(&cifs_tcp_ses_lock); | 
 | 576 | 			cFYI(1, "No matching file for oplock break"); | 
 | 577 | 			return true; | 
 | 578 | 		} | 
 | 579 | 	} | 
 | 580 | 	spin_unlock(&cifs_tcp_ses_lock); | 
 | 581 | 	cFYI(1, "Can not process oplock break for non-existent connection"); | 
 | 582 | 	return false; | 
 | 583 | } |