diff options
author | Pavel Shilovsky <pshilovsky@samba.org> | 2012-09-18 19:20:33 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2012-09-24 22:46:30 -0400 |
commit | 983c88a497914d60c91f431b05a8449ddda19167 (patch) | |
tree | 6e8ce5d70c6a584472ed7414497d06c034eb0595 /fs/cifs/smb2misc.c | |
parent | 95a3f2f377735ed13e42d3b8039aa1d73af2c90e (diff) |
CIFS: Add oplock break support for SMB2
Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs/cifs/smb2misc.c')
-rw-r--r-- | fs/cifs/smb2misc.c | 76 |
1 files changed, 74 insertions, 2 deletions
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 78225f517a60..01479a3fee8d 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c | |||
@@ -142,8 +142,8 @@ smb2_check_message(char *buf, unsigned int length) | |||
142 | } | 142 | } |
143 | 143 | ||
144 | if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) { | 144 | if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) { |
145 | if (hdr->Status == 0 || | 145 | if (command != SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0 || |
146 | pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2) { | 146 | pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2)) { |
147 | /* error packets have 9 byte structure size */ | 147 | /* error packets have 9 byte structure size */ |
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); |
@@ -162,6 +162,9 @@ smb2_check_message(char *buf, unsigned int length) | |||
162 | if (4 + len != clc_len) { | 162 | if (4 + len != clc_len) { |
163 | cFYI(1, "Calculated size %u length %u mismatch mid %llu", | 163 | cFYI(1, "Calculated size %u length %u mismatch mid %llu", |
164 | clc_len, 4 + len, mid); | 164 | clc_len, 4 + len, mid); |
165 | /* Windows 7 server returns 24 bytes more */ | ||
166 | if (clc_len + 20 == len && command == SMB2_OPLOCK_BREAK_HE) | ||
167 | return 0; | ||
165 | /* server can return one byte more */ | 168 | /* server can return one byte more */ |
166 | if (clc_len == 4 + len + 1) | 169 | if (clc_len == 4 + len + 1) |
167 | return 0; | 170 | return 0; |
@@ -356,3 +359,72 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) | |||
356 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 359 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
357 | return to; | 360 | return to; |
358 | } | 361 | } |
362 | |||
363 | bool | ||
364 | smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) | ||
365 | { | ||
366 | struct smb2_oplock_break *rsp = (struct smb2_oplock_break *)buffer; | ||
367 | struct list_head *tmp, *tmp1, *tmp2; | ||
368 | struct cifs_ses *ses; | ||
369 | struct cifs_tcon *tcon; | ||
370 | struct cifsInodeInfo *cinode; | ||
371 | struct cifsFileInfo *cfile; | ||
372 | |||
373 | cFYI(1, "Checking for oplock break"); | ||
374 | |||
375 | if (rsp->hdr.Command != SMB2_OPLOCK_BREAK) | ||
376 | return false; | ||
377 | |||
378 | if (le16_to_cpu(rsp->StructureSize) != | ||
379 | smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { | ||
380 | return false; | ||
381 | } | ||
382 | |||
383 | cFYI(1, "oplock level 0x%d", rsp->OplockLevel); | ||
384 | |||
385 | /* look up tcon based on tid & uid */ | ||
386 | spin_lock(&cifs_tcp_ses_lock); | ||
387 | list_for_each(tmp, &server->smb_ses_list) { | ||
388 | ses = list_entry(tmp, struct cifs_ses, smb_ses_list); | ||
389 | list_for_each(tmp1, &ses->tcon_list) { | ||
390 | tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); | ||
391 | |||
392 | cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); | ||
393 | spin_lock(&cifs_file_list_lock); | ||
394 | list_for_each(tmp2, &tcon->openFileList) { | ||
395 | cfile = list_entry(tmp2, struct cifsFileInfo, | ||
396 | tlist); | ||
397 | if (rsp->PersistentFid != | ||
398 | cfile->fid.persistent_fid || | ||
399 | rsp->VolatileFid != | ||
400 | cfile->fid.volatile_fid) | ||
401 | continue; | ||
402 | |||
403 | cFYI(1, "file id match, oplock break"); | ||
404 | cinode = CIFS_I(cfile->dentry->d_inode); | ||
405 | |||
406 | if (!cinode->clientCanCacheAll && | ||
407 | rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE) | ||
408 | cfile->oplock_break_cancelled = true; | ||
409 | else | ||
410 | cfile->oplock_break_cancelled = false; | ||
411 | |||
412 | smb2_set_oplock_level(cinode, | ||
413 | rsp->OplockLevel ? SMB2_OPLOCK_LEVEL_II : 0); | ||
414 | |||
415 | queue_work(cifsiod_wq, &cfile->oplock_break); | ||
416 | |||
417 | spin_unlock(&cifs_file_list_lock); | ||
418 | spin_unlock(&cifs_tcp_ses_lock); | ||
419 | return true; | ||
420 | } | ||
421 | spin_unlock(&cifs_file_list_lock); | ||
422 | spin_unlock(&cifs_tcp_ses_lock); | ||
423 | cFYI(1, "No matching file for oplock break"); | ||
424 | return true; | ||
425 | } | ||
426 | } | ||
427 | spin_unlock(&cifs_tcp_ses_lock); | ||
428 | cFYI(1, "Can not process oplock break for non-existent connection"); | ||
429 | return false; | ||
430 | } | ||