Loading fs/cifs/smb2file.c +2 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,8 @@ void smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) { oplock &= 0xFF; if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) return; if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { cinode->clientCanCacheAll = true; cinode->clientCanCacheRead = true; Loading fs/cifs/smb2misc.c +89 −1 Original line number Diff line number Diff line Loading @@ -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; } } Loading Loading @@ -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) { Loading @@ -377,6 +462,9 @@ 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]) { if (le16_to_cpu(rsp->StructureSize) == 44) return smb2_is_valid_lease_break(buffer, server); else return false; } Loading fs/cifs/smb2ops.c +4 −0 Original line number Diff line number Diff line Loading @@ -513,6 +513,10 @@ static int smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, struct cifsInodeInfo *cinode) { if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) return SMB2_lease_break(0, tcon, cinode->lease_key, smb2_get_lease_state(cinode)); return SMB2_oplock_break(0, tcon, fid->persistent_fid, fid->volatile_fid, cinode->clientCanCacheRead ? 1 : 0); Loading fs/cifs/smb2pdu.c +33 −13 Original line number Diff line number Diff line Loading @@ -911,7 +911,6 @@ parse_lease_state(struct smb2_create_rsp *rsp) { char *data_offset; struct create_lease *lc; __u8 oplock = 0; bool found = false; data_offset = (char *)rsp; Loading @@ -932,19 +931,9 @@ parse_lease_state(struct smb2_create_rsp *rsp) } while (le32_to_cpu(lc->ccontext.Next) != 0); if (!found) return oplock; if (le32_to_cpu(lc->lcontext.LeaseState) & SMB2_LEASE_WRITE_CACHING) { if (le32_to_cpu(lc->lcontext.LeaseState) & SMB2_LEASE_HANDLE_CACHING) oplock = SMB2_OPLOCK_LEVEL_BATCH; else oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE; } else if (le32_to_cpu(lc->lcontext.LeaseState) & SMB2_LEASE_READ_CACHING) oplock = SMB2_OPLOCK_LEVEL_II; return 0; return oplock; return smb2_map_lease_to_oplock(lc->lcontext.LeaseState); } int Loading Loading @@ -2228,3 +2217,34 @@ SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock); } int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, __u8 *lease_key, const __le32 lease_state) { int rc; struct smb2_lease_ack *req = NULL; cFYI(1, "SMB2_lease_break"); rc = small_smb2_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req); if (rc) return rc; req->hdr.CreditRequest = cpu_to_le16(1); req->StructureSize = cpu_to_le16(36); inc_rfc1001_len(req, 12); memcpy(req->LeaseKey, lease_key, 16); req->LeaseState = lease_state; rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP); /* SMB2 buffer freed by function above */ if (rc) { cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); cFYI(1, "Send error in Lease Break = %d", rc); } return rc; } fs/cifs/smb2pdu.h +25 −0 Original line number Diff line number Diff line Loading @@ -693,6 +693,31 @@ struct smb2_oplock_break { __u64 VolatileFid; } __packed; #define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01) struct smb2_lease_break { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 44 */ __le16 Reserved; __le32 Flags; __u8 LeaseKey[16]; __le32 CurrentLeaseState; __le32 NewLeaseState; __le32 BreakReason; __le32 AccessMaskHint; __le32 ShareMaskHint; } __packed; struct smb2_lease_ack { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 36 */ __le16 Reserved; __le32 Flags; __u8 LeaseKey[16]; __le32 LeaseState; __le64 LeaseDuration; } __packed; /* * PDU infolevel structure definitions * BB consider moving to a different header Loading Loading
fs/cifs/smb2file.c +2 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,8 @@ void smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) { oplock &= 0xFF; if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) return; if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { cinode->clientCanCacheAll = true; cinode->clientCanCacheRead = true; Loading
fs/cifs/smb2misc.c +89 −1 Original line number Diff line number Diff line Loading @@ -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; } } Loading Loading @@ -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) { Loading @@ -377,6 +462,9 @@ 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]) { if (le16_to_cpu(rsp->StructureSize) == 44) return smb2_is_valid_lease_break(buffer, server); else return false; } Loading
fs/cifs/smb2ops.c +4 −0 Original line number Diff line number Diff line Loading @@ -513,6 +513,10 @@ static int smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, struct cifsInodeInfo *cinode) { if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) return SMB2_lease_break(0, tcon, cinode->lease_key, smb2_get_lease_state(cinode)); return SMB2_oplock_break(0, tcon, fid->persistent_fid, fid->volatile_fid, cinode->clientCanCacheRead ? 1 : 0); Loading
fs/cifs/smb2pdu.c +33 −13 Original line number Diff line number Diff line Loading @@ -911,7 +911,6 @@ parse_lease_state(struct smb2_create_rsp *rsp) { char *data_offset; struct create_lease *lc; __u8 oplock = 0; bool found = false; data_offset = (char *)rsp; Loading @@ -932,19 +931,9 @@ parse_lease_state(struct smb2_create_rsp *rsp) } while (le32_to_cpu(lc->ccontext.Next) != 0); if (!found) return oplock; if (le32_to_cpu(lc->lcontext.LeaseState) & SMB2_LEASE_WRITE_CACHING) { if (le32_to_cpu(lc->lcontext.LeaseState) & SMB2_LEASE_HANDLE_CACHING) oplock = SMB2_OPLOCK_LEVEL_BATCH; else oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE; } else if (le32_to_cpu(lc->lcontext.LeaseState) & SMB2_LEASE_READ_CACHING) oplock = SMB2_OPLOCK_LEVEL_II; return 0; return oplock; return smb2_map_lease_to_oplock(lc->lcontext.LeaseState); } int Loading Loading @@ -2228,3 +2217,34 @@ SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock); } int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, __u8 *lease_key, const __le32 lease_state) { int rc; struct smb2_lease_ack *req = NULL; cFYI(1, "SMB2_lease_break"); rc = small_smb2_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req); if (rc) return rc; req->hdr.CreditRequest = cpu_to_le16(1); req->StructureSize = cpu_to_le16(36); inc_rfc1001_len(req, 12); memcpy(req->LeaseKey, lease_key, 16); req->LeaseState = lease_state; rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP); /* SMB2 buffer freed by function above */ if (rc) { cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); cFYI(1, "Send error in Lease Break = %d", rc); } return rc; }
fs/cifs/smb2pdu.h +25 −0 Original line number Diff line number Diff line Loading @@ -693,6 +693,31 @@ struct smb2_oplock_break { __u64 VolatileFid; } __packed; #define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01) struct smb2_lease_break { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 44 */ __le16 Reserved; __le32 Flags; __u8 LeaseKey[16]; __le32 CurrentLeaseState; __le32 NewLeaseState; __le32 BreakReason; __le32 AccessMaskHint; __le32 ShareMaskHint; } __packed; struct smb2_lease_ack { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 36 */ __le16 Reserved; __le32 Flags; __u8 LeaseKey[16]; __le32 LeaseState; __le64 LeaseDuration; } __packed; /* * PDU infolevel structure definitions * BB consider moving to a different header Loading