1#![forbid(unsafe_code)]
9#![deny(missing_docs)]
10
11use std::collections::HashMap;
12use std::collections::HashSet;
13use std::path::PathBuf;
14use std::sync::Arc;
15use std::sync::atomic::{AtomicU16, Ordering};
16
17const DHANDLE_FLAG_PERSISTENT: u32 = 0x0000_0002;
19const STYPE_IPC_SPECIAL: u32 = 0x8000_0003;
22
23use smb_server_vfs::OpenFile;
24use smb_server_vfs::Vfs;
25
26use smb_server_handle_store::HandleStore;
27
28pub fn now_ms() -> u64 {
30 std::time::SystemTime::now()
31 .duration_since(std::time::UNIX_EPOCH)
32 .map(|d| d.as_millis() as u64)
33 .unwrap_or(0)
34}
35
36#[derive(Clone)]
38pub struct Share {
39 pub name: String,
41 pub root: PathBuf,
43 pub vfs: Arc<dyn Vfs>,
45 pub is_ipc: bool,
47 pub encrypt: bool,
51 pub compress: bool,
54 pub ca: bool,
59}
60
61#[derive(Clone)]
63pub struct ServerShared {
64 pub shares: HashMap<String, Share>,
66 pub guid: [u8; 16],
68 pub domain: String,
70 pub server_name: String,
72 pub users: HashMap<String, String>,
74 pub allow_guest: bool,
76 pub require_signing: bool,
79 pub encrypt: bool,
82 pub locks: Arc<LockManager>,
85 pub share_modes: Arc<ShareModeTable>,
88 pub oplocks: Arc<OplockTable>,
91 pub leases: Arc<LeaseTable>,
94 pub durables: Arc<dyn HandleStore>,
97 pub sessions: Arc<SessionTable>,
99 pub app_instances: Arc<AppInstanceTable>,
101}
102
103pub type LockOwner = (u64, [u8; 16]);
105
106#[derive(Clone, Copy)]
107struct HeldLock {
108 offset: u64,
109 length: u64,
110 exclusive: bool,
111 owner: LockOwner,
112}
113
114fn overlaps(h: &HeldLock, off: u64, len: u64) -> bool {
116 off < h.offset.saturating_add(h.length) && h.offset < off.saturating_add(len)
117}
118
119pub struct LockManager {
123 held: std::sync::Mutex<HashMap<String, Vec<HeldLock>>>,
124 released: tokio::sync::Notify,
125}
126
127impl Default for LockManager {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133impl LockManager {
134 pub fn new() -> Self {
136 Self {
137 held: std::sync::Mutex::new(HashMap::new()),
138 released: tokio::sync::Notify::new(),
139 }
140 }
141
142 fn conflicts(existing: &HeldLock, off: u64, len: u64, excl: bool) -> bool {
147 let overlap = off < existing.offset.saturating_add(existing.length)
148 && existing.offset < off.saturating_add(len);
149 overlap && (excl || existing.exclusive)
150 }
151
152 pub fn try_acquire(&self, path: &str, ranges: &[(u64, u64, bool)], owner: LockOwner) -> bool {
155 let mut held = self.held.lock().unwrap();
156 let list = held.entry(path.to_string()).or_default();
157 let clash = ranges
158 .iter()
159 .any(|&(off, len, excl)| list.iter().any(|h| Self::conflicts(h, off, len, excl)));
160 if clash {
161 return false;
162 }
163 for &(off, len, excl) in ranges {
164 list.push(HeldLock {
165 offset: off,
166 length: len,
167 exclusive: excl,
168 owner,
169 });
170 }
171 true
172 }
173
174 pub fn write_conflict(&self, path: &str, off: u64, len: u64, owner: LockOwner) -> bool {
178 let held = self.held.lock().unwrap();
179 let Some(list) = held.get(path) else {
180 return false;
181 };
182 list.iter()
183 .any(|h| overlaps(h, off, len) && (!h.exclusive || h.owner != owner))
184 }
185
186 pub fn read_conflict(&self, path: &str, off: u64, len: u64, owner: LockOwner) -> bool {
189 let held = self.held.lock().unwrap();
190 let Some(list) = held.get(path) else {
191 return false;
192 };
193 list.iter()
194 .any(|h| overlaps(h, off, len) && h.exclusive && h.owner != owner)
195 }
196
197 pub fn release(&self, path: &str, ranges: &[(u64, u64)], owner: LockOwner) {
199 let mut held = self.held.lock().unwrap();
200 if let Some(list) = held.get_mut(path) {
201 for &(off, len) in ranges {
202 if let Some(pos) = list
203 .iter()
204 .position(|h| h.owner == owner && h.offset == off && h.length == len)
205 {
206 list.swap_remove(pos);
207 }
208 }
209 if list.is_empty() {
210 held.remove(path);
211 }
212 }
213 drop(held);
214 self.released.notify_waiters();
215 }
216
217 pub fn release_owner(&self, owner: LockOwner) {
219 let mut held = self.held.lock().unwrap();
220 held.retain(|_, list| {
221 list.retain(|h| h.owner != owner);
222 !list.is_empty()
223 });
224 drop(held);
225 self.released.notify_waiters();
226 }
227
228 pub fn release_session(&self, session_id: u64) {
230 let mut held = self.held.lock().unwrap();
231 held.retain(|_, list| {
232 list.retain(|h| h.owner.0 != session_id);
233 !list.is_empty()
234 });
235 drop(held);
236 self.released.notify_waiters();
237 }
238
239 pub fn released(&self) -> &tokio::sync::Notify {
241 &self.released
242 }
243}
244
245#[derive(Clone, Copy)]
247struct OpenMode {
248 access: u32,
249 share: u32,
250 owner: LockOwner,
251}
252
253#[derive(Default)]
256pub struct ShareModeTable {
257 opens: std::sync::Mutex<HashMap<String, Vec<OpenMode>>>,
258}
259
260impl ShareModeTable {
261 pub fn new() -> Self {
263 Self {
264 opens: std::sync::Mutex::new(HashMap::new()),
265 }
266 }
267
268 pub fn try_open(&self, path: &str, access: u32, share: u32, owner: LockOwner) -> bool {
271 let mut opens = self.opens.lock().unwrap();
272 let list = opens.entry(path.to_string()).or_default();
273 if list.iter().any(|e| !compatible(e, access, share)) {
274 if list.is_empty() {
275 opens.remove(path);
276 }
277 return false;
278 }
279 list.push(OpenMode {
280 access,
281 share,
282 owner,
283 });
284 true
285 }
286
287 pub fn close(&self, path: &str, owner: LockOwner) {
289 let mut opens = self.opens.lock().unwrap();
290 if let Some(list) = opens.get_mut(path) {
291 if let Some(pos) = list.iter().position(|e| e.owner == owner) {
292 list.swap_remove(pos);
293 }
294 if list.is_empty() {
295 opens.remove(path);
296 }
297 }
298 }
299
300 pub fn close_session(&self, session_id: u64) {
302 let mut opens = self.opens.lock().unwrap();
303 opens.retain(|_, list| {
304 list.retain(|e| e.owner.0 != session_id);
305 !list.is_empty()
306 });
307 }
308
309 pub fn open_count(&self, path: &str) -> usize {
311 self.opens.lock().unwrap().get(path).map_or(0, |l| l.len())
312 }
313
314 pub fn has_open_under(&self, dir: &str, exclude: LockOwner) -> bool {
318 let prefix = format!("{dir}/");
319 let opens = self.opens.lock().unwrap();
320 opens.iter().any(|(path, list)| {
321 if path.starts_with(&prefix) {
322 !list.is_empty()
323 } else if path.as_str() == dir {
324 list.iter().any(|e| e.owner != exclude)
325 } else {
326 false
327 }
328 })
329 }
330}
331
332struct AppOpen {
334 app_instance_id: [u8; 16],
335 path: String,
336 client_guid: [u8; 16],
337 owner: LockOwner,
338 version: Option<(u64, u64)>,
339}
340
341pub enum AppInstanceMatch {
343 None,
345 ForceClose {
347 path: String,
349 owner: LockOwner,
351 },
352 Reject,
354}
355
356#[derive(Default)]
360pub struct AppInstanceTable {
361 opens: std::sync::Mutex<Vec<AppOpen>>,
362 forced: std::sync::Mutex<HashSet<LockOwner>>,
363}
364
365impl AppInstanceTable {
366 pub fn new() -> Self {
368 Self::default()
369 }
370
371 pub fn resolve(
375 &self,
376 id: [u8; 16],
377 path: &str,
378 client_guid: [u8; 16],
379 is_311: bool,
380 req_version: Option<(u64, u64)>,
381 ) -> AppInstanceMatch {
382 let mut opens = self.opens.lock().unwrap();
383 let Some(pos) = opens
384 .iter()
385 .position(|o| o.app_instance_id == id && o.path == path && o.client_guid != client_guid)
386 else {
387 return AppInstanceMatch::None;
388 };
389 if let Some((eh, el)) = opens[pos].version {
390 match req_version {
391 Some((rh, rl)) if is_311 && (eh > rh || (eh == rh && el >= rl)) => {
395 return AppInstanceMatch::Reject;
396 }
397 None => return AppInstanceMatch::Reject,
401 _ => {}
402 }
403 }
404 let victim = opens.remove(pos);
405 self.forced.lock().unwrap().insert(victim.owner);
406 AppInstanceMatch::ForceClose { path: victim.path, owner: victim.owner }
407 }
408
409 pub fn register(
411 &self,
412 id: [u8; 16],
413 path: String,
414 client_guid: [u8; 16],
415 owner: LockOwner,
416 version: Option<(u64, u64)>,
417 ) {
418 self.opens.lock().unwrap().push(AppOpen {
419 app_instance_id: id,
420 path,
421 client_guid,
422 owner,
423 version,
424 });
425 }
426
427 pub fn take_forced(&self, owner: LockOwner) -> bool {
429 self.forced.lock().unwrap().remove(&owner)
430 }
431
432 pub fn close(&self, owner: LockOwner) {
434 self.opens.lock().unwrap().retain(|o| o.owner != owner);
435 self.forced.lock().unwrap().remove(&owner);
436 }
437
438 pub fn close_session(&self, session_id: u64) {
440 self.opens.lock().unwrap().retain(|o| o.owner.0 != session_id);
441 self.forced.lock().unwrap().retain(|o| o.0 != session_id);
442 }
443}
444
445#[derive(Clone)]
448pub struct BreakCrypto {
449 pub dialect: Option<u16>,
451 pub signing_key: Option<[u8; 16]>,
453 pub enc_keys: Option<([u8; 32], [u8; 32])>,
455 pub cipher: Option<u16>,
457 pub session_id: u64,
459 pub encrypt: bool,
461 pub signed: bool,
463}
464
465pub type LeaseBreak = ([u8; 16], u32, u32, u16, tokio::sync::mpsc::Sender<Vec<u8>>, BreakCrypto);
468
469pub struct OplockHolder {
471 pub session_id: u64,
473 pub file_id: [u8; 16],
475 pub outbound: tokio::sync::mpsc::Sender<Vec<u8>>,
477 pub crypto: BreakCrypto,
479}
480
481#[derive(Default)]
483pub struct OplockTable {
484 held: std::sync::Mutex<HashMap<String, OplockHolder>>,
485}
486
487impl OplockTable {
488 pub fn new() -> Self {
490 Self {
491 held: std::sync::Mutex::new(HashMap::new()),
492 }
493 }
494
495 pub fn grant(&self, path: &str, holder: OplockHolder) -> bool {
497 let mut held = self.held.lock().unwrap();
498 if held.contains_key(path) {
499 return false;
500 }
501 held.insert(path.to_string(), holder);
502 true
503 }
504
505 pub fn take(&self, path: &str) -> Option<OplockHolder> {
507 self.held.lock().unwrap().remove(path)
508 }
509
510 pub fn release(&self, path: &str, owner: LockOwner) -> Option<OplockHolder> {
512 let mut held = self.held.lock().unwrap();
513 if held.get(path).map(|h| (h.session_id, h.file_id)) == Some(owner) {
514 held.remove(path)
515 } else {
516 None
517 }
518 }
519
520 pub fn release_session(&self, session_id: u64) {
522 self.held
523 .lock()
524 .unwrap()
525 .retain(|_, h| h.session_id != session_id);
526 }
527}
528
529pub struct LeaseHolder {
531 pub key: [u8; 16],
533 pub state: u32,
535 pub epoch: u16,
537 pub v2: bool,
539 pub session_id: u64,
541 pub file_id: [u8; 16],
543 pub outbound: tokio::sync::mpsc::Sender<Vec<u8>>,
545 pub crypto: BreakCrypto,
547 pub client_guid: [u8; 16],
549 pub breaking: bool,
551 pub break_to: u32,
553}
554
555pub enum LeaseAckError {
557 NotFound,
559 NotBreaking,
561 StateNotAccepted,
563}
564
565#[derive(Default)]
572pub struct LeaseTable {
573 held: std::sync::Mutex<HashMap<String, Vec<LeaseHolder>>>,
574 ack_waiters: std::sync::Mutex<HashMap<[u8; 16], Vec<tokio::sync::oneshot::Sender<()>>>>,
577}
578
579impl LeaseTable {
580 pub fn new() -> Self {
582 Self {
583 held: std::sync::Mutex::new(HashMap::new()),
584 ack_waiters: std::sync::Mutex::new(HashMap::new()),
585 }
586 }
587
588 pub fn grant(&self, path: &str, holder: LeaseHolder) -> bool {
592 let mut held = self.held.lock().unwrap();
593 let holders = held.entry(path.to_string()).or_default();
594 if holders
595 .iter()
596 .any(|h| h.key == holder.key && h.file_id == holder.file_id)
597 {
598 return false;
599 }
600 holders.push(holder);
601 true
602 }
603
604 pub fn peek_key(&self, path: &str, key: [u8; 16]) -> Option<(u32, u16, bool)> {
609 self.held
610 .lock()
611 .unwrap()
612 .get(path)?
613 .iter()
614 .find(|h| h.key == key)
615 .map(|h| (h.state, h.epoch, h.v2))
616 }
617
618 pub fn has_holders(&self, path: &str) -> bool {
620 self.held
621 .lock()
622 .unwrap()
623 .get(path)
624 .is_some_and(|h| !h.is_empty())
625 }
626
627 pub fn break_conflict(
634 &self,
635 path: &str,
636 owner: LockOwner,
637 req_key: Option<[u8; 16]>,
638 clear: u32,
639 ) -> Vec<LeaseBreak> {
640 let mut held = self.held.lock().unwrap();
641 let mut breaks = Vec::new();
642 let Some(holders) = held.get_mut(path) else {
643 return breaks;
644 };
645 for h in holders.iter_mut() {
646 if (h.session_id, h.file_id) == owner || req_key == Some(h.key) {
647 continue;
648 }
649 let new_state = h.state & !clear;
650 if new_state == h.state {
651 continue;
652 }
653 let old = h.state;
654 h.state = new_state;
655 h.epoch = h.epoch.wrapping_add(1);
656 h.breaking = true;
657 h.break_to = new_state;
658 breaks.push((
659 h.key,
660 old,
661 new_state,
662 h.epoch,
663 h.outbound.clone(),
664 h.crypto.clone(),
665 ));
666 }
667 breaks
668 }
669
670 pub fn break_subtree(
676 &self,
677 dir: &str,
678 owner: LockOwner,
679 clear: u32,
680 ) -> Vec<LeaseBreak> {
681 let prefix = format!("{dir}/");
682 let mut held = self.held.lock().unwrap();
683 let mut breaks = Vec::new();
684 for (path, holders) in held.iter_mut() {
685 if path.as_str() != dir && !path.starts_with(&prefix) {
686 continue;
687 }
688 for h in holders.iter_mut() {
689 if (h.session_id, h.file_id) == owner {
690 continue;
691 }
692 let new_state = h.state & !clear;
693 if new_state == h.state {
694 continue;
695 }
696 let old = h.state;
697 h.state = new_state;
698 h.epoch = h.epoch.wrapping_add(1);
699 h.breaking = true;
700 h.break_to = new_state;
701 breaks.push((
702 h.key,
703 old,
704 new_state,
705 h.epoch,
706 h.outbound.clone(),
707 h.crypto.clone(),
708 ));
709 }
710 }
711 breaks
712 }
713
714 pub fn set_state(&self, key: [u8; 16], state: u32) {
716 let mut held = self.held.lock().unwrap();
717 if let Some(h) = held.values_mut().flatten().find(|h| h.key == key) {
718 h.state = state;
719 }
720 }
721
722 pub fn acknowledge(
726 &self,
727 client_guid: [u8; 16],
728 key: [u8; 16],
729 acked: u32,
730 ) -> Result<u32, LeaseAckError> {
731 let mut held = self.held.lock().unwrap();
732 let h = held
733 .values_mut()
734 .flatten()
735 .find(|h| h.key == key && h.client_guid == client_guid)
736 .ok_or(LeaseAckError::NotFound)?;
737 if !h.breaking {
738 return Err(LeaseAckError::NotBreaking);
739 }
740 if acked & !h.break_to != 0 {
741 return Err(LeaseAckError::StateNotAccepted);
742 }
743 h.state = acked;
744 h.breaking = false;
745 Ok(acked)
746 }
747
748 pub fn release(&self, path: &str, owner: LockOwner) -> Option<LeaseHolder> {
750 let mut held = self.held.lock().unwrap();
751 let holders = held.get_mut(path)?;
752 let idx = holders
753 .iter()
754 .position(|h| (h.session_id, h.file_id) == owner)?;
755 let removed = holders.remove(idx);
756 if holders.is_empty() {
757 held.remove(path);
758 }
759 Some(removed)
760 }
761
762 pub fn register_ack_wait(&self, key: [u8; 16]) -> tokio::sync::oneshot::Receiver<()> {
765 let (tx, rx) = tokio::sync::oneshot::channel();
766 self.ack_waiters.lock().unwrap().entry(key).or_default().push(tx);
767 rx
768 }
769
770 pub fn signal_ack(&self, key: [u8; 16]) {
772 if let Some(waiters) = self.ack_waiters.lock().unwrap().remove(&key) {
773 for w in waiters {
774 let _ = w.send(());
775 }
776 }
777 }
778
779 pub fn release_session(&self, session_id: u64) {
781 let mut held = self.held.lock().unwrap();
782 for holders in held.values_mut() {
783 holders.retain(|h| h.session_id != session_id);
784 }
785 held.retain(|_, holders| !holders.is_empty());
786 }
787}
788
789#[derive(Clone)]
793pub struct DurableEntry {
794 pub persistent_id: [u8; 16],
796 pub create_guid: [u8; 16],
798 pub rel: String,
800 pub is_dir: bool,
802 pub access: u32,
804 pub options: u32,
806 pub session_id: u64,
808 pub owner_user: String,
811 pub client_guid: [u8; 16],
813 pub lease_key: Option<[u8; 16]>,
815 pub lease_state: u32,
817 pub persistent: bool,
819 pub timeout: u32,
821 pub deadline: std::time::Instant,
823}
824
825impl DurableEntry {
826 pub fn into_record(self, now_ms: u64) -> smb_server_handle_store::HandleRecord {
829 smb_server_handle_store::HandleRecord {
830 create_guid: self.persistent_id,
831 path: self.rel,
832 share: String::new(),
833 session_id: self.session_id,
834 owner_user: self.owner_user,
835 access: self.access,
836 share_access: 0,
837 create_options: self.options,
838 is_dir: self.is_dir,
839 flags: if self.persistent {
840 DHANDLE_FLAG_PERSISTENT
841 } else {
842 0
843 },
844 match_guid: if self.create_guid == [0u8; 16] {
845 None
846 } else {
847 Some(self.create_guid)
848 },
849 owner_node: String::new(),
850 lease_key: self.lease_key,
851 lease_state: self.lease_state,
852 client_guid: self.client_guid,
853 timeout_ms: self.timeout as u64,
854 deadline_ms: if self.persistent {
855 0
856 } else {
857 now_ms + self.timeout as u64
858 },
859 delete_on_close: false,
860 }
861 }
862}
863
864#[derive(Clone)]
867pub struct SessionEntry {
868 pub session_key: Option<[u8; 16]>,
870 pub signing_key: Option<[u8; 16]>,
872 pub dialect: Option<u16>,
874 pub user: String,
876 pub guest: bool,
878 pub cipher: Option<u16>,
880 pub enc_keys: Option<([u8; 32], [u8; 32])>,
882 pub encrypt_data: bool,
884}
885
886#[derive(Default)]
888pub struct SessionTable {
889 sessions: std::sync::Mutex<HashMap<u64, SessionEntry>>,
890}
891
892impl SessionTable {
893 pub fn new() -> Self {
895 Self {
896 sessions: std::sync::Mutex::new(HashMap::new()),
897 }
898 }
899
900 pub fn insert(&self, id: u64, entry: SessionEntry) {
902 self.sessions.lock().unwrap().insert(id, entry);
903 }
904
905 pub fn get(&self, id: u64) -> Option<SessionEntry> {
907 self.sessions.lock().unwrap().get(&id).cloned()
908 }
909
910 pub fn remove(&self, id: u64) {
912 self.sessions.lock().unwrap().remove(&id);
913 }
914}
915
916mod amask {
918 pub const FILE_READ_DATA: u32 = 0x0000_0001;
919 pub const FILE_WRITE_DATA: u32 = 0x0000_0002;
920 pub const FILE_APPEND_DATA: u32 = 0x0000_0004;
921 pub const FILE_EXECUTE: u32 = 0x0000_0020;
922 pub const DELETE: u32 = 0x0001_0000;
923 pub const GENERIC_ALL: u32 = 0x1000_0000;
924 pub const GENERIC_WRITE: u32 = 0x4000_0000;
925 pub const GENERIC_READ: u32 = 0x8000_0000;
926}
927
928mod share {
930 pub const READ: u32 = 0x0000_0001;
931 pub const WRITE: u32 = 0x0000_0002;
932 pub const DELETE: u32 = 0x0000_0004;
933}
934
935fn wants_read(access: u32) -> bool {
936 access
937 & (amask::FILE_READ_DATA | amask::FILE_EXECUTE | amask::GENERIC_READ | amask::GENERIC_ALL)
938 != 0
939}
940fn wants_write(access: u32) -> bool {
941 access
942 & (amask::FILE_WRITE_DATA
943 | amask::FILE_APPEND_DATA
944 | amask::GENERIC_WRITE
945 | amask::GENERIC_ALL)
946 != 0
947}
948fn wants_delete(access: u32) -> bool {
949 access & (amask::DELETE | amask::GENERIC_ALL) != 0
950}
951
952fn compatible(existing: &OpenMode, access: u32, share: u32) -> bool {
955 (!wants_read(access) || existing.share & share::READ != 0)
956 && (!wants_read(existing.access) || share & share::READ != 0)
957 && (!wants_write(access) || existing.share & share::WRITE != 0)
958 && (!wants_write(existing.access) || share & share::WRITE != 0)
959 && (!wants_delete(access) || existing.share & share::DELETE != 0)
960 && (!wants_delete(existing.access) || share & share::DELETE != 0)
961}
962
963impl ServerShared {
964 pub fn share_infos(&self) -> Vec<crate::srvsvc::ShareInfo> {
968 let mut v: Vec<crate::srvsvc::ShareInfo> = self
969 .shares
970 .values()
971 .map(|s| crate::srvsvc::ShareInfo {
972 netname: s.name.clone(),
973 shi_type: if s.is_ipc { STYPE_IPC_SPECIAL } else { 0 },
974 remark: String::new(),
975 })
976 .collect();
977 v.sort_by(|a, b| a.netname.cmp(&b.netname));
978 v
979 }
980}
981
982#[derive(Debug, Clone)]
984pub struct Session {
985 pub user: String,
987 pub guest: bool,
989 pub trees: Vec<u16>,
991}
992
993pub struct SearchCtx {
995 pub queue: std::vec::IntoIter<String>,
997 pub level: u16,
999 pub base_dir: PathBuf,
1001}
1002
1003pub struct ConnState {
1005 pub challenge: [u8; 8],
1007 pub negotiated: bool,
1009 pub uid: u16,
1011 pub auth_pending: bool,
1013 pub upgraded_smb2: bool,
1015 pub spnego: bool,
1017 pub session: Option<Session>,
1019 pub trees: HashMap<u16, String>,
1021 pub handles: HashMap<u16, Box<OpenFile>>,
1023 pub searches: HashMap<u16, SearchCtx>,
1025}
1026
1027impl ConnState {
1028 pub fn new(challenge: [u8; 8]) -> Self {
1030 ConnState {
1031 challenge,
1032 negotiated: false,
1033 uid: 0,
1034 auth_pending: false,
1035 upgraded_smb2: false,
1036 spnego: false,
1037 session: None,
1038 trees: HashMap::new(),
1039 handles: HashMap::new(),
1040 searches: HashMap::new(),
1041 }
1042 }
1043}
1044
1045static UID: AtomicU16 = AtomicU16::new(0x0400);
1046static TID: AtomicU16 = AtomicU16::new(0x1000);
1047static FID: AtomicU16 = AtomicU16::new(0x2000);
1048static SID: AtomicU16 = AtomicU16::new(1);
1049
1050fn next_id(counter: &AtomicU16) -> u16 {
1051 loop {
1052 let v = counter.fetch_add(1, Ordering::Relaxed);
1053 if v != 0 && v != u16::MAX {
1054 return v;
1055 }
1056 }
1057}
1058
1059pub fn next_uid() -> u16 {
1061 next_id(&UID)
1062}
1063pub fn next_tid() -> u16 {
1065 next_id(&TID)
1066}
1067pub fn next_fid() -> u16 {
1069 let v = FID.fetch_add(1, Ordering::Relaxed);
1070 if v == u16::MAX {
1071 FID.fetch_add(1, Ordering::Relaxed)
1072 } else {
1073 v
1074 }
1075}
1076pub fn next_sid() -> u16 {
1078 SID.fetch_add(1, Ordering::Relaxed)
1079}
1080
1081pub type SharedServer = Arc<ServerShared>;
1083
1084#[cfg(test)]
1085mod lock_tests {
1086 use super::*;
1087
1088 fn owner(n: u8) -> LockOwner {
1089 (1, [n; 16])
1090 }
1091
1092 #[test]
1093 fn exclusive_lock_blocks_other_owner() {
1094 let m = LockManager::new();
1095 assert!(m.try_acquire("f", &[(0, 10, true)], owner(1)));
1096 assert!(!m.try_acquire("f", &[(5, 10, true)], owner(2)));
1098 assert!(m.try_acquire("f", &[(100, 10, true)], owner(2)));
1100 }
1101
1102 #[test]
1103 fn shared_locks_coexist_but_block_exclusive() {
1104 let m = LockManager::new();
1105 assert!(m.try_acquire("f", &[(0, 10, false)], owner(1)));
1106 assert!(
1107 m.try_acquire("f", &[(0, 10, false)], owner(2)),
1108 "shared+shared ok"
1109 );
1110 assert!(
1111 !m.try_acquire("f", &[(0, 10, true)], owner(3)),
1112 "shared blocks exclusive"
1113 );
1114 }
1115
1116 #[test]
1117 fn exclusive_conflicts_even_for_same_owner() {
1118 let m = LockManager::new();
1119 assert!(m.try_acquire("f", &[(0, 10, true)], owner(1)));
1120 assert!(
1123 !m.try_acquire("f", &[(0, 10, true)], owner(1)),
1124 "overlapping exclusive conflicts"
1125 );
1126 assert!(
1127 m.try_acquire("f", &[(10, 10, true)], owner(1)),
1128 "adjacent range ok"
1129 );
1130 }
1131
1132 #[test]
1133 fn release_frees_the_range() {
1134 let m = LockManager::new();
1135 assert!(m.try_acquire("f", &[(0, 10, true)], owner(1)));
1136 assert!(!m.try_acquire("f", &[(0, 10, true)], owner(2)));
1137 m.release("f", &[(0, 10)], owner(1));
1138 assert!(
1139 m.try_acquire("f", &[(0, 10, true)], owner(2)),
1140 "range freed"
1141 );
1142 }
1143
1144 #[test]
1145 fn release_session_drops_all() {
1146 let m = LockManager::new();
1147 assert!(m.try_acquire("a", &[(0, 10, true)], (7, [1; 16])));
1148 assert!(m.try_acquire("b", &[(0, 10, true)], (7, [2; 16])));
1149 m.release_session(7);
1150 assert!(m.try_acquire("a", &[(0, 10, true)], (9, [3; 16])));
1151 assert!(m.try_acquire("b", &[(0, 10, true)], (9, [4; 16])));
1152 }
1153}
1154
1155#[cfg(test)]
1156mod share_mode_tests {
1157 use super::*;
1158
1159 const GENERIC_READ: u32 = 0x8000_0000;
1160 const GENERIC_WRITE: u32 = 0x4000_0000;
1161 const SHARE_READ: u32 = 0x1;
1162 const SHARE_WRITE: u32 = 0x2;
1163
1164 #[test]
1165 fn two_shared_readers_are_compatible() {
1166 let t = ShareModeTable::new();
1167 assert!(t.try_open("f", GENERIC_READ, SHARE_READ, (1, [1; 16])));
1168 assert!(t.try_open("f", GENERIC_READ, SHARE_READ, (1, [2; 16])));
1169 }
1170
1171 #[test]
1172 fn writer_without_share_write_blocks_second_writer() {
1173 let t = ShareModeTable::new();
1174 assert!(t.try_open("f", GENERIC_WRITE, SHARE_READ, (1, [1; 16])));
1175 assert!(!t.try_open("f", GENERIC_WRITE, SHARE_READ | SHARE_WRITE, (1, [2; 16])));
1177 }
1178
1179 #[test]
1180 fn exclusive_open_blocks_everyone() {
1181 let t = ShareModeTable::new();
1182 assert!(
1183 t.try_open("f", GENERIC_READ, 0, (1, [1; 16])),
1184 "first open ok"
1185 );
1186 assert!(
1187 !t.try_open("f", GENERIC_READ, SHARE_READ, (1, [2; 16])),
1188 "no-share open blocks"
1189 );
1190 }
1191
1192 #[test]
1193 fn close_reopens_share() {
1194 let t = ShareModeTable::new();
1195 assert!(t.try_open("f", GENERIC_WRITE, 0, (1, [1; 16])));
1196 assert!(!t.try_open("f", GENERIC_READ, SHARE_READ, (1, [2; 16])));
1197 t.close("f", (1, [1; 16]));
1198 assert!(
1199 t.try_open("f", GENERIC_READ, SHARE_READ, (1, [2; 16])),
1200 "freed after close"
1201 );
1202 }
1203
1204 #[test]
1205 fn session_table_binds_and_forgets() {
1206 let t = SessionTable::new();
1207 assert!(t.get(7).is_none(), "unknown session");
1208 t.insert(
1209 7,
1210 SessionEntry {
1211 session_key: Some([9u8; 16]),
1212 signing_key: Some([8u8; 16]),
1213 dialect: Some(0x0311),
1214 user: "faraz".into(),
1215 guest: false,
1216 cipher: Some(2),
1217 enc_keys: None,
1218 encrypt_data: false,
1219 },
1220 );
1221 let e = t.get(7).expect("session present for binding");
1222 assert_eq!(
1223 e.signing_key,
1224 Some([8u8; 16]),
1225 "bound channel reuses signing key"
1226 );
1227 assert_eq!(e.dialect, Some(0x0311));
1228 t.remove(7);
1229 assert!(t.get(7).is_none(), "forgotten on logoff");
1230 }
1231}