|
|
|
@ -148,6 +148,13 @@ smb2_check_message(char *buf, unsigned int length) |
|
|
|
|
cERROR(1, "Illegal response size %u for command %d", |
|
|
|
|
le16_to_cpu(pdu->StructureSize2), command); |
|
|
|
|
return 1; |
|
|
|
|
} else if (command == SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0) |
|
|
|
|
&& (le16_to_cpu(pdu->StructureSize2) != 44) |
|
|
|
|
&& (le16_to_cpu(pdu->StructureSize2) != 36)) { |
|
|
|
|
/* special case for SMB2.1 lease break message */ |
|
|
|
|
cERROR(1, "Illegal response size %d for oplock break", |
|
|
|
|
le16_to_cpu(pdu->StructureSize2)); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -360,6 +367,84 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) |
|
|
|
|
return to; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
__le32 |
|
|
|
|
smb2_get_lease_state(struct cifsInodeInfo *cinode) |
|
|
|
|
{ |
|
|
|
|
if (cinode->clientCanCacheAll) |
|
|
|
|
return SMB2_LEASE_WRITE_CACHING | SMB2_LEASE_READ_CACHING; |
|
|
|
|
else if (cinode->clientCanCacheRead) |
|
|
|
|
return SMB2_LEASE_READ_CACHING; |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
__u8 smb2_map_lease_to_oplock(__le32 lease_state) |
|
|
|
|
{ |
|
|
|
|
if (lease_state & SMB2_LEASE_WRITE_CACHING) { |
|
|
|
|
if (lease_state & SMB2_LEASE_HANDLE_CACHING) |
|
|
|
|
return SMB2_OPLOCK_LEVEL_BATCH; |
|
|
|
|
else |
|
|
|
|
return SMB2_OPLOCK_LEVEL_EXCLUSIVE; |
|
|
|
|
} else if (lease_state & SMB2_LEASE_READ_CACHING) |
|
|
|
|
return SMB2_OPLOCK_LEVEL_II; |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool |
|
|
|
|
smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) |
|
|
|
|
{ |
|
|
|
|
struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer; |
|
|
|
|
struct list_head *tmp, *tmp1, *tmp2; |
|
|
|
|
struct cifs_ses *ses; |
|
|
|
|
struct cifs_tcon *tcon; |
|
|
|
|
struct cifsInodeInfo *cinode; |
|
|
|
|
struct cifsFileInfo *cfile; |
|
|
|
|
|
|
|
|
|
cFYI(1, "Checking for lease break"); |
|
|
|
|
|
|
|
|
|
/* look up tcon based on tid & uid */ |
|
|
|
|
spin_lock(&cifs_tcp_ses_lock); |
|
|
|
|
list_for_each(tmp, &server->smb_ses_list) { |
|
|
|
|
ses = list_entry(tmp, struct cifs_ses, smb_ses_list); |
|
|
|
|
list_for_each(tmp1, &ses->tcon_list) { |
|
|
|
|
tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); |
|
|
|
|
|
|
|
|
|
cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); |
|
|
|
|
spin_lock(&cifs_file_list_lock); |
|
|
|
|
list_for_each(tmp2, &tcon->openFileList) { |
|
|
|
|
cfile = list_entry(tmp2, struct cifsFileInfo, |
|
|
|
|
tlist); |
|
|
|
|
cinode = CIFS_I(cfile->dentry->d_inode); |
|
|
|
|
|
|
|
|
|
if (memcmp(cinode->lease_key, rsp->LeaseKey, |
|
|
|
|
SMB2_LEASE_KEY_SIZE)) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
cFYI(1, "lease key match, lease break 0x%d", |
|
|
|
|
le32_to_cpu(rsp->NewLeaseState)); |
|
|
|
|
|
|
|
|
|
smb2_set_oplock_level(cinode, |
|
|
|
|
smb2_map_lease_to_oplock(rsp->NewLeaseState)); |
|
|
|
|
|
|
|
|
|
if (rsp->Flags & |
|
|
|
|
SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) |
|
|
|
|
cfile->oplock_break_cancelled = false; |
|
|
|
|
else |
|
|
|
|
cfile->oplock_break_cancelled = true; |
|
|
|
|
|
|
|
|
|
queue_work(cifsiod_wq, &cfile->oplock_break); |
|
|
|
|
|
|
|
|
|
spin_unlock(&cifs_file_list_lock); |
|
|
|
|
spin_unlock(&cifs_tcp_ses_lock); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
spin_unlock(&cifs_file_list_lock); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
spin_unlock(&cifs_tcp_ses_lock); |
|
|
|
|
cFYI(1, "Can not process lease break - no lease matched"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool |
|
|
|
|
smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) |
|
|
|
|
{ |
|
|
|
@ -377,7 +462,10 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) |
|
|
|
|
|
|
|
|
|
if (le16_to_cpu(rsp->StructureSize) != |
|
|
|
|
smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { |
|
|
|
|
return false; |
|
|
|
|
if (le16_to_cpu(rsp->StructureSize) == 44) |
|
|
|
|
return smb2_is_valid_lease_break(buffer, server); |
|
|
|
|
else |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
cFYI(1, "oplock level 0x%d", rsp->OplockLevel); |
|
|
|
|