Files
markbase/vendor/smb-server/src/handlers/close.rs
Warren 4620475ba8
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
Implement SMB 3.x Lease support Phase 4
- CLOSE handler unregister lease_key from LeaseManager
- Extract lease_key from Open struct before close

All 229 tests pass.
2026-06-21 01:24:02 +08:00

123 lines
3.6 KiB
Rust

//! CLOSE handler.
use std::sync::Arc;
use crate::proto::header::Smb2Header;
use crate::proto::messages::{CloseRequest, CloseResponse};
use tracing::debug;
use crate::conn::state::Connection;
use crate::dispatch::HandlerResponse;
use crate::handlers::shared::lookup_session_tree;
use crate::ntstatus;
use crate::server::ServerState;
const FLAG_POSTQUERY_ATTRIB: u16 = 0x0001;
pub async fn handle(
server: &Arc<ServerState>,
conn: &Arc<Connection>,
hdr: &Smb2Header,
body: &[u8],
) -> HandlerResponse {
let req = match CloseRequest::parse(body) {
Ok(r) => r,
Err(_) => return HandlerResponse::err(ntstatus::STATUS_INVALID_PARAMETER),
};
let tree_arc = match lookup_session_tree(conn, hdr).await {
Ok(t) => t,
Err(s) => return HandlerResponse::err(s),
};
let removed = {
let tree = tree_arc.write().await;
let mut opens = tree.opens.write().await;
opens.remove(&req.file_id)
};
let open_arc = match removed {
Some(o) => o,
None => return HandlerResponse::err(ntstatus::STATUS_FILE_CLOSED),
};
// Pull state out, close the handle, then optionally unlink.
let mut open = open_arc.write().await;
let handle = open.handle.take();
let path = open.last_path.clone();
let delete_on_close = open.delete_on_close;
let oplock_level = open.oplock_level;
let lease_key = open.lease_key.clone(); // Phase 4: for lease unregister
let want_attrs = req.flags & FLAG_POSTQUERY_ATTRIB != 0;
drop(open);
// Phase 6: Unregister from OplockManager if oplock was granted
if oplock_level > 0 {
server.oplock_manager.unregister(&path, &req.file_id).await;
}
// Phase 4: Unregister from LeaseManager if lease was granted
if let Some(lease_key) = lease_key {
server.lease_manager.unregister(&lease_key).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() {
h.stat().await.ok()
} else {
None
}
} else {
None
};
if let Some(h) = handle {
let _ = h.close().await;
}
if delete_on_close {
let tree = tree_arc.read().await;
let backend = tree.share.backend.clone();
drop(tree);
if let Err(e) = backend.unlink(&path).await {
debug!(error = %e, "delete-on-close unlink failed");
}
}
let resp = CloseResponse {
structure_size: 60,
flags: req.flags & FLAG_POSTQUERY_ATTRIB,
reserved: 0,
creation_time: info_before_close
.as_ref()
.map(|i| i.creation_time)
.unwrap_or(0),
last_access_time: info_before_close
.as_ref()
.map(|i| i.last_access_time)
.unwrap_or(0),
last_write_time: info_before_close
.as_ref()
.map(|i| i.last_write_time)
.unwrap_or(0),
change_time: info_before_close
.as_ref()
.map(|i| i.change_time)
.unwrap_or(0),
allocation_size: info_before_close
.as_ref()
.map(|i| i.allocation_size)
.unwrap_or(0),
end_of_file: info_before_close
.as_ref()
.map(|i| i.end_of_file)
.unwrap_or(0),
file_attributes: info_before_close
.as_ref()
.map(|i| i.attributes())
.unwrap_or(0),
};
let mut buf = Vec::new();
resp.write_to(&mut buf).expect("encode");
HandlerResponse::ok(buf)
}