aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPavel Shilovsky <pshilovsky@samba.org>2013-09-05 07:00:07 -0400
committerSteve French <smfrench@gmail.com>2013-09-08 15:41:43 -0400
commit933d4b36576c951d0371bbfed05ec0135d516a6e (patch)
tree2b4ce96ea00ee15b4e61768a5b27bda33baa939d
parent1a05096de82f3cd672c76389f63964952678506f (diff)
CIFS: Fix missing lease break
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. Cc: <stable@vger.kernel.org> Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org> Signed-off-by: Steve French <smfrench@gmail.com>
-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 38c93c305c9e..314bd60f30ce 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -421,97 +421,108 @@ cifs_ses_oplock_break(struct work_struct *work)
421} 421}
422 422
423static bool 423static bool
424smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) 424smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
425 struct smb2_lease_break_work *lw)
425{ 426{
426 struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer; 427 bool found;
427 struct list_head *tmp, *tmp1, *tmp2; 428 __u8 lease_state;
428 struct cifs_ses *ses; 429 struct list_head *tmp;
429 struct cifs_tcon *tcon;
430 struct cifsInodeInfo *cinode;
431 struct cifsFileInfo *cfile; 430 struct cifsFileInfo *cfile;
432 struct cifs_pending_open *open; 431 struct cifs_pending_open *open;
433 struct smb2_lease_break_work *lw; 432 struct cifsInodeInfo *cinode;
434 bool found;
435 int ack_req = le32_to_cpu(rsp->Flags & 433 int ack_req = le32_to_cpu(rsp->Flags &
436 SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); 434 SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED);
437 435
438 lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); 436 lease_state = smb2_map_lease_to_oplock(rsp->NewLeaseState);
439 if (!lw)
440 return false;
441 437
442 INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); 438 list_for_each(tmp, &tcon->openFileList) {
443 lw->lease_state = rsp->NewLeaseState; 439 cfile = list_entry(tmp, struct cifsFileInfo, tlist);
440 cinode = CIFS_I(cfile->dentry->d_inode);
444 441
445 cifs_dbg(FYI, "Checking for lease break\n"); 442 if (memcmp(cinode->lease_key, rsp->LeaseKey,
443 SMB2_LEASE_KEY_SIZE))
444 continue;
446 445
447 /* look up tcon based on tid & uid */ 446 cifs_dbg(FYI, "found in the open list\n");
448 spin_lock(&cifs_tcp_ses_lock); 447 cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
449 list_for_each(tmp, &server->smb_ses_list) { 448 le32_to_cpu(rsp->NewLeaseState));
450 ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
451 449
452 spin_lock(&cifs_file_list_lock); 450 smb2_set_oplock_level(cinode, lease_state);
453 list_for_each(tmp1, &ses->tcon_list) {
454 tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
455 451
456 cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); 452 if (ack_req)
457 list_for_each(tmp2, &tcon->openFileList) { 453 cfile->oplock_break_cancelled = false;
458 cfile = list_entry(tmp2, struct cifsFileInfo, 454 else
459 tlist); 455 cfile->oplock_break_cancelled = true;
460 cinode = CIFS_I(cfile->dentry->d_inode);
461 456
462 if (memcmp(cinode->lease_key, rsp->LeaseKey, 457 queue_work(cifsiod_wq, &cfile->oplock_break);
463 SMB2_LEASE_KEY_SIZE)) 458 kfree(lw);
464 continue; 459 return true;
460 }
465 461
466 cifs_dbg(FYI, "found in the open list\n"); 462 found = false;
467 cifs_dbg(FYI, "lease key match, lease break 0x%d\n", 463 list_for_each_entry(open, &tcon->pending_opens, olist) {
468 le32_to_cpu(rsp->NewLeaseState)); 464 if (memcmp(open->lease_key, rsp->LeaseKey,
465 SMB2_LEASE_KEY_SIZE))
466 continue;
467
468 if (!found && ack_req) {
469 found = true;
470 memcpy(lw->lease_key, open->lease_key,
471 SMB2_LEASE_KEY_SIZE);
472 lw->tlink = cifs_get_tlink(open->tlink);
473 queue_work(cifsiod_wq, &lw->lease_break);
474 }
469 475
470 smb2_set_oplock_level(cinode, 476 cifs_dbg(FYI, "found in the pending open list\n");
471 smb2_map_lease_to_oplock(rsp->NewLeaseState)); 477 cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
478 le32_to_cpu(rsp->NewLeaseState));
472 479
473 if (ack_req) 480 open->oplock = lease_state;
474 cfile->oplock_break_cancelled = false; 481 }
475 else 482 return found;
476 cfile->oplock_break_cancelled = true; 483}
477 484
478 queue_work(cifsiod_wq, &cfile->oplock_break); 485static bool
486smb2_is_valid_lease_break(char *buffer)
487{
488 struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer;
489 struct list_head *tmp, *tmp1, *tmp2;
490 struct TCP_Server_Info *server;
491 struct cifs_ses *ses;
492 struct cifs_tcon *tcon;
493 struct smb2_lease_break_work *lw;
479 494
480 kfree(lw); 495 lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
481 spin_unlock(&cifs_file_list_lock); 496 if (!lw)
482 spin_unlock(&cifs_tcp_ses_lock); 497 return false;
483 return true;
484 }
485 498
486 found = false; 499 INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
487 list_for_each_entry(open, &tcon->pending_opens, olist) { 500 lw->lease_state = rsp->NewLeaseState;
488 if (memcmp(open->lease_key, rsp->LeaseKey,
489 SMB2_LEASE_KEY_SIZE))
490 continue;
491 501
492 if (!found && ack_req) { 502 cifs_dbg(FYI, "Checking for lease break\n");
493 found = true; 503
494 memcpy(lw->lease_key, open->lease_key, 504 /* look up tcon based on tid & uid */
495 SMB2_LEASE_KEY_SIZE); 505 spin_lock(&cifs_tcp_ses_lock);
496 lw->tlink = cifs_get_tlink(open->tlink); 506 list_for_each(tmp, &cifs_tcp_ses_list) {
497 queue_work(cifsiod_wq, 507 server = list_entry(tmp, struct TCP_Server_Info, tcp_ses_list);
498 &lw->lease_break);
499 }
500 508
501 cifs_dbg(FYI, "found in the pending open list\n"); 509 list_for_each(tmp1, &server->smb_ses_list) {
502 cifs_dbg(FYI, "lease key match, lease break 0x%d\n", 510 ses = list_entry(tmp1, struct cifs_ses, smb_ses_list);
503 le32_to_cpu(rsp->NewLeaseState));
504 511
505 open->oplock = 512 spin_lock(&cifs_file_list_lock);
506 smb2_map_lease_to_oplock(rsp->NewLeaseState); 513 list_for_each(tmp2, &ses->tcon_list) {
507 } 514 tcon = list_entry(tmp2, struct cifs_tcon,
508 if (found) { 515 tcon_list);
509 spin_unlock(&cifs_file_list_lock); 516 cifs_stats_inc(
510 spin_unlock(&cifs_tcp_ses_lock); 517 &tcon->stats.cifs_stats.num_oplock_brks);
511 return true; 518 if (smb2_tcon_has_lease(tcon, rsp, lw)) {
519 spin_unlock(&cifs_file_list_lock);
520 spin_unlock(&cifs_tcp_ses_lock);
521 return true;
522 }
512 } 523 }
524 spin_unlock(&cifs_file_list_lock);
513 } 525 }
514 spin_unlock(&cifs_file_list_lock);
515 } 526 }
516 spin_unlock(&cifs_tcp_ses_lock); 527 spin_unlock(&cifs_tcp_ses_lock);
517 kfree(lw); 528 kfree(lw);
@@ -537,7 +548,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
537 if (rsp->StructureSize != 548 if (rsp->StructureSize !=
538 smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { 549 smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
539 if (le16_to_cpu(rsp->StructureSize) == 44) 550 if (le16_to_cpu(rsp->StructureSize) == 44)
540 return smb2_is_valid_lease_break(buffer, server); 551 return smb2_is_valid_lease_break(buffer);
541 else 552 else
542 return false; 553 return false;
543 } 554 }