Implement Oplock Break Acknowledgement handler (MS-SMB2 §2.2.24)
- 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.
This commit is contained in:
56
vendor/smb-server/src/handlers/oplock_break.rs
vendored
56
vendor/smb-server/src/handlers/oplock_break.rs
vendored
@@ -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<ServerState>,
|
||||
_conn: &Arc<Connection>,
|
||||
_hdr: &Smb2Header,
|
||||
_body: &[u8],
|
||||
server: &Arc<ServerState>,
|
||||
conn: &Arc<Connection>,
|
||||
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)
|
||||
}
|
||||
|
||||
15
vendor/smb-server/src/oplock.rs
vendored
15
vendor/smb-server/src/oplock.rs
vendored
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user