aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorPavel Shilovsky <pshilovsky@samba.org>2013-09-05 07:00:07 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-09-26 20:18:05 -0400
commitb08d9b572074b3d0899349070bb61688ad6eb630 (patch)
tree43c1a0e91c273349b80bacc7f6156726291a2d72 /fs/cifs
parentaf66f40c4cb1d31b3b403d5f9a8471261a0cc945 (diff)
CIFS: Fix missing lease break
commit 933d4b36576c951d0371bbfed05ec0135d516a6e upstream. If a server sends a lease break to a connection that doesn't have opens with a lease key specified in the server response, we can't find an open file to send an ack. Fix this by walking through all connections we have. Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org> Signed-off-by: Steve French <smfrench@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/smb2misc.c149
1 files changed, 80 insertions, 69 deletions
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 2b1dc7f4464a..4f791e0e98d7 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -413,97 +413,108 @@ cifs_ses_oplock_break(struct work_struct *work)
413} 413}
414 414
415static bool 415static bool
416smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) 416smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
417 struct smb2_lease_break_work *lw)
417{ 418{
418 struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer; 419 bool found;
419 struct list_head *tmp, *tmp1, *tmp2; 420 __u8 lease_state;
420 struct cifs_ses *ses; 421 struct list_head *tmp;
421 struct cifs_tcon *tcon;
422 struct cifsInodeInfo *cinode;
423 struct cifsFileInfo *cfile; 422 struct cifsFileInfo *cfile;
424 struct cifs_pending_open *open; 423 struct cifs_pending_open *open;
425 struct smb2_lease_break_work *lw; 424 struct cifsInodeInfo *cinode;
426 bool found;
427 int ack_req = le32_to_cpu(rsp->Flags & 425 int ack_req = le32_to_cpu(rsp->Flags &
428 SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); 426 SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED);
429 427
430 lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); 428 lease_state = smb2_map_lease_to_oplock(rsp->NewLeaseState);
431 if (!lw)
432 return false;
433 429
434 INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); 430 list_for_each(tmp, &tcon->openFileList) {
435 lw->lease_state = rsp->NewLeaseState; 431 cfile = list_entry(tmp, struct cifsFileInfo, tlist);
432 cinode = CIFS_I(cfile->dentry->d_inode);
436 433
437 cifs_dbg(FYI, "Checking for lease break\n"); 434 if (memcmp(cinode->lease_key, rsp->LeaseKey,
435 SMB2_LEASE_KEY_SIZE))
436 continue;
438 437
439 /* look up tcon based on tid & uid */ 438 cifs_dbg(FYI, "found in the open list\n");
440 spin_lock(&cifs_tcp_ses_lock); 439 cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
441 list_for_each(tmp, &server->smb_ses_list) { 440 le32_to_cpu(rsp->NewLeaseState));
442 ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
443 441
444 spin_lock(&cifs_file_list_lock); 442 smb2_set_oplock_level(cinode, lease_state);
445 list_for_each(tmp1, &ses->tcon_list) {
446 tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
447 443
448 cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); 444 if (ack_req)
449 list_for_each(tmp2, &tcon->openFileList) { 445 cfile->oplock_break_cancelled = false;
450 cfile = list_entry(tmp2, struct cifsFileInfo, 446 else
451 tlist); 447 cfile->oplock_break_cancelled = true;
452 cinode = CIFS_I(cfile->dentry->d_inode);
453 448
454 if (memcmp(cinode->lease_key, rsp->LeaseKey, 449 queue_work(cifsiod_wq, &cfile->oplock_break);
455 SMB2_LEASE_KEY_SIZE)) 450 kfree(lw);
456 continue; 451 return true;
452 }
457 453
458 cifs_dbg(FYI, "found in the open list\n"); 454 found = false;
459 cifs_dbg(FYI, "lease key match, lease break 0x%d\n", 455 list_for_each_entry(open, &tcon->pending_opens, olist) {
460 le32_to_cpu(rsp->NewLeaseState)); 456 if (memcmp(open->lease_key, rsp->LeaseKey,
457 SMB2_LEASE_KEY_SIZE))
458 continue;
459
460 if (!found && ack_req) {
461 found = true;
462 memcpy(lw->lease_key, open->lease_key,
463 SMB2_LEASE_KEY_SIZE);
464 lw->tlink = cifs_get_tlink(open->tlink);
465 queue_work(cifsiod_wq, &lw->lease_break);
466 }
461 467
462 smb2_set_oplock_level(cinode, 468 cifs_dbg(FYI, "found in the pending open list\n");
463 smb2_map_lease_to_oplock(rsp->NewLeaseState)); 469 cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
470 le32_to_cpu(rsp->NewLeaseState));
464 471
465 if (ack_req) 472 open->oplock = lease_state;
466 cfile->oplock_break_cancelled = false; 473 }
467 else 474 return found;
468 cfile->oplock_break_cancelled = true; 475}
469 476
470 queue_work(cifsiod_wq, &cfile->oplock_break); 477static bool
478smb2_is_valid_lease_break(char *buffer)
479{
480 struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer;
481 struct list_head *tmp, *tmp1, *tmp2;
482 struct TCP_Server_Info *server;
483 struct cifs_ses *ses;
484 struct cifs_tcon *tcon;
485 struct smb2_lease_break_work *lw;
471 486
472 kfree(lw); 487 lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
473 spin_unlock(&cifs_file_list_lock); 488 if (!lw)
474 spin_unlock(&cifs_tcp_ses_lock); 489 return false;
475 return true;
476 }
477 490
478 found = false; 491 INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
479 list_for_each_entry(open, &tcon->pending_opens, olist) { 492 lw->lease_state = rsp->NewLeaseState;
480 if (memcmp(open->lease_key, rsp->LeaseKey,
481 SMB2_LEASE_KEY_SIZE))
482 continue;
483 493
484 if (!found && ack_req) { 494 cifs_dbg(FYI, "Checking for lease break\n");
485 found = true; 495
486 memcpy(lw->lease_key, open->lease_key, 496 /* look up tcon based on tid & uid */
487 SMB2_LEASE_KEY_SIZE); 497 spin_lock(&cifs_tcp_ses_lock);
488 lw->tlink = cifs_get_tlink(open->tlink); 498 list_for_each(tmp, &cifs_tcp_ses_list) {
489 queue_work(cifsiod_wq, 499 server = list_entry(tmp, struct TCP_Server_Info, tcp_ses_list);
490 &lw->lease_break);
491 }
492 500
493 cifs_dbg(FYI, "found in the pending open list\n"); 501 list_for_each(tmp1, &server->smb_ses_list) {
494 cifs_dbg(FYI, "lease key match, lease break 0x%d\n", 502 ses = list_entry(tmp1, struct cifs_ses, smb_ses_list);
495 le32_to_cpu(rsp->NewLeaseState));
496 503
497 open->oplock = 504 spin_lock(&cifs_file_list_lock);
498 smb2_map_lease_to_oplock(rsp->NewLeaseState); 505 list_for_each(tmp2, &ses->tcon_list) {
499 } 506 tcon = list_entry(tmp2, struct cifs_tcon,
500 if (found) { 507 tcon_list);
501 spin_unlock(&cifs_file_list_lock); 508 cifs_stats_inc(
502 spin_unlock(&cifs_tcp_ses_lock); 509 &tcon->stats.cifs_stats.num_oplock_brks);
503 return true; 510 if (smb2_tcon_has_lease(tcon, rsp, lw)) {
511 spin_unlock(&cifs_file_list_lock);
512 spin_unlock(&cifs_tcp_ses_lock);
513 return true;
514 }
504 } 515 }
516 spin_unlock(&cifs_file_list_lock);
505 } 517 }
506 spin_unlock(&cifs_file_list_lock);
507 } 518 }
508 spin_unlock(&cifs_tcp_ses_lock); 519 spin_unlock(&cifs_tcp_ses_lock);
509 kfree(lw); 520 kfree(lw);
@@ -529,7 +540,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
529 if (rsp->StructureSize != 540 if (rsp->StructureSize !=
530 smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { 541 smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
531 if (le16_to_cpu(rsp->StructureSize) == 44) 542 if (le16_to_cpu(rsp->StructureSize) == 44)
532 return smb2_is_valid_lease_break(buffer, server); 543 return smb2_is_valid_lease_break(buffer);
533 else 544 else
534 return false; 545 return false;
535 } 546 }