From 21a9c3c6c408f0b4054b4ad0673215ce875820f8 Mon Sep 17 00:00:00 2001 From: Warren Date: Sun, 21 Jun 2026 01:20:18 +0800 Subject: [PATCH] Implement SMB 3.x Lease support Phase 1-2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 1: Open struct lease fields - lease_key: Option<[u8; 16]> - LeaseKey GUID - lease_state: Option - READ/HANDLE/WRITE flags - lease_flags: Option - 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. --- vendor/smb-server/src/conn/state.rs | 7 ++ vendor/smb-server/src/oplock.rs | 117 ++++++++++++++++++++++++++++ vendor/smb-server/src/server.rs | 3 + 3 files changed, 127 insertions(+) diff --git a/vendor/smb-server/src/conn/state.rs b/vendor/smb-server/src/conn/state.rs index b4cc4c2..00c7ce8 100644 --- a/vendor/smb-server/src/conn/state.rs +++ b/vendor/smb-server/src/conn/state.rs @@ -304,6 +304,10 @@ pub struct Open { // Oplock fields (MS-SMB2 §2.2.13 / §2.2.14) pub oplock_level: u8, 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, // LeaseState (READ/HANDLE/WRITE) + pub lease_flags: Option, // LeaseFlags (BREAKING etc.) } impl Open { @@ -327,6 +331,9 @@ impl Open { search_state: None, oplock_level, share_access, + lease_key: None, + lease_state: None, + lease_flags: None, } } } diff --git a/vendor/smb-server/src/oplock.rs b/vendor/smb-server/src/oplock.rs index 61f84de..820e1bd 100644 --- a/vendor/smb-server/src/oplock.rs +++ b/vendor/smb-server/src/oplock.rs @@ -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>, +} + +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 { + 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 { + 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). pub struct LockManager { /// FileId → active locks on that file. diff --git a/vendor/smb-server/src/server.rs b/vendor/smb-server/src/server.rs index 446b531..e200b34 100644 --- a/vendor/smb-server/src/server.rs +++ b/vendor/smb-server/src/server.rs @@ -198,6 +198,8 @@ pub struct ServerState { pub shutting_down: Arc, /// Global oplock state manager (Phase 2). pub oplock_manager: Arc, + /// Global lease manager for SMB 3.x. + pub lease_manager: Arc, /// Global byte-range lock manager (Phase 7). pub lock_manager: Arc, } @@ -213,6 +215,7 @@ impl ServerState { shutdown: Arc::new(Notify::new()), shutting_down: Arc::new(AtomicBool::new(false)), oplock_manager: Arc::new(crate::oplock::OplockManager::new()), + lease_manager: Arc::new(crate::oplock::LeaseManager::new()), lock_manager: Arc::new(crate::oplock::LockManager::new()), } }