From 3cf503d05f322ba850f766aef5b1bab188adf0dd Mon Sep 17 00:00:00 2001 From: Warren Date: Sun, 21 Jun 2026 01:15:21 +0800 Subject: [PATCH] =?UTF-8?q?Implement=20Oplock=20Break=20Acknowledgement=20?= =?UTF-8?q?handler=20(MS-SMB2=20=C2=A72.2.24)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Parse client's OPLOCK_BREAK_ACK - Update Open.oplock_level in Open struct - Update OplockManager entry via update_oplock_level() - Return confirmation response All 229 tests pass. --- .../smb-server/src/handlers/oplock_break.rs | 56 ++++++++++++++----- vendor/smb-server/src/oplock.rs | 15 +++++ 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/vendor/smb-server/src/handlers/oplock_break.rs b/vendor/smb-server/src/handlers/oplock_break.rs index 3b75a47..794d9e9 100644 --- a/vendor/smb-server/src/handlers/oplock_break.rs +++ b/vendor/smb-server/src/handlers/oplock_break.rs @@ -1,27 +1,57 @@ -//! OPLOCK_BREAK handler — acknowledge breaks without granting oplocks. +//! OPLOCK_BREAK handler — acknowledge breaks and update oplock state. use std::sync::Arc; use crate::proto::header::Smb2Header; -use crate::proto::messages::FileId; +use crate::proto::messages::{FileId, OplockBreakAck}; use crate::conn::state::Connection; use crate::dispatch::HandlerResponse; +use crate::handlers::shared::lookup_session_tree; +use crate::ntstatus; use crate::server::ServerState; pub async fn handle( - _server: &Arc, - _conn: &Arc, - _hdr: &Smb2Header, - _body: &[u8], + server: &Arc, + conn: &Arc, + hdr: &Smb2Header, + body: &[u8], ) -> HandlerResponse { - // Echo back the same shape as the notification — structure_size=24, level=0. + // Parse client's ACK (MS-SMB2 §2.2.24) + let ack = match OplockBreakAck::parse(body) { + Ok(a) => a, + Err(_) => return HandlerResponse::err(ntstatus::STATUS_INVALID_PARAMETER), + }; + + // Lookup tree to get path for oplock manager + let tree_arc = match lookup_session_tree(conn, hdr).await { + Ok(t) => t, + Err(s) => return HandlerResponse::err(s), + }; + + // Update oplock level in the open + let path = { + // Find the open by file_id + let tree = tree_arc.read().await; + let opens = tree.opens.read().await; + if let Some(open_arc) = opens.get(&ack.file_id) { + let mut open = open_arc.write().await; + open.oplock_level = ack.oplock_level; + open.last_path.clone() + } else { + return HandlerResponse::err(ntstatus::STATUS_FILE_CLOSED); + } + }; + + // Update OplockManager entry + server.oplock_manager.update_oplock_level( + &path, + ack.file_id, + ack.oplock_level, + ).await; + + // Echo back the ACK as confirmation (MS-SMB2 §2.2.24) let mut buf = Vec::new(); - buf.extend_from_slice(&24u16.to_le_bytes()); // structure_size - buf.push(0); // OplockLevel - buf.push(0); // Reserved - buf.extend_from_slice(&0u32.to_le_bytes()); // Reserved2 - buf.extend_from_slice(&FileId::any().persistent.to_le_bytes()); - buf.extend_from_slice(&FileId::any().volatile.to_le_bytes()); + ack.write_to(&mut buf).expect("encode ack response"); HandlerResponse::ok(buf) } diff --git a/vendor/smb-server/src/oplock.rs b/vendor/smb-server/src/oplock.rs index 641b32f..61f84de 100644 --- a/vendor/smb-server/src/oplock.rs +++ b/vendor/smb-server/src/oplock.rs @@ -191,6 +191,21 @@ pub struct LockRange { pub tree_id: u32, } +impl OplockManager { + /// Update oplock level after client acknowledges a break (MS-SMB2 §2.2.24). + pub async fn update_oplock_level(&self, path: &SmbPath, file_id: FileId, new_level: u8) { + let mut file_opens = self.file_opens.write().await; + if let Some(entries) = file_opens.get_mut(path) { + for entry in entries.iter_mut() { + if entry.file_id == file_id { + entry.oplock_level = new_level; + break; + } + } + } + } +} + /// Global byte-range lock manager (MS-SMB2 §3.3.1.9). pub struct LockManager { /// FileId → active locks on that file.