1use crate::security;
6use crate::task::{CurrentTask, Kernel};
7use crate::vfs::fs_args::MountParams;
8use crate::vfs::fs_node_cache::FsNodeCache;
9use crate::vfs::{
10 DirEntry, DirEntryHandle, FsNode, FsNodeFlags, FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr,
11 FsString, RenameContext,
12};
13use flyweights::FlyByteStr;
14use linked_hash_map::LinkedHashMap;
15use ref_cast::RefCast;
16use smallvec::SmallVec;
17use starnix_crypt::CryptService;
18use starnix_sync::{
19 DynamicLockDepMutex, FileOpsCore, FileSystemEntriesLock, FileSystemPermanentLock, FsRename,
20 FsRenameRecursive, FuseFsRenameLevel, LockDepMutex, LockEqualOrBefore, Locked,
21};
22use starnix_uapi::arc_key::ArcKey;
23use starnix_uapi::as_any::AsAny;
24use starnix_uapi::auth::FsCred;
25use starnix_uapi::device_id::DeviceId;
26use starnix_uapi::errors::Errno;
27use starnix_uapi::file_mode::mode;
28use starnix_uapi::mount_flags::{AtomicFileSystemFlags, FileSystemFlags};
29use starnix_uapi::{error, ino_t, statfs};
30use std::collections::HashSet;
31use std::ops::Range;
32use std::sync::atomic::Ordering;
33use std::sync::{Arc, OnceLock, Weak};
34
35#[derive(Debug, Default)]
36pub struct FileSystemRenameToken {}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum FsLockDepType {
47 Normal,
48 Recursive,
49 Fuse,
50}
51
52pub struct FileSystem {
54 pub kernel: Weak<Kernel>,
55 root: OnceLock<DirEntryHandle>,
56 ops: Box<dyn FileSystemOps>,
57
58 pub options: FileSystemOptions,
61
62 pub dev_id: DeviceId,
65
66 pub rename_mutex: DynamicLockDepMutex<FileSystemRenameToken>,
75
76 node_cache: Arc<FsNodeCache>,
86
87 dcache: DirEntryCache,
92
93 pub security_state: security::FileSystemState,
96}
97
98impl std::fmt::Debug for FileSystem {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 write!(f, "FileSystem")
101 }
102}
103
104#[derive(Debug, Default)]
105pub struct FileSystemOptions {
106 pub source: FlyByteStr,
108 pub flags: AtomicFileSystemFlags,
110 pub params: MountParams,
112}
113
114impl Clone for FileSystemOptions {
115 fn clone(&self) -> Self {
116 Self {
117 source: self.source.clone(),
118 flags: self.flags.load(Ordering::Relaxed).into(),
119 params: self.params.clone(),
120 }
121 }
122}
123
124impl FileSystemOptions {
125 pub fn source_for_display(&self) -> &FsStr {
126 if self.source.is_empty() {
127 return "none".into();
128 }
129 self.source.as_ref()
130 }
131}
132
133struct LruCache {
134 capacity: usize,
135 entries: LockDepMutex<LinkedHashMap<ArcKey<DirEntry>, ()>, FileSystemEntriesLock>,
136}
137
138enum DirEntryCache {
139 Permanent(LockDepMutex<HashSet<ArcKey<DirEntry>>, FileSystemPermanentLock>),
140 Lru(LruCache),
141 Uncached,
142}
143
144pub struct CacheConfig {
146 pub capacity: usize,
147}
148
149pub enum CacheMode {
150 Permanent,
154 Cached(CacheConfig),
156 Uncached,
159}
160
161impl FileSystem {
162 pub fn new<L>(
164 locked: &mut Locked<L>,
165 kernel: &Kernel,
166 cache_mode: CacheMode,
167 ops: impl FileSystemOps,
168 mut options: FileSystemOptions,
169 ) -> Result<FileSystemHandle, Errno>
170 where
171 L: LockEqualOrBefore<FileOpsCore>,
172 {
173 let uses_external_node_ids = ops.uses_external_node_ids();
174 let node_cache = Arc::new(FsNodeCache::new(uses_external_node_ids));
175 assert_eq!(ops.uses_external_node_ids(), node_cache.uses_external_node_ids());
176
177 let mount_options = security::sb_eat_lsm_opts(&kernel, &mut options.params)?;
178 let security_state = security::file_system_init_security(&mount_options, &ops)?;
179
180 let fs_lockdep_type = ops.fs_lockdep_type();
181
182 let file_system = Arc::new(FileSystem {
183 kernel: kernel.weak_self.clone(),
184 root: OnceLock::new(),
185 ops: Box::new(ops),
186 options,
187 dev_id: kernel.device_registry.next_anonymous_dev_id(locked),
188 rename_mutex: match fs_lockdep_type {
189 FsLockDepType::Normal => {
190 DynamicLockDepMutex::new::<FsRename>(FileSystemRenameToken::default())
191 }
192 FsLockDepType::Recursive => {
193 DynamicLockDepMutex::new::<FsRenameRecursive>(FileSystemRenameToken::default())
194 }
195 FsLockDepType::Fuse => {
196 DynamicLockDepMutex::new::<FuseFsRenameLevel>(FileSystemRenameToken::default())
197 }
198 },
199 node_cache,
200 dcache: match cache_mode {
201 CacheMode::Permanent => DirEntryCache::Permanent(Default::default()),
202 CacheMode::Cached(CacheConfig { capacity }) => {
203 DirEntryCache::Lru(LruCache { capacity, entries: Default::default() })
204 }
205 CacheMode::Uncached => DirEntryCache::Uncached,
206 },
207 security_state,
208 });
209
210 security::file_system_post_init_security(kernel, &file_system);
213
214 Ok(file_system)
215 }
216
217 fn set_root(self: &FileSystemHandle, root: FsNodeHandle) {
218 let root_dir = DirEntry::new_uncached(root, None, FsString::default());
220 assert!(
221 self.root.set(root_dir).is_ok(),
222 "FileSystem::set_root can't be called more than once"
223 );
224 }
225
226 pub fn has_permanent_entries(&self) -> bool {
227 matches!(self.dcache, DirEntryCache::Permanent(_))
228 }
229
230 pub fn fs_lockdep_type(&self) -> FsLockDepType {
232 self.ops.fs_lockdep_type()
233 }
234
235 pub fn root(&self) -> &DirEntryHandle {
239 self.root.get().unwrap_or_else(|| panic!("FileSystem {} has no root", self.name()))
240 }
241
242 pub fn maybe_root(&self) -> Option<&DirEntryHandle> {
244 self.root.get()
245 }
246
247 pub fn get_or_create_node<F>(
248 &self,
249 node_key: ino_t,
250 create_fn: F,
251 ) -> Result<FsNodeHandle, Errno>
252 where
253 F: FnOnce() -> Result<FsNodeHandle, Errno>,
254 {
255 self.get_and_validate_or_create_node(node_key, |_| true, create_fn)
256 }
257
258 pub fn get_and_validate_or_create_node<V, C>(
273 &self,
274 node_key: ino_t,
275 validate_fn: V,
276 create_fn: C,
277 ) -> Result<FsNodeHandle, Errno>
278 where
279 V: Fn(&FsNodeHandle) -> bool,
280 C: FnOnce() -> Result<FsNodeHandle, Errno>,
281 {
282 self.node_cache.get_and_validate_or_create_node(node_key, validate_fn, create_fn)
283 }
284
285 pub fn create_node_with_flags(
289 self: &Arc<Self>,
290 ino: Option<ino_t>,
291 ops: impl Into<Box<dyn FsNodeOps>>,
292 info: FsNodeInfo,
293 flags: FsNodeFlags,
294 ) -> FsNodeHandle {
295 let ino = ino.unwrap_or_else(|| self.allocate_ino());
296 let node = FsNode::new_uncached(ino, ops, self, info, flags);
297 self.node_cache.insert_node(&node);
298 node
299 }
300
301 pub fn create_node(
302 self: &Arc<Self>,
303 ino: ino_t,
304 ops: impl Into<Box<dyn FsNodeOps>>,
305 info: FsNodeInfo,
306 ) -> FsNodeHandle {
307 self.create_node_with_flags(Some(ino), ops, info, FsNodeFlags::empty())
308 }
309
310 pub fn create_node_and_allocate_node_id(
311 self: &Arc<Self>,
312 ops: impl Into<Box<dyn FsNodeOps>>,
313 info: FsNodeInfo,
314 ) -> FsNodeHandle {
315 self.create_node_with_flags(None, ops, info, FsNodeFlags::empty())
316 }
317
318 pub fn create_detached_node(
320 self: &Arc<Self>,
321 ino: ino_t,
322 ops: impl Into<Box<dyn FsNodeOps>>,
323 info: FsNodeInfo,
324 ) -> FsNodeHandle {
325 assert!(info.mode.is_dir());
326 let node = FsNode::new_uncached(ino, ops, self, info, FsNodeFlags::empty());
327 self.node_cache.insert_node(&node);
328 node
329 }
330
331 pub fn create_root(self: &Arc<Self>, ino: ino_t, ops: impl Into<Box<dyn FsNodeOps>>) {
336 let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
337 self.create_root_with_info(ino, ops, info);
338 }
339
340 pub fn create_root_with_info(
341 self: &Arc<Self>,
342 ino: ino_t,
343 ops: impl Into<Box<dyn FsNodeOps>>,
344 info: FsNodeInfo,
345 ) {
346 let node = self.create_detached_node(ino, ops, info);
347 self.set_root(node);
348 }
349
350 pub fn remove_node(&self, node: &FsNode) {
354 self.node_cache.remove_node(node);
355 }
356
357 pub fn allocate_ino(&self) -> ino_t {
358 self.node_cache
359 .allocate_ino()
360 .expect("allocate_ino called on a filesystem that uses external node IDs")
361 }
362
363 pub fn allocate_ino_range(&self, size: usize) -> Range<ino_t> {
365 self.node_cache
366 .allocate_ino_range(size)
367 .expect("allocate_ino_range called on a filesystem that uses external node IDs")
368 }
369
370 pub fn rename<L>(
375 &self,
376 locked: &mut Locked<L>,
377 current_task: &CurrentTask,
378 context: &mut RenameContext<'_>,
379 old_name: &FsStr,
380 new_name: &FsStr,
381 ) -> Result<(), Errno>
382 where
383 L: LockEqualOrBefore<FileOpsCore>,
384 {
385 let locked = locked.cast_locked::<FileOpsCore>();
386 self.ops.rename(locked, self, current_task, context, old_name, new_name)
387 }
388
389 pub fn exchange(
392 &self,
393 current_task: &CurrentTask,
394 context: &mut RenameContext<'_>,
395 name1: &FsStr,
396 name2: &FsStr,
397 ) -> Result<(), Errno> {
398 self.ops.exchange(self, current_task, context, name1, name2)
399 }
400
401 pub fn force_unmount_ops(&self) {
405 self.ops.unmount();
406 }
407
408 pub fn statfs<L>(
415 &self,
416 locked: &mut Locked<L>,
417 current_task: &CurrentTask,
418 ) -> Result<statfs, Errno>
419 where
420 L: LockEqualOrBefore<FileOpsCore>,
421 {
422 security::sb_statfs(current_task, &self)?;
423 let locked = locked.cast_locked::<FileOpsCore>();
424 let mut stat = self.ops.statfs(locked, self, current_task)?;
425 if stat.f_frsize == 0 {
426 stat.f_frsize = stat.f_bsize as i64;
427 }
428 Ok(stat)
429 }
430
431 pub fn sync<L>(&self, locked: &mut Locked<L>, current_task: &CurrentTask) -> Result<(), Errno>
432 where
433 L: LockEqualOrBefore<FileOpsCore>,
434 {
435 self.ops.sync(locked.cast_locked::<FileOpsCore>(), self, current_task)
436 }
437
438 pub fn did_create_dir_entry(&self, entry: &DirEntryHandle) {
439 match &self.dcache {
440 DirEntryCache::Permanent(p) => {
441 p.lock().insert(ArcKey(entry.clone()));
442 }
443 DirEntryCache::Lru(LruCache { entries, .. }) => {
444 entries.lock().insert(ArcKey(entry.clone()), ());
445 }
446 DirEntryCache::Uncached => {}
447 }
448 }
449
450 pub fn will_destroy_dir_entry(&self, entry: &DirEntryHandle) {
451 match &self.dcache {
452 DirEntryCache::Permanent(p) => {
453 p.lock().remove(ArcKey::ref_cast(entry));
454 }
455 DirEntryCache::Lru(LruCache { entries, .. }) => {
456 entries.lock().remove(ArcKey::ref_cast(entry));
457 }
458 DirEntryCache::Uncached => {}
459 };
460 }
461
462 pub fn did_access_dir_entry(&self, entry: &DirEntryHandle) {
464 if let DirEntryCache::Lru(LruCache { entries, .. }) = &self.dcache {
465 entries.lock().get_refresh(ArcKey::ref_cast(entry));
466 }
467 }
468
469 pub fn purge_old_entries(&self) {
474 if let DirEntryCache::Lru(l) = &self.dcache {
475 let mut purged = SmallVec::<[DirEntryHandle; 4]>::new();
476 {
477 let mut entries = l.entries.lock();
478 while entries.len() > l.capacity {
479 purged.push(entries.pop_front().unwrap().0.0);
480 }
481 }
482 std::mem::drop(purged);
484 }
485 }
486
487 pub fn downcast_ops<T: 'static>(&self) -> Option<&T> {
489 self.ops.as_ref().as_any().downcast_ref()
490 }
491
492 pub fn name(&self) -> &'static FsStr {
493 self.ops.name()
494 }
495
496 pub fn manages_timestamps(&self) -> bool {
497 self.ops.manages_timestamps()
498 }
499
500 pub fn crypt_service(&self) -> Option<Arc<CryptService>> {
504 self.ops.crypt_service()
505 }
506
507 pub fn update_flags(
511 &self,
512 current_task: &CurrentTask,
513 flags: FileSystemFlags,
514 ) -> Result<(), Errno> {
515 self.ops.update_flags(self, current_task, flags)
516 }
517}
518
519pub trait FileSystemOps: AsAny + Send + Sync + 'static {
521 fn fs_lockdep_type(&self) -> FsLockDepType {
526 FsLockDepType::Normal
527 }
528
529 fn statfs(
543 &self,
544 _locked: &mut Locked<FileOpsCore>,
545 _fs: &FileSystem,
546 _current_task: &CurrentTask,
547 ) -> Result<statfs, Errno>;
548
549 fn update_flags(
554 &self,
555 fs: &FileSystem,
556 _current_task: &CurrentTask,
557 new_flags: FileSystemFlags,
558 ) -> Result<(), Errno> {
559 fs.options.flags.store(new_flags, Ordering::Relaxed);
560 Ok(())
561 }
562
563 fn name(&self) -> &'static FsStr;
564
565 fn uses_external_node_ids(&self) -> bool {
570 false
571 }
572
573 fn rename(
588 &self,
589 _locked: &mut Locked<FileOpsCore>,
590 _fs: &FileSystem,
591 _current_task: &CurrentTask,
592 _context: &mut RenameContext<'_>,
593 _old_name: &FsStr,
594 _new_name: &FsStr,
595 ) -> Result<(), Errno> {
596 error!(EROFS)
597 }
598
599 fn exchange(
606 &self,
607 _fs: &FileSystem,
608 _current_task: &CurrentTask,
609 _context: &mut RenameContext<'_>,
610 _name1: &FsStr,
611 _name2: &FsStr,
612 ) -> Result<(), Errno> {
613 error!(EINVAL)
614 }
615
616 fn unmount(&self) {}
618
619 fn manages_timestamps(&self) -> bool {
625 false
626 }
627
628 fn crypt_service(&self) -> Option<Arc<CryptService>> {
630 None
631 }
632
633 fn sync(
634 &self,
635 _locked: &mut Locked<FileOpsCore>,
636 _fs: &FileSystem,
637 _current_task: &CurrentTask,
638 ) -> Result<(), Errno> {
639 Ok(())
640 }
641}
642
643impl Drop for FileSystem {
644 fn drop(&mut self) {
645 self.ops.unmount();
646 }
647}
648
649pub type FileSystemHandle = Arc<FileSystem>;