diff options
author | Pavel Shilovsky <pshilovsky@samba.org> | 2012-09-19 09:22:45 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2012-09-24 22:46:33 -0400 |
commit | 0822f51426b51bd599b3a7e972b14aacaa045a92 (patch) | |
tree | c11376ec62881566a6ca9e1f7ef85881fc961599 /fs/cifs | |
parent | 25078105fbe14e7b3270391eaa11514bec787a52 (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.c | 2 | ||||
-rw-r--r-- | fs/cifs/smb2misc.c | 90 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 4 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 46 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 25 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 4 |
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 | |||
38 | smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) | 38 | smb2_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 | ||
371 | smb2_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 | |||
392 | static bool | ||
393 | smb2_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 | |||
363 | bool | 448 | bool |
364 | smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) | 449 | smb2_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 | |||
513 | smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, | 513 | smb2_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 | ||
950 | int | 939 | int |
@@ -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 | |||
2221 | int | ||
2222 | SMB2_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 | |||
698 | struct 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 | |||
711 | struct 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, | |||
48 | extern struct mid_q_entry *smb2_setup_async_request( | 48 | extern 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); |
50 | extern void smb2_echo_request(struct work_struct *work); | 50 | extern void smb2_echo_request(struct work_struct *work); |
51 | extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode); | ||
52 | extern __u8 smb2_map_lease_to_oplock(__le32 lease_state); | ||
51 | extern bool smb2_is_valid_oplock_break(char *buffer, | 53 | extern 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); |
156 | extern 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 */ |