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}