aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/misc.c
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-17 16:15:55 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-17 16:15:55 -0500
commit8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch)
treea8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /fs/cifs/misc.c
parent406089d01562f1e2bf9f089fd7637009ebaad589 (diff)
Patched in Tegra support.
Diffstat (limited to 'fs/cifs/misc.c')
-rw-r--r--fs/cifs/misc.c269
1 files changed, 168 insertions, 101 deletions
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 3a00c0d0cea..7c169339259 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -29,9 +29,6 @@
29#include "smberr.h" 29#include "smberr.h"
30#include "nterr.h" 30#include "nterr.h"
31#include "cifs_unicode.h" 31#include "cifs_unicode.h"
32#ifdef CONFIG_CIFS_SMB2
33#include "smb2pdu.h"
34#endif
35 32
36extern mempool_t *cifs_sm_req_poolp; 33extern mempool_t *cifs_sm_req_poolp;
37extern mempool_t *cifs_req_poolp; 34extern mempool_t *cifs_req_poolp;
@@ -43,7 +40,7 @@ extern mempool_t *cifs_req_poolp;
43 since the cifs fs was mounted */ 40 since the cifs fs was mounted */
44 41
45unsigned int 42unsigned int
46_get_xid(void) 43_GetXid(void)
47{ 44{
48 unsigned int xid; 45 unsigned int xid;
49 46
@@ -61,7 +58,7 @@ _get_xid(void)
61} 58}
62 59
63void 60void
64_free_xid(unsigned int xid) 61_FreeXid(unsigned int xid)
65{ 62{
66 spin_lock(&GlobalMid_Lock); 63 spin_lock(&GlobalMid_Lock);
67 /* if (GlobalTotalActiveXid == 0) 64 /* if (GlobalTotalActiveXid == 0)
@@ -146,27 +143,17 @@ struct smb_hdr *
146cifs_buf_get(void) 143cifs_buf_get(void)
147{ 144{
148 struct smb_hdr *ret_buf = NULL; 145 struct smb_hdr *ret_buf = NULL;
149 size_t buf_size = sizeof(struct smb_hdr); 146
150 147/* We could use negotiated size instead of max_msgsize -
151#ifdef CONFIG_CIFS_SMB2 148 but it may be more efficient to always alloc same size
152 /* 149 albeit slightly larger than necessary and maxbuffersize
153 * SMB2 header is bigger than CIFS one - no problems to clean some 150 defaults to this and can not be bigger */
154 * more bytes for CIFS.
155 */
156 buf_size = sizeof(struct smb2_hdr);
157#endif
158 /*
159 * We could use negotiated size instead of max_msgsize -
160 * but it may be more efficient to always alloc same size
161 * albeit slightly larger than necessary and maxbuffersize
162 * defaults to this and can not be bigger.
163 */
164 ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS); 151 ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS);
165 152
166 /* clear the first few header bytes */ 153 /* clear the first few header bytes */
167 /* for most paths, more is cleared in header_assemble */ 154 /* for most paths, more is cleared in header_assemble */
168 if (ret_buf) { 155 if (ret_buf) {
169 memset(ret_buf, 0, buf_size + 3); 156 memset(ret_buf, 0, sizeof(struct smb_hdr) + 3);
170 atomic_inc(&bufAllocCount); 157 atomic_inc(&bufAllocCount);
171#ifdef CONFIG_CIFS_STATS2 158#ifdef CONFIG_CIFS_STATS2
172 atomic_inc(&totBufAllocCount); 159 atomic_inc(&totBufAllocCount);
@@ -225,6 +212,85 @@ cifs_small_buf_release(void *buf_to_free)
225 return; 212 return;
226} 213}
227 214
215/*
216 Find a free multiplex id (SMB mid). Otherwise there could be
217 mid collisions which might cause problems, demultiplexing the
218 wrong response to this request. Multiplex ids could collide if
219 one of a series requests takes much longer than the others, or
220 if a very large number of long lived requests (byte range
221 locks or FindNotify requests) are pending. No more than
222 64K-1 requests can be outstanding at one time. If no
223 mids are available, return zero. A future optimization
224 could make the combination of mids and uid the key we use
225 to demultiplex on (rather than mid alone).
226 In addition to the above check, the cifs demultiplex
227 code already used the command code as a secondary
228 check of the frame and if signing is negotiated the
229 response would be discarded if the mid were the same
230 but the signature was wrong. Since the mid is not put in the
231 pending queue until later (when it is about to be dispatched)
232 we do have to limit the number of outstanding requests
233 to somewhat less than 64K-1 although it is hard to imagine
234 so many threads being in the vfs at one time.
235*/
236__u16 GetNextMid(struct TCP_Server_Info *server)
237{
238 __u16 mid = 0;
239 __u16 last_mid;
240 bool collision;
241
242 spin_lock(&GlobalMid_Lock);
243 last_mid = server->CurrentMid; /* we do not want to loop forever */
244 server->CurrentMid++;
245 /* This nested loop looks more expensive than it is.
246 In practice the list of pending requests is short,
247 fewer than 50, and the mids are likely to be unique
248 on the first pass through the loop unless some request
249 takes longer than the 64 thousand requests before it
250 (and it would also have to have been a request that
251 did not time out) */
252 while (server->CurrentMid != last_mid) {
253 struct mid_q_entry *mid_entry;
254 unsigned int num_mids;
255
256 collision = false;
257 if (server->CurrentMid == 0)
258 server->CurrentMid++;
259
260 num_mids = 0;
261 list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
262 ++num_mids;
263 if (mid_entry->mid == server->CurrentMid &&
264 mid_entry->midState == MID_REQUEST_SUBMITTED) {
265 /* This mid is in use, try a different one */
266 collision = true;
267 break;
268 }
269 }
270
271 /*
272 * if we have more than 32k mids in the list, then something
273 * is very wrong. Possibly a local user is trying to DoS the
274 * box by issuing long-running calls and SIGKILL'ing them. If
275 * we get to 2^16 mids then we're in big trouble as this
276 * function could loop forever.
277 *
278 * Go ahead and assign out the mid in this situation, but force
279 * an eventual reconnect to clean out the pending_mid_q.
280 */
281 if (num_mids > 32768)
282 server->tcpStatus = CifsNeedReconnect;
283
284 if (!collision) {
285 mid = server->CurrentMid;
286 break;
287 }
288 server->CurrentMid++;
289 }
290 spin_unlock(&GlobalMid_Lock);
291 return mid;
292}
293
228/* NB: MID can not be set if treeCon not passed in, in that 294/* NB: MID can not be set if treeCon not passed in, in that
229 case it is responsbility of caller to set the mid */ 295 case it is responsbility of caller to set the mid */
230void 296void
@@ -232,6 +298,8 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
232 const struct cifs_tcon *treeCon, int word_count 298 const struct cifs_tcon *treeCon, int word_count
233 /* length of fixed section (word count) in two byte units */) 299 /* length of fixed section (word count) in two byte units */)
234{ 300{
301 struct list_head *temp_item;
302 struct cifs_ses *ses;
235 char *temp = (char *) buffer; 303 char *temp = (char *) buffer;
236 304
237 memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ 305 memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */
@@ -260,7 +328,52 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
260 328
261 /* Uid is not converted */ 329 /* Uid is not converted */
262 buffer->Uid = treeCon->ses->Suid; 330 buffer->Uid = treeCon->ses->Suid;
263 buffer->Mid = get_next_mid(treeCon->ses->server); 331 buffer->Mid = GetNextMid(treeCon->ses->server);
332 if (multiuser_mount != 0) {
333 /* For the multiuser case, there are few obvious technically */
334 /* possible mechanisms to match the local linux user (uid) */
335 /* to a valid remote smb user (smb_uid): */
336 /* 1) Query Winbind (or other local pam/nss daemon */
337 /* for userid/password/logon_domain or credential */
338 /* 2) Query Winbind for uid to sid to username mapping */
339 /* and see if we have a matching password for existing*/
340 /* session for that user perhas getting password by */
341 /* adding a new pam_cifs module that stores passwords */
342 /* so that the cifs vfs can get at that for all logged*/
343 /* on users */
344 /* 3) (Which is the mechanism we have chosen) */
345 /* Search through sessions to the same server for a */
346 /* a match on the uid that was passed in on mount */
347 /* with the current processes uid (or euid?) and use */
348 /* that smb uid. If no existing smb session for */
349 /* that uid found, use the default smb session ie */
350 /* the smb session for the volume mounted which is */
351 /* the same as would be used if the multiuser mount */
352 /* flag were disabled. */
353
354 /* BB Add support for establishing new tCon and SMB Session */
355 /* with userid/password pairs found on the smb session */
356 /* for other target tcp/ip addresses BB */
357 if (current_fsuid() != treeCon->ses->linux_uid) {
358 cFYI(1, "Multiuser mode and UID "
359 "did not match tcon uid");
360 spin_lock(&cifs_tcp_ses_lock);
361 list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
362 ses = list_entry(temp_item, struct cifs_ses, smb_ses_list);
363 if (ses->linux_uid == current_fsuid()) {
364 if (ses->server == treeCon->ses->server) {
365 cFYI(1, "found matching uid substitute right smb_uid");
366 buffer->Uid = ses->Suid;
367 break;
368 } else {
369 /* BB eventually call cifs_setup_session here */
370 cFYI(1, "local UID found but no smb sess with this server exists");
371 }
372 }
373 }
374 spin_unlock(&cifs_tcp_ses_lock);
375 }
376 }
264 } 377 }
265 if (treeCon->Flags & SMB_SHARE_IS_IN_DFS) 378 if (treeCon->Flags & SMB_SHARE_IS_IN_DFS)
266 buffer->Flags2 |= SMBFLG2_DFS; 379 buffer->Flags2 |= SMBFLG2_DFS;
@@ -307,24 +420,19 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid)
307} 420}
308 421
309int 422int
310checkSMB(char *buf, unsigned int total_read) 423checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length)
311{ 424{
312 struct smb_hdr *smb = (struct smb_hdr *)buf; 425 __u32 len = be32_to_cpu(smb->smb_buf_length);
313 __u16 mid = smb->Mid;
314 __u32 rfclen = be32_to_cpu(smb->smb_buf_length);
315 __u32 clc_len; /* calculated length */ 426 __u32 clc_len; /* calculated length */
316 cFYI(0, "checkSMB Length: 0x%x, smb_buf_length: 0x%x", 427 cFYI(0, "checkSMB Length: 0x%x, smb_buf_length: 0x%x", length, len);
317 total_read, rfclen);
318 428
319 /* is this frame too small to even get to a BCC? */ 429 if (length < 2 + sizeof(struct smb_hdr)) {
320 if (total_read < 2 + sizeof(struct smb_hdr)) { 430 if ((length >= sizeof(struct smb_hdr) - 1)
321 if ((total_read >= sizeof(struct smb_hdr) - 1)
322 && (smb->Status.CifsError != 0)) { 431 && (smb->Status.CifsError != 0)) {
323 /* it's an error return */
324 smb->WordCount = 0; 432 smb->WordCount = 0;
325 /* some error cases do not return wct and bcc */ 433 /* some error cases do not return wct and bcc */
326 return 0; 434 return 0;
327 } else if ((total_read == sizeof(struct smb_hdr) + 1) && 435 } else if ((length == sizeof(struct smb_hdr) + 1) &&
328 (smb->WordCount == 0)) { 436 (smb->WordCount == 0)) {
329 char *tmp = (char *)smb; 437 char *tmp = (char *)smb;
330 /* Need to work around a bug in two servers here */ 438 /* Need to work around a bug in two servers here */
@@ -344,35 +452,39 @@ checkSMB(char *buf, unsigned int total_read)
344 } else { 452 } else {
345 cERROR(1, "Length less than smb header size"); 453 cERROR(1, "Length less than smb header size");
346 } 454 }
347 return -EIO; 455 return 1;
456 }
457 if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
458 cERROR(1, "smb length greater than MaxBufSize, mid=%d",
459 smb->Mid);
460 return 1;
348 } 461 }
349 462
350 /* otherwise, there is enough to get to the BCC */
351 if (check_smb_hdr(smb, mid)) 463 if (check_smb_hdr(smb, mid))
352 return -EIO; 464 return 1;
353 clc_len = smbCalcSize(smb); 465 clc_len = smbCalcSize(smb);
354 466
355 if (4 + rfclen != total_read) { 467 if (4 + len != length) {
356 cERROR(1, "Length read does not match RFC1001 length %d", 468 cERROR(1, "Length read does not match RFC1001 length %d",
357 rfclen); 469 len);
358 return -EIO; 470 return 1;
359 } 471 }
360 472
361 if (4 + rfclen != clc_len) { 473 if (4 + len != clc_len) {
362 /* check if bcc wrapped around for large read responses */ 474 /* check if bcc wrapped around for large read responses */
363 if ((rfclen > 64 * 1024) && (rfclen > clc_len)) { 475 if ((len > 64 * 1024) && (len > clc_len)) {
364 /* check if lengths match mod 64K */ 476 /* check if lengths match mod 64K */
365 if (((4 + rfclen) & 0xFFFF) == (clc_len & 0xFFFF)) 477 if (((4 + len) & 0xFFFF) == (clc_len & 0xFFFF))
366 return 0; /* bcc wrapped */ 478 return 0; /* bcc wrapped */
367 } 479 }
368 cFYI(1, "Calculated size %u vs length %u mismatch for mid=%u", 480 cFYI(1, "Calculated size %u vs length %u mismatch for mid=%u",
369 clc_len, 4 + rfclen, smb->Mid); 481 clc_len, 4 + len, smb->Mid);
370 482
371 if (4 + rfclen < clc_len) { 483 if (4 + len < clc_len) {
372 cERROR(1, "RFC1001 size %u smaller than SMB for mid=%u", 484 cERROR(1, "RFC1001 size %u smaller than SMB for mid=%u",
373 rfclen, smb->Mid); 485 len, smb->Mid);
374 return -EIO; 486 return 1;
375 } else if (rfclen > clc_len + 512) { 487 } else if (len > clc_len + 512) {
376 /* 488 /*
377 * Some servers (Windows XP in particular) send more 489 * Some servers (Windows XP in particular) send more
378 * data than the lengths in the SMB packet would 490 * data than the lengths in the SMB packet would
@@ -383,17 +495,16 @@ checkSMB(char *buf, unsigned int total_read)
383 * data to 512 bytes. 495 * data to 512 bytes.
384 */ 496 */
385 cERROR(1, "RFC1001 size %u more than 512 bytes larger " 497 cERROR(1, "RFC1001 size %u more than 512 bytes larger "
386 "than SMB for mid=%u", rfclen, smb->Mid); 498 "than SMB for mid=%u", len, smb->Mid);
387 return -EIO; 499 return 1;
388 } 500 }
389 } 501 }
390 return 0; 502 return 0;
391} 503}
392 504
393bool 505bool
394is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) 506is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
395{ 507{
396 struct smb_hdr *buf = (struct smb_hdr *)buffer;
397 struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; 508 struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;
398 struct list_head *tmp, *tmp1, *tmp2; 509 struct list_head *tmp, *tmp1, *tmp2;
399 struct cifs_ses *ses; 510 struct cifs_ses *ses;
@@ -461,12 +572,12 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
461 if (tcon->tid != buf->Tid) 572 if (tcon->tid != buf->Tid)
462 continue; 573 continue;
463 574
464 cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); 575 cifs_stats_inc(&tcon->num_oplock_brks);
465 spin_lock(&cifs_file_list_lock); 576 spin_lock(&cifs_file_list_lock);
466 list_for_each(tmp2, &tcon->openFileList) { 577 list_for_each(tmp2, &tcon->openFileList) {
467 netfile = list_entry(tmp2, struct cifsFileInfo, 578 netfile = list_entry(tmp2, struct cifsFileInfo,
468 tlist); 579 tlist);
469 if (pSMB->Fid != netfile->fid.netfid) 580 if (pSMB->Fid != netfile->netfid)
470 continue; 581 continue;
471 582
472 cFYI(1, "file id match, oplock break"); 583 cFYI(1, "file id match, oplock break");
@@ -474,7 +585,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
474 585
475 cifs_set_oplock_level(pCifsInode, 586 cifs_set_oplock_level(pCifsInode,
476 pSMB->OplockLevel ? OPLOCK_READ : 0); 587 pSMB->OplockLevel ? OPLOCK_READ : 0);
477 queue_work(cifsiod_wq, 588 queue_work(system_nrt_wq,
478 &netfile->oplock_break); 589 &netfile->oplock_break);
479 netfile->oplock_break_cancelled = false; 590 netfile->oplock_break_cancelled = false;
480 591
@@ -494,15 +605,16 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
494} 605}
495 606
496void 607void
497dump_smb(void *buf, int smb_buf_length) 608dump_smb(struct smb_hdr *smb_buf, int smb_buf_length)
498{ 609{
499 int i, j; 610 int i, j;
500 char debug_line[17]; 611 char debug_line[17];
501 unsigned char *buffer = buf; 612 unsigned char *buffer;
502 613
503 if (traceSMB == 0) 614 if (traceSMB == 0)
504 return; 615 return;
505 616
617 buffer = (unsigned char *) smb_buf;
506 for (i = 0, j = 0; i < smb_buf_length; i++, j++) { 618 for (i = 0, j = 0; i < smb_buf_length; i++, j++) {
507 if (i % 8 == 0) { 619 if (i % 8 == 0) {
508 /* have reached the beginning of line */ 620 /* have reached the beginning of line */
@@ -564,48 +676,3 @@ void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
564 cinode->clientCanCacheRead = false; 676 cinode->clientCanCacheRead = false;
565 } 677 }
566} 678}
567
568bool
569backup_cred(struct cifs_sb_info *cifs_sb)
570{
571 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) {
572 if (cifs_sb->mnt_backupuid == current_fsuid())
573 return true;
574 }
575 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) {
576 if (in_group_p(cifs_sb->mnt_backupgid))
577 return true;
578 }
579
580 return false;
581}
582
583void
584cifs_del_pending_open(struct cifs_pending_open *open)
585{
586 spin_lock(&cifs_file_list_lock);
587 list_del(&open->olist);
588 spin_unlock(&cifs_file_list_lock);
589}
590
591void
592cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink,
593 struct cifs_pending_open *open)
594{
595#ifdef CONFIG_CIFS_SMB2
596 memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE);
597#endif
598 open->oplock = CIFS_OPLOCK_NO_CHANGE;
599 open->tlink = tlink;
600 fid->pending_open = open;
601 list_add_tail(&open->olist, &tlink_tcon(tlink)->pending_opens);
602}
603
604void
605cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
606 struct cifs_pending_open *open)
607{
608 spin_lock(&cifs_file_list_lock);
609 cifs_add_pending_open_locked(fid, tlink, open);
610 spin_unlock(&cifs_file_list_lock);
611}