]>
Commit | Line | Data |
---|---|---|
23db65f5 JL |
1 | /* |
2 | * SMB1 (CIFS) version specific operations | |
3 | * | |
4 | * Copyright (c) 2012, Jeff Layton <jlayton@redhat.com> | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License v2 as published | |
8 | * by the Free Software Foundation. | |
9 | * | |
10 | * This library is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See | |
13 | * the GNU Lesser General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU Lesser General Public License | |
16 | * along with this library; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | */ | |
19 | ||
20 | #include "cifsglob.h" | |
121b046a JL |
21 | #include "cifsproto.h" |
22 | #include "cifs_debug.h" | |
106dc538 | 23 | #include "cifspdu.h" |
121b046a JL |
24 | |
25 | /* | |
26 | * An NT cancel request header looks just like the original request except: | |
27 | * | |
28 | * The Command is SMB_COM_NT_CANCEL | |
29 | * The WordCount is zeroed out | |
30 | * The ByteCount is zeroed out | |
31 | * | |
32 | * This function mangles an existing request buffer into a | |
33 | * SMB_COM_NT_CANCEL request and then sends it. | |
34 | */ | |
35 | static int | |
36 | send_nt_cancel(struct TCP_Server_Info *server, void *buf, | |
37 | struct mid_q_entry *mid) | |
38 | { | |
39 | int rc = 0; | |
40 | struct smb_hdr *in_buf = (struct smb_hdr *)buf; | |
41 | ||
42 | /* -4 for RFC1001 length and +2 for BCC field */ | |
43 | in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2); | |
44 | in_buf->Command = SMB_COM_NT_CANCEL; | |
45 | in_buf->WordCount = 0; | |
46 | put_bcc(0, in_buf); | |
47 | ||
48 | mutex_lock(&server->srv_mutex); | |
49 | rc = cifs_sign_smb(in_buf, server, &mid->sequence_number); | |
50 | if (rc) { | |
51 | mutex_unlock(&server->srv_mutex); | |
52 | return rc; | |
53 | } | |
54 | rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); | |
55 | mutex_unlock(&server->srv_mutex); | |
56 | ||
57 | cFYI(1, "issued NT_CANCEL for mid %u, rc = %d", | |
58 | in_buf->Mid, rc); | |
59 | ||
60 | return rc; | |
61 | } | |
23db65f5 | 62 | |
55157dfb PS |
63 | static bool |
64 | cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) | |
65 | { | |
66 | return ob1->netfid == ob2->netfid; | |
67 | } | |
68 | ||
eb378711 PS |
69 | static unsigned int |
70 | cifs_read_data_offset(char *buf) | |
71 | { | |
72 | READ_RSP *rsp = (READ_RSP *)buf; | |
73 | return le16_to_cpu(rsp->DataOffset); | |
74 | } | |
75 | ||
76 | static unsigned int | |
77 | cifs_read_data_length(char *buf) | |
78 | { | |
79 | READ_RSP *rsp = (READ_RSP *)buf; | |
80 | return (le16_to_cpu(rsp->DataLengthHigh) << 16) + | |
81 | le16_to_cpu(rsp->DataLength); | |
82 | } | |
83 | ||
8aa26f3e PS |
84 | static struct mid_q_entry * |
85 | cifs_find_mid(struct TCP_Server_Info *server, char *buffer) | |
86 | { | |
87 | struct smb_hdr *buf = (struct smb_hdr *)buffer; | |
88 | struct mid_q_entry *mid; | |
89 | ||
90 | spin_lock(&GlobalMid_Lock); | |
91 | list_for_each_entry(mid, &server->pending_mid_q, qhead) { | |
92 | if (mid->mid == buf->Mid && | |
93 | mid->mid_state == MID_REQUEST_SUBMITTED && | |
94 | le16_to_cpu(mid->command) == buf->Command) { | |
95 | spin_unlock(&GlobalMid_Lock); | |
96 | return mid; | |
97 | } | |
98 | } | |
99 | spin_unlock(&GlobalMid_Lock); | |
100 | return NULL; | |
101 | } | |
102 | ||
45275789 | 103 | static void |
a891f0f8 PS |
104 | cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add, |
105 | const int optype) | |
45275789 PS |
106 | { |
107 | spin_lock(&server->req_lock); | |
108 | server->credits += add; | |
109 | server->in_flight--; | |
110 | spin_unlock(&server->req_lock); | |
111 | wake_up(&server->request_q); | |
112 | } | |
113 | ||
114 | static void | |
115 | cifs_set_credits(struct TCP_Server_Info *server, const int val) | |
116 | { | |
117 | spin_lock(&server->req_lock); | |
118 | server->credits = val; | |
119 | server->oplocks = val > 1 ? enable_oplocks : false; | |
120 | spin_unlock(&server->req_lock); | |
121 | } | |
122 | ||
123 | static int * | |
a891f0f8 | 124 | cifs_get_credits_field(struct TCP_Server_Info *server, const int optype) |
45275789 PS |
125 | { |
126 | return &server->credits; | |
127 | } | |
128 | ||
a891f0f8 PS |
129 | static unsigned int |
130 | cifs_get_credits(struct mid_q_entry *mid) | |
131 | { | |
132 | return 1; | |
133 | } | |
134 | ||
88257360 PS |
135 | /* |
136 | * Find a free multiplex id (SMB mid). Otherwise there could be | |
137 | * mid collisions which might cause problems, demultiplexing the | |
138 | * wrong response to this request. Multiplex ids could collide if | |
139 | * one of a series requests takes much longer than the others, or | |
140 | * if a very large number of long lived requests (byte range | |
141 | * locks or FindNotify requests) are pending. No more than | |
142 | * 64K-1 requests can be outstanding at one time. If no | |
143 | * mids are available, return zero. A future optimization | |
144 | * could make the combination of mids and uid the key we use | |
145 | * to demultiplex on (rather than mid alone). | |
146 | * In addition to the above check, the cifs demultiplex | |
147 | * code already used the command code as a secondary | |
148 | * check of the frame and if signing is negotiated the | |
149 | * response would be discarded if the mid were the same | |
150 | * but the signature was wrong. Since the mid is not put in the | |
151 | * pending queue until later (when it is about to be dispatched) | |
152 | * we do have to limit the number of outstanding requests | |
153 | * to somewhat less than 64K-1 although it is hard to imagine | |
154 | * so many threads being in the vfs at one time. | |
155 | */ | |
156 | static __u64 | |
157 | cifs_get_next_mid(struct TCP_Server_Info *server) | |
158 | { | |
159 | __u64 mid = 0; | |
160 | __u16 last_mid, cur_mid; | |
161 | bool collision; | |
162 | ||
163 | spin_lock(&GlobalMid_Lock); | |
164 | ||
165 | /* mid is 16 bit only for CIFS/SMB */ | |
166 | cur_mid = (__u16)((server->CurrentMid) & 0xffff); | |
167 | /* we do not want to loop forever */ | |
168 | last_mid = cur_mid; | |
169 | cur_mid++; | |
170 | ||
171 | /* | |
172 | * This nested loop looks more expensive than it is. | |
173 | * In practice the list of pending requests is short, | |
174 | * fewer than 50, and the mids are likely to be unique | |
175 | * on the first pass through the loop unless some request | |
176 | * takes longer than the 64 thousand requests before it | |
177 | * (and it would also have to have been a request that | |
178 | * did not time out). | |
179 | */ | |
180 | while (cur_mid != last_mid) { | |
181 | struct mid_q_entry *mid_entry; | |
182 | unsigned int num_mids; | |
183 | ||
184 | collision = false; | |
185 | if (cur_mid == 0) | |
186 | cur_mid++; | |
187 | ||
188 | num_mids = 0; | |
189 | list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { | |
190 | ++num_mids; | |
191 | if (mid_entry->mid == cur_mid && | |
192 | mid_entry->mid_state == MID_REQUEST_SUBMITTED) { | |
193 | /* This mid is in use, try a different one */ | |
194 | collision = true; | |
195 | break; | |
196 | } | |
197 | } | |
198 | ||
199 | /* | |
200 | * if we have more than 32k mids in the list, then something | |
201 | * is very wrong. Possibly a local user is trying to DoS the | |
202 | * box by issuing long-running calls and SIGKILL'ing them. If | |
203 | * we get to 2^16 mids then we're in big trouble as this | |
204 | * function could loop forever. | |
205 | * | |
206 | * Go ahead and assign out the mid in this situation, but force | |
207 | * an eventual reconnect to clean out the pending_mid_q. | |
208 | */ | |
209 | if (num_mids > 32768) | |
210 | server->tcpStatus = CifsNeedReconnect; | |
211 | ||
212 | if (!collision) { | |
213 | mid = (__u64)cur_mid; | |
214 | server->CurrentMid = mid; | |
215 | break; | |
216 | } | |
217 | cur_mid++; | |
218 | } | |
219 | spin_unlock(&GlobalMid_Lock); | |
220 | return mid; | |
221 | } | |
222 | ||
316cf94a PS |
223 | /* |
224 | return codes: | |
225 | 0 not a transact2, or all data present | |
226 | >0 transact2 with that much data missing | |
227 | -EINVAL invalid transact2 | |
228 | */ | |
229 | static int | |
230 | check2ndT2(char *buf) | |
231 | { | |
232 | struct smb_hdr *pSMB = (struct smb_hdr *)buf; | |
233 | struct smb_t2_rsp *pSMBt; | |
234 | int remaining; | |
235 | __u16 total_data_size, data_in_this_rsp; | |
236 | ||
237 | if (pSMB->Command != SMB_COM_TRANSACTION2) | |
238 | return 0; | |
239 | ||
240 | /* check for plausible wct, bcc and t2 data and parm sizes */ | |
241 | /* check for parm and data offset going beyond end of smb */ | |
242 | if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */ | |
243 | cFYI(1, "invalid transact2 word count"); | |
244 | return -EINVAL; | |
245 | } | |
246 | ||
247 | pSMBt = (struct smb_t2_rsp *)pSMB; | |
248 | ||
249 | total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); | |
250 | data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); | |
251 | ||
252 | if (total_data_size == data_in_this_rsp) | |
253 | return 0; | |
254 | else if (total_data_size < data_in_this_rsp) { | |
255 | cFYI(1, "total data %d smaller than data in frame %d", | |
256 | total_data_size, data_in_this_rsp); | |
257 | return -EINVAL; | |
258 | } | |
259 | ||
260 | remaining = total_data_size - data_in_this_rsp; | |
261 | ||
262 | cFYI(1, "missing %d bytes from transact2, check next response", | |
263 | remaining); | |
264 | if (total_data_size > CIFSMaxBufSize) { | |
265 | cERROR(1, "TotalDataSize %d is over maximum buffer %d", | |
266 | total_data_size, CIFSMaxBufSize); | |
267 | return -EINVAL; | |
268 | } | |
269 | return remaining; | |
270 | } | |
271 | ||
272 | static int | |
273 | coalesce_t2(char *second_buf, struct smb_hdr *target_hdr) | |
274 | { | |
275 | struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf; | |
276 | struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr; | |
277 | char *data_area_of_tgt; | |
278 | char *data_area_of_src; | |
279 | int remaining; | |
280 | unsigned int byte_count, total_in_tgt; | |
281 | __u16 tgt_total_cnt, src_total_cnt, total_in_src; | |
282 | ||
283 | src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount); | |
284 | tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); | |
285 | ||
286 | if (tgt_total_cnt != src_total_cnt) | |
287 | cFYI(1, "total data count of primary and secondary t2 differ " | |
288 | "source=%hu target=%hu", src_total_cnt, tgt_total_cnt); | |
289 | ||
290 | total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); | |
291 | ||
292 | remaining = tgt_total_cnt - total_in_tgt; | |
293 | ||
294 | if (remaining < 0) { | |
295 | cFYI(1, "Server sent too much data. tgt_total_cnt=%hu " | |
296 | "total_in_tgt=%hu", tgt_total_cnt, total_in_tgt); | |
297 | return -EPROTO; | |
298 | } | |
299 | ||
300 | if (remaining == 0) { | |
301 | /* nothing to do, ignore */ | |
302 | cFYI(1, "no more data remains"); | |
303 | return 0; | |
304 | } | |
305 | ||
306 | total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); | |
307 | if (remaining < total_in_src) | |
308 | cFYI(1, "transact2 2nd response contains too much data"); | |
309 | ||
310 | /* find end of first SMB data area */ | |
311 | data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + | |
312 | get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); | |
313 | ||
314 | /* validate target area */ | |
315 | data_area_of_src = (char *)&pSMBs->hdr.Protocol + | |
316 | get_unaligned_le16(&pSMBs->t2_rsp.DataOffset); | |
317 | ||
318 | data_area_of_tgt += total_in_tgt; | |
319 | ||
320 | total_in_tgt += total_in_src; | |
321 | /* is the result too big for the field? */ | |
322 | if (total_in_tgt > USHRT_MAX) { | |
323 | cFYI(1, "coalesced DataCount too large (%u)", total_in_tgt); | |
324 | return -EPROTO; | |
325 | } | |
326 | put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); | |
327 | ||
328 | /* fix up the BCC */ | |
329 | byte_count = get_bcc(target_hdr); | |
330 | byte_count += total_in_src; | |
331 | /* is the result too big for the field? */ | |
332 | if (byte_count > USHRT_MAX) { | |
333 | cFYI(1, "coalesced BCC too large (%u)", byte_count); | |
334 | return -EPROTO; | |
335 | } | |
336 | put_bcc(byte_count, target_hdr); | |
337 | ||
338 | byte_count = be32_to_cpu(target_hdr->smb_buf_length); | |
339 | byte_count += total_in_src; | |
340 | /* don't allow buffer to overflow */ | |
341 | if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { | |
342 | cFYI(1, "coalesced BCC exceeds buffer size (%u)", byte_count); | |
343 | return -ENOBUFS; | |
344 | } | |
345 | target_hdr->smb_buf_length = cpu_to_be32(byte_count); | |
346 | ||
347 | /* copy second buffer into end of first buffer */ | |
348 | memcpy(data_area_of_tgt, data_area_of_src, total_in_src); | |
349 | ||
350 | if (remaining != total_in_src) { | |
351 | /* more responses to go */ | |
352 | cFYI(1, "waiting for more secondary responses"); | |
353 | return 1; | |
354 | } | |
355 | ||
356 | /* we are done */ | |
357 | cFYI(1, "found the last secondary response"); | |
358 | return 0; | |
359 | } | |
360 | ||
361 | static bool | |
362 | cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server, | |
363 | char *buf, int malformed) | |
364 | { | |
365 | if (malformed) | |
366 | return false; | |
367 | if (check2ndT2(buf) <= 0) | |
368 | return false; | |
369 | mid->multiRsp = true; | |
370 | if (mid->resp_buf) { | |
371 | /* merge response - fix up 1st*/ | |
372 | malformed = coalesce_t2(buf, mid->resp_buf); | |
373 | if (malformed > 0) | |
374 | return true; | |
375 | /* All parts received or packet is malformed. */ | |
376 | mid->multiEnd = true; | |
377 | dequeue_mid(mid, malformed); | |
378 | return true; | |
379 | } | |
380 | if (!server->large_buf) { | |
381 | /*FIXME: switch to already allocated largebuf?*/ | |
382 | cERROR(1, "1st trans2 resp needs bigbuf"); | |
383 | } else { | |
384 | /* Have first buffer */ | |
385 | mid->resp_buf = buf; | |
386 | mid->large_buf = true; | |
387 | server->bigbuf = NULL; | |
388 | } | |
389 | return true; | |
390 | } | |
391 | ||
286170aa PS |
392 | static bool |
393 | cifs_need_neg(struct TCP_Server_Info *server) | |
394 | { | |
395 | return server->maxBuf == 0; | |
396 | } | |
397 | ||
398 | static int | |
399 | cifs_negotiate(const unsigned int xid, struct cifs_ses *ses) | |
400 | { | |
401 | int rc; | |
402 | rc = CIFSSMBNegotiate(xid, ses); | |
403 | if (rc == -EAGAIN) { | |
404 | /* retry only once on 1st time connection */ | |
405 | set_credits(ses->server, 1); | |
406 | rc = CIFSSMBNegotiate(xid, ses); | |
407 | if (rc == -EAGAIN) | |
408 | rc = -EHOSTDOWN; | |
409 | } | |
410 | return rc; | |
411 | } | |
412 | ||
af4281dc PS |
413 | static void |
414 | cifs_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) | |
415 | { | |
416 | CIFSSMBQFSDeviceInfo(xid, tcon); | |
417 | CIFSSMBQFSAttributeInfo(xid, tcon); | |
418 | } | |
419 | ||
68889f26 PS |
420 | static int |
421 | cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, | |
422 | struct cifs_sb_info *cifs_sb, const char *full_path) | |
423 | { | |
424 | int rc; | |
425 | FILE_ALL_INFO *file_info; | |
426 | ||
427 | file_info = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); | |
428 | if (file_info == NULL) | |
429 | return -ENOMEM; | |
430 | ||
431 | rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info, | |
432 | 0 /* not legacy */, cifs_sb->local_nls, | |
433 | cifs_sb->mnt_cifs_flags & | |
434 | CIFS_MOUNT_MAP_SPECIAL_CHR); | |
435 | ||
436 | if (rc == -EOPNOTSUPP || rc == -EINVAL) | |
437 | rc = SMBQueryInformation(xid, tcon, full_path, file_info, | |
438 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & | |
439 | CIFS_MOUNT_MAP_SPECIAL_CHR); | |
440 | kfree(file_info); | |
441 | return rc; | |
442 | } | |
443 | ||
23db65f5 | 444 | struct smb_version_operations smb1_operations = { |
121b046a | 445 | .send_cancel = send_nt_cancel, |
55157dfb | 446 | .compare_fids = cifs_compare_fids, |
082d0642 PS |
447 | .setup_request = cifs_setup_request, |
448 | .check_receive = cifs_check_receive, | |
45275789 PS |
449 | .add_credits = cifs_add_credits, |
450 | .set_credits = cifs_set_credits, | |
451 | .get_credits_field = cifs_get_credits_field, | |
a891f0f8 | 452 | .get_credits = cifs_get_credits, |
88257360 | 453 | .get_next_mid = cifs_get_next_mid, |
eb378711 PS |
454 | .read_data_offset = cifs_read_data_offset, |
455 | .read_data_length = cifs_read_data_length, | |
456 | .map_error = map_smb_to_linux_error, | |
8aa26f3e PS |
457 | .find_mid = cifs_find_mid, |
458 | .check_message = checkSMB, | |
459 | .dump_detail = cifs_dump_detail, | |
460 | .is_oplock_break = is_valid_oplock_break, | |
316cf94a | 461 | .check_trans2 = cifs_check_trans2, |
286170aa PS |
462 | .need_neg = cifs_need_neg, |
463 | .negotiate = cifs_negotiate, | |
58c45c58 PS |
464 | .sess_setup = CIFS_SessSetup, |
465 | .logoff = CIFSSMBLogoff, | |
2e6e02ab PS |
466 | .tree_connect = CIFSTCon, |
467 | .tree_disconnect = CIFSSMBTDis, | |
b669f33c | 468 | .get_dfs_refer = CIFSGetDFSRefer, |
af4281dc | 469 | .qfs_tcon = cifs_qfs_tcon, |
68889f26 | 470 | .is_path_accessible = cifs_is_path_accessible, |
23db65f5 JL |
471 | }; |
472 | ||
473 | struct smb_version_values smb1_values = { | |
474 | .version_string = SMB1_VERSION_STRING, | |
106dc538 PS |
475 | .large_lock_type = LOCKING_ANDX_LARGE_FILES, |
476 | .exclusive_lock_type = 0, | |
477 | .shared_lock_type = LOCKING_ANDX_SHARED_LOCK, | |
478 | .unlock_lock_type = 0, | |
1887f601 PS |
479 | .header_size = sizeof(struct smb_hdr), |
480 | .max_header_size = MAX_CIFS_HDR_SIZE, | |
eb378711 | 481 | .read_rsp_size = sizeof(READ_RSP), |
2dc7e1c0 | 482 | .lock_cmd = cpu_to_le16(SMB_COM_LOCKING_ANDX), |
23db65f5 | 483 | }; |