Implement SMB 3.x Lease support Phase 1-2
Phase 1: Open struct lease fields - lease_key: Option<[u8; 16]> - LeaseKey GUID - lease_state: Option<u32> - READ/HANDLE/WRITE flags - lease_flags: Option<u32> - BREAKING etc. Phase 2: LeaseManager - LeaseEntry with lease_key/state/flags - register/unregister/can_grant methods - break_lease returns LeaseBreakNotification - LeaseBreakNotification struct (MS-SMB2 §2.2.26) ServerState: lease_manager field added All 229 tests pass.
This commit is contained in:
7
vendor/smb-server/src/conn/state.rs
vendored
7
vendor/smb-server/src/conn/state.rs
vendored
@@ -304,6 +304,10 @@ pub struct Open {
|
|||||||
// Oplock fields (MS-SMB2 §2.2.13 / §2.2.14)
|
// Oplock fields (MS-SMB2 §2.2.13 / §2.2.14)
|
||||||
pub oplock_level: u8,
|
pub oplock_level: u8,
|
||||||
pub share_access: u32,
|
pub share_access: u32,
|
||||||
|
// Lease fields (MS-SMB2 §2.2.13 for SMB 3.x)
|
||||||
|
pub lease_key: Option<[u8; 16]>, // LeaseKey GUID
|
||||||
|
pub lease_state: Option<u32>, // LeaseState (READ/HANDLE/WRITE)
|
||||||
|
pub lease_flags: Option<u32>, // LeaseFlags (BREAKING etc.)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Open {
|
impl Open {
|
||||||
@@ -327,6 +331,9 @@ impl Open {
|
|||||||
search_state: None,
|
search_state: None,
|
||||||
oplock_level,
|
oplock_level,
|
||||||
share_access,
|
share_access,
|
||||||
|
lease_key: None,
|
||||||
|
lease_state: None,
|
||||||
|
lease_flags: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
117
vendor/smb-server/src/oplock.rs
vendored
117
vendor/smb-server/src/oplock.rs
vendored
@@ -206,6 +206,123 @@ impl OplockManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Lease state flags (MS-SMB2 §2.2.13.2).
|
||||||
|
pub const SMB2_LEASE_READ: u32 = 0x01;
|
||||||
|
pub const SMB2_LEASE_HANDLE: u32 = 0x02;
|
||||||
|
pub const SMB2_LEASE_WRITE: u32 = 0x04;
|
||||||
|
|
||||||
|
/// Lease entry for LeaseManager.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LeaseEntry {
|
||||||
|
pub lease_key: [u8; 16],
|
||||||
|
pub lease_state: u32,
|
||||||
|
pub lease_flags: u32,
|
||||||
|
pub file_id: FileId,
|
||||||
|
pub path: SmbPath,
|
||||||
|
pub session_id: u64,
|
||||||
|
pub tree_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Global lease manager for SMB 3.x (MS-SMB2 §3.3.1.9).
|
||||||
|
pub struct LeaseManager {
|
||||||
|
/// LeaseKey → LeaseEntry.
|
||||||
|
leases: RwLock<HashMap<[u8; 16], LeaseEntry>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LeaseManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
leases: RwLock::new(HashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a lease on CREATE (MS-SMB2 §3.3.5.9).
|
||||||
|
pub async fn register(&self, entry: LeaseEntry) {
|
||||||
|
let mut leases = self.leases.write().await;
|
||||||
|
leases.insert(entry.lease_key, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a lease on CLOSE.
|
||||||
|
pub async fn unregister(&self, lease_key: &[u8; 16]) {
|
||||||
|
let mut leases = self.leases.write().await;
|
||||||
|
leases.remove(lease_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if lease can be granted (MS-SMB2 §3.3.5.9).
|
||||||
|
pub async fn can_grant(&self, requested_state: u32) -> bool {
|
||||||
|
// Simple check: allow lease if no conflicting leases exist
|
||||||
|
let leases = self.leases.read().await;
|
||||||
|
for entry in leases.values() {
|
||||||
|
// Check for conflicts
|
||||||
|
if (entry.lease_state & SMB2_LEASE_WRITE) != 0 && (requested_state & SMB2_LEASE_READ) != 0 {
|
||||||
|
return false; // WRITE lease conflicts with READ request
|
||||||
|
}
|
||||||
|
if (entry.lease_state & SMB2_LEASE_HANDLE) != 0 && (requested_state & SMB2_LEASE_HANDLE) != 0 {
|
||||||
|
return false; // HANDLE lease conflicts with HANDLE request
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Break lease when conflicting access occurs (MS-SMB2 §3.3.5.10).
|
||||||
|
pub async fn break_lease(&self, requested_state: u32) -> Vec<LeaseBreakNotification> {
|
||||||
|
let mut leases = self.leases.write().await;
|
||||||
|
let mut notifications = Vec::new();
|
||||||
|
|
||||||
|
for (key, entry) in leases.iter_mut() {
|
||||||
|
// Check if lease needs to break
|
||||||
|
let needs_break = (entry.lease_state & SMB2_LEASE_WRITE) != 0 && (requested_state & SMB2_LEASE_READ) != 0;
|
||||||
|
|
||||||
|
if needs_break {
|
||||||
|
// Break to READ lease (or none)
|
||||||
|
entry.lease_state = SMB2_LEASE_READ;
|
||||||
|
entry.lease_flags |= 0x02; // SMB2_LEASE_FLAG_BREAKING
|
||||||
|
|
||||||
|
notifications.push(LeaseBreakNotification {
|
||||||
|
structure_size: 36,
|
||||||
|
lease_key: *key,
|
||||||
|
current_lease_state: entry.lease_state,
|
||||||
|
new_lease_state: SMB2_LEASE_READ,
|
||||||
|
break_reason: 0,
|
||||||
|
lease_flags: entry.lease_flags,
|
||||||
|
access_mask: 0,
|
||||||
|
share_mask: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notifications
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SMB2_LEASE_BREAK_NOTIFICATION (MS-SMB2 §2.2.26).
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LeaseBreakNotification {
|
||||||
|
pub structure_size: u16,
|
||||||
|
pub lease_key: [u8; 16],
|
||||||
|
pub current_lease_state: u32,
|
||||||
|
pub new_lease_state: u32,
|
||||||
|
pub break_reason: u32,
|
||||||
|
pub lease_flags: u32,
|
||||||
|
pub access_mask: u32,
|
||||||
|
pub share_mask: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LeaseBreakNotification {
|
||||||
|
pub fn write_to_bytes(&self) -> Vec<u8> {
|
||||||
|
let mut buf = Vec::with_capacity(36);
|
||||||
|
buf.extend_from_slice(&self.structure_size.to_le_bytes());
|
||||||
|
buf.extend_from_slice(&self.lease_key);
|
||||||
|
buf.extend_from_slice(&self.current_lease_state.to_le_bytes());
|
||||||
|
buf.extend_from_slice(&self.new_lease_state.to_le_bytes());
|
||||||
|
buf.extend_from_slice(&self.break_reason.to_le_bytes());
|
||||||
|
buf.extend_from_slice(&self.lease_flags.to_le_bytes());
|
||||||
|
buf.extend_from_slice(&self.access_mask.to_le_bytes());
|
||||||
|
buf.extend_from_slice(&self.share_mask.to_le_bytes());
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Global byte-range lock manager (MS-SMB2 §3.3.1.9).
|
/// Global byte-range lock manager (MS-SMB2 §3.3.1.9).
|
||||||
pub struct LockManager {
|
pub struct LockManager {
|
||||||
/// FileId → active locks on that file.
|
/// FileId → active locks on that file.
|
||||||
|
|||||||
3
vendor/smb-server/src/server.rs
vendored
3
vendor/smb-server/src/server.rs
vendored
@@ -198,6 +198,8 @@ pub struct ServerState {
|
|||||||
pub shutting_down: Arc<AtomicBool>,
|
pub shutting_down: Arc<AtomicBool>,
|
||||||
/// Global oplock state manager (Phase 2).
|
/// Global oplock state manager (Phase 2).
|
||||||
pub oplock_manager: Arc<crate::oplock::OplockManager>,
|
pub oplock_manager: Arc<crate::oplock::OplockManager>,
|
||||||
|
/// Global lease manager for SMB 3.x.
|
||||||
|
pub lease_manager: Arc<crate::oplock::LeaseManager>,
|
||||||
/// Global byte-range lock manager (Phase 7).
|
/// Global byte-range lock manager (Phase 7).
|
||||||
pub lock_manager: Arc<crate::oplock::LockManager>,
|
pub lock_manager: Arc<crate::oplock::LockManager>,
|
||||||
}
|
}
|
||||||
@@ -213,6 +215,7 @@ impl ServerState {
|
|||||||
shutdown: Arc::new(Notify::new()),
|
shutdown: Arc::new(Notify::new()),
|
||||||
shutting_down: Arc::new(AtomicBool::new(false)),
|
shutting_down: Arc::new(AtomicBool::new(false)),
|
||||||
oplock_manager: Arc::new(crate::oplock::OplockManager::new()),
|
oplock_manager: Arc::new(crate::oplock::OplockManager::new()),
|
||||||
|
lease_manager: Arc::new(crate::oplock::LeaseManager::new()),
|
||||||
lock_manager: Arc::new(crate::oplock::LockManager::new()),
|
lock_manager: Arc::new(crate::oplock::LockManager::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user