Skip to main content

rustsmb/
session_scope.rs

1//! Per-session state shared across the channels bound to one session
2//! ([MS-SMB2] §3.3.5.5.3 multichannel): the Session.TreeConnectTable and
3//! Session.OpenTable ([MS-SMB2] §3.3.1.8).
4//!
5//! The server runs on a single `tokio_uring` thread, so channels share this
6//! state through a thread-local registry of `Rc<RefCell<SessionScope>>` keyed
7//! by session id. This avoids a `Send`/`Sync` bound — io_uring open handles are
8//! `!Send` — and needs no cross-thread locking.
9//!
10//! Handlers must never hold a `RefCell` borrow across an `.await`; async file
11//! I/O checks an `OpenFile` out of the map, awaits on the owned value, then
12//! checks it back in (see [`Smb2Conn`](crate::smb2::Smb2Conn) helpers).
13
14use std::cell::RefCell;
15use std::collections::HashMap;
16use std::rc::Rc;
17
18/// State shared by every channel bound to one session.
19#[derive(Default)]
20pub struct SessionScope {
21    /// TreeId -> share name (Session.TreeConnectTable).
22    pub trees: HashMap<u32, String>,
23    /// Open handles keyed by 16-byte SMB2 FileId (Session.OpenTable).
24    pub handles: HashMap<[u8; 16], Box<smb_server_vfs::OpenFile>>,
25    /// Per-open channel-sequence replay state keyed by FileId ([MS-SMB2]
26    /// §3.3.5.2.10), shared so a bound channel sees the same Open.ChannelSequence.
27    pub channel_seq: HashMap<[u8; 16], ChannelSeq>,
28}
29
30/// Per-open channel-sequence counters ([MS-SMB2] §3.3.5.2.10).
31#[derive(Clone, Copy, Default)]
32pub struct ChannelSeq {
33    /// Open.ChannelSequence.
34    pub channel_sequence: u16,
35    /// Open.OutstandingRequestCount.
36    pub outstanding_request_count: u32,
37    /// Open.OutstandingPreRequestCount.
38    pub outstanding_pre_request_count: u32,
39}
40
41/// Shared, per-thread handle to a session's [`SessionScope`].
42pub type ScopeRef = Rc<RefCell<SessionScope>>;
43
44thread_local! {
45    static SCOPES: RefCell<HashMap<u64, ScopeRef>> = RefCell::new(HashMap::new());
46}
47
48/// Return the scope for `session_id`, creating and registering it if absent.
49/// The first channel of a session creates it; a binding channel reuses it.
50pub fn get_or_create(session_id: u64) -> ScopeRef {
51    SCOPES.with(|s| {
52        s.borrow_mut()
53            .entry(session_id)
54            .or_insert_with(|| Rc::new(RefCell::new(SessionScope::default())))
55            .clone()
56    })
57}
58
59/// Drop the scope when the session is torn down (LOGOFF or last channel gone).
60pub fn remove(session_id: u64) {
61    SCOPES.with(|s| {
62        s.borrow_mut().remove(&session_id);
63    });
64}
65
66/// Create a fresh, unregistered scope. A new connection starts with one so
67/// pre-session state is isolated; session setup replaces it with the session's
68/// shared scope from the registry.
69pub fn detached() -> ScopeRef {
70    Rc::new(RefCell::new(SessionScope::default()))
71}