aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorPavel Shilovsky <pshilovsky@samba.org>2012-09-19 09:22:45 -0400
committerSteve French <smfrench@gmail.com>2012-09-24 22:46:33 -0400
commit0822f51426b51bd599b3a7e972b14aacaa045a92 (patch)
treec11376ec62881566a6ca9e1f7ef85881fc961599 /fs/cifs
parent25078105fbe14e7b3270391eaa11514bec787a52 (diff)
CIFS: Add SMB2.1 lease break support
Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org> Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/smb2file.c2
-rw-r--r--fs/cifs/smb2misc.c90
-rw-r--r--fs/cifs/smb2ops.c4
-rw-r--r--fs/cifs/smb2pdu.c46
-rw-r--r--fs/cifs/smb2pdu.h25
-rw-r--r--fs/cifs/smb2proto.h4
6 files changed, 157 insertions, 14 deletions
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
index 78fb2050e0d6..a93eec30a50d 100644
--- a/fs/cifs/smb2file.c
+++ b/fs/cifs/smb2file.c
@@ -38,6 +38,8 @@ void
38smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) 38smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
39{ 39{
40 oplock &= 0xFF; 40 oplock &= 0xFF;
41 if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
42 return;
41 if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { 43 if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
42 cinode->clientCanCacheAll = true; 44 cinode->clientCanCacheAll = true;
43 cinode->clientCanCacheRead = true; 45 cinode->clientCanCacheRead = true;
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 01479a3fee8d..3a7f8bd5127d 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -148,6 +148,13 @@ smb2_check_message(char *buf, unsigned int length)
148 cERROR(1, "Illegal response size %u for command %d", 148 cERROR(1, "Illegal response size %u for command %d",
149 le16_to_cpu(pdu->StructureSize2), command); 149 le16_to_cpu(pdu->StructureSize2), command);
150 return 1; 150 return 1;
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;
151 } 158 }
152 } 159 }
153 160
@@ -360,6 +367,84 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
360 return to; 367 return to;
361} 368}
362 369
370__le32
371smb2_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
392static bool
393smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
394{
395 struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer;
396 struct list_head *tmp, *tmp1, *tmp2;
397 struct cifs_ses *ses;
398 struct cifs_tcon *tcon;
399 struct cifsInodeInfo *cinode;
400 struct cifsFileInfo *cfile;
401
402 cFYI(1, "Checking for lease break");
403
404 /* look up tcon based on tid & uid */
405 spin_lock(&cifs_tcp_ses_lock);
406 list_for_each(tmp, &server->smb_ses_list) {
407 ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
408 list_for_each(tmp1, &ses->tcon_list) {
409 tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
410
411 cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
412 spin_lock(&cifs_file_list_lock);
413 list_for_each(tmp2, &tcon->openFileList) {
414 cfile = list_entry(tmp2, struct cifsFileInfo,
415 tlist);
416 cinode = CIFS_I(cfile->dentry->d_inode);
417
418 if (memcmp(cinode->lease_key, rsp->LeaseKey,
419 SMB2_LEASE_KEY_SIZE))
420 continue;
421
422 cFYI(1, "lease key match, lease break 0x%d",
423 le32_to_cpu(rsp->NewLeaseState));
424
425 smb2_set_oplock_level(cinode,
426 smb2_map_lease_to_oplock(rsp->NewLeaseState));
427
428 if (rsp->Flags &
429 SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED)
430 cfile->oplock_break_cancelled = false;
431 else
432 cfile->oplock_break_cancelled = true;
433
434 queue_work(cifsiod_wq, &cfile->oplock_break);
435
436 spin_unlock(&cifs_file_list_lock);
437 spin_unlock(&cifs_tcp_ses_lock);
438 return true;
439 }
440 spin_unlock(&cifs_file_list_lock);
441 }
442 }
443 spin_unlock(&cifs_tcp_ses_lock);
444 cFYI(1, "Can not process lease break - no lease matched");
445 return false;
446}
447
363bool 448bool
364smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) 449smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
365{ 450{
@@ -377,7 +462,10 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
377 462
378 if (le16_to_cpu(rsp->StructureSize) != 463 if (le16_to_cpu(rsp->StructureSize) !=
379 smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { 464 smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
380 return false; 465 if (le16_to_cpu(rsp->StructureSize) == 44)
466 return smb2_is_valid_lease_break(buffer, server);
467 else
468 return false;
381 } 469 }
382 470
383 cFYI(1, "oplock level 0x%d", rsp->OplockLevel); 471 cFYI(1, "oplock level 0x%d", rsp->OplockLevel);
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 360d9079af49..630156f98cc7 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -513,6 +513,10 @@ static int
513smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, 513smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
514 struct cifsInodeInfo *cinode) 514 struct cifsInodeInfo *cinode)
515{ 515{
516 if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
517 return SMB2_lease_break(0, tcon, cinode->lease_key,
518 smb2_get_lease_state(cinode));
519
516 return SMB2_oplock_break(0, tcon, fid->persistent_fid, 520 return SMB2_oplock_break(0, tcon, fid->persistent_fid,
517 fid->volatile_fid, 521 fid->volatile_fid,
518 cinode->clientCanCacheRead ? 1 : 0); 522 cinode->clientCanCacheRead ? 1 : 0);
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 89d2824587b2..1572abefb378 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -911,7 +911,6 @@ parse_lease_state(struct smb2_create_rsp *rsp)
911{ 911{
912 char *data_offset; 912 char *data_offset;
913 struct create_lease *lc; 913 struct create_lease *lc;
914 __u8 oplock = 0;
915 bool found = false; 914 bool found = false;
916 915
917 data_offset = (char *)rsp; 916 data_offset = (char *)rsp;
@@ -932,19 +931,9 @@ parse_lease_state(struct smb2_create_rsp *rsp)
932 } while (le32_to_cpu(lc->ccontext.Next) != 0); 931 } while (le32_to_cpu(lc->ccontext.Next) != 0);
933 932
934 if (!found) 933 if (!found)
935 return oplock; 934 return 0;
936
937 if (le32_to_cpu(lc->lcontext.LeaseState) & SMB2_LEASE_WRITE_CACHING) {
938 if (le32_to_cpu(lc->lcontext.LeaseState) &
939 SMB2_LEASE_HANDLE_CACHING)
940 oplock = SMB2_OPLOCK_LEVEL_BATCH;
941 else
942 oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
943 } else if (le32_to_cpu(lc->lcontext.LeaseState) &
944 SMB2_LEASE_READ_CACHING)
945 oplock = SMB2_OPLOCK_LEVEL_II;
946 935
947 return oplock; 936 return smb2_map_lease_to_oplock(lc->lcontext.LeaseState);
948} 937}
949 938
950int 939int
@@ -2228,3 +2217,34 @@ SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
2228 2217
2229 return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock); 2218 return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock);
2230} 2219}
2220
2221int
2222SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
2223 __u8 *lease_key, const __le32 lease_state)
2224{
2225 int rc;
2226 struct smb2_lease_ack *req = NULL;
2227
2228 cFYI(1, "SMB2_lease_break");
2229 rc = small_smb2_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req);
2230
2231 if (rc)
2232 return rc;
2233
2234 req->hdr.CreditRequest = cpu_to_le16(1);
2235 req->StructureSize = cpu_to_le16(36);
2236 inc_rfc1001_len(req, 12);
2237
2238 memcpy(req->LeaseKey, lease_key, 16);
2239 req->LeaseState = lease_state;
2240
2241 rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP);
2242 /* SMB2 buffer freed by function above */
2243
2244 if (rc) {
2245 cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
2246 cFYI(1, "Send error in Lease Break = %d", rc);
2247 }
2248
2249 return rc;
2250}
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index e818a5cc5bd8..da099225b1a9 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -693,6 +693,31 @@ struct smb2_oplock_break {
693 __u64 VolatileFid; 693 __u64 VolatileFid;
694} __packed; 694} __packed;
695 695
696#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01)
697
698struct smb2_lease_break {
699 struct smb2_hdr hdr;
700 __le16 StructureSize; /* Must be 44 */
701 __le16 Reserved;
702 __le32 Flags;
703 __u8 LeaseKey[16];
704 __le32 CurrentLeaseState;
705 __le32 NewLeaseState;
706 __le32 BreakReason;
707 __le32 AccessMaskHint;
708 __le32 ShareMaskHint;
709} __packed;
710
711struct smb2_lease_ack {
712 struct smb2_hdr hdr;
713 __le16 StructureSize; /* Must be 36 */
714 __le16 Reserved;
715 __le32 Flags;
716 __u8 LeaseKey[16];
717 __le32 LeaseState;
718 __le64 LeaseDuration;
719} __packed;
720
696/* 721/*
697 * PDU infolevel structure definitions 722 * PDU infolevel structure definitions
698 * BB consider moving to a different header 723 * BB consider moving to a different header
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 8b4d3712255b..7d25f8b14f93 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -48,6 +48,8 @@ extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses,
48extern struct mid_q_entry *smb2_setup_async_request( 48extern struct mid_q_entry *smb2_setup_async_request(
49 struct TCP_Server_Info *server, struct smb_rqst *rqst); 49 struct TCP_Server_Info *server, struct smb_rqst *rqst);
50extern void smb2_echo_request(struct work_struct *work); 50extern void smb2_echo_request(struct work_struct *work);
51extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode);
52extern __u8 smb2_map_lease_to_oplock(__le32 lease_state);
51extern bool smb2_is_valid_oplock_break(char *buffer, 53extern bool smb2_is_valid_oplock_break(char *buffer,
52 struct TCP_Server_Info *srv); 54 struct TCP_Server_Info *srv);
53 55
@@ -151,5 +153,7 @@ extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
151 const __u64 persist_fid, const __u64 volatile_fid, 153 const __u64 persist_fid, const __u64 volatile_fid,
152 const __u32 pid, const __u32 num_lock, 154 const __u32 pid, const __u32 num_lock,
153 struct smb2_lock_element *buf); 155 struct smb2_lock_element *buf);
156extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
157 __u8 *lease_key, const __le32 lease_state);
154 158
155#endif /* _SMB2PROTO_H */ 159#endif /* _SMB2PROTO_H */