Implement SMB Byte-range Lock (Phase 7)

- Add LockManager to oplock.rs:
  - LockRange struct for tracking byte-range locks
  - acquire() - check conflicts before granting lock
  - release() - remove specific lock by offset/length
  - clear() - clear all locks when file closed
  - ranges_overlap() - helper for conflict detection

- Add LockManager to ServerState

- Update handlers/lock.rs:
  - Parse LockRequest and LockElement
  - Process each lock element (acquire/release)
  - Support FLAG_EXCLUSIVE_LOCK, FLAG_SHARED_LOCK, FLAG_UNLOCK
  - Return STATUS_LOCK_NOT_GRANTED on conflict

- Update handlers/close.rs:
  - Clear all locks when file closed

- Add STATUS_LOCK_NOT_GRANTED to ntstatus.rs

All 229 tests pass.
This commit is contained in:
Warren
2026-06-21 00:25:55 +08:00
parent 54ce0d6916
commit 276308af12
5 changed files with 186 additions and 5 deletions
+3
View File
@@ -52,6 +52,9 @@ pub async fn handle(
server.oplock_manager.unregister(&path, &req.file_id).await;
}
// Phase 7: Clear all byte-range locks for this file
server.lock_manager.clear(&req.file_id).await;
// Stat before closing if needed.
let info_before_close = if want_attrs {
if let Some(h) = handle.as_ref() {
+51 -5
View File
@@ -3,18 +3,64 @@
use std::sync::Arc;
use crate::proto::header::Smb2Header;
use crate::proto::messages::LockResponse;
use crate::proto::messages::{LockElement, LockRequest, LockResponse};
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 {
let req = match LockRequest::parse(body) {
Ok(r) => r,
Err(_) => return HandlerResponse::err(ntstatus::STATUS_INVALID_PARAMETER),
};
// Validate tree/session
let tree_arc = match lookup_session_tree(conn, hdr).await {
Ok(t) => t,
Err(s) => return HandlerResponse::err(s),
};
// Phase 7: Process each lock element
for lock in &req.locks {
let exclusive = lock.flags & LockElement::FLAG_EXCLUSIVE_LOCK != 0;
let unlock = lock.flags & LockElement::FLAG_UNLOCK != 0;
if unlock {
// Release lock
server.lock_manager.release(
&req.file_id,
lock.offset,
lock.length,
hdr.session_id,
tree_arc.read().await.id,
).await;
} else {
// Acquire lock
let fail_immediately = lock.flags & LockElement::FLAG_FAIL_IMMEDIATELY != 0;
let result = server.lock_manager.acquire(
&req.file_id,
lock.offset,
lock.length,
exclusive,
hdr.session_id,
tree_arc.read().await.id,
).await;
if result.is_err() && fail_immediately {
return HandlerResponse::err(ntstatus::STATUS_LOCK_NOT_GRANTED);
}
}
}
let mut buf = Vec::new();
LockResponse::default().write_to(&mut buf).expect("encode");
HandlerResponse::ok(buf)