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,
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, FsRename, FsRenameRecursive, FuseFsRenameLevel,
20 LockEqualOrBefore, Locked, Mutex,
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: Mutex<LinkedHashMap<ArcKey<DirEntry>, ()>>,
136}
137
138enum DirEntryCache {
139 Permanent(Mutex<HashSet<ArcKey<DirEntry>>>),
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(Mutex::new(HashSet::new())),
202 CacheMode::Cached(CacheConfig { capacity }) => DirEntryCache::Lru(LruCache {
203 capacity,
204 entries: Mutex::new(LinkedHashMap::new()),
205 }),
206 CacheMode::Uncached => DirEntryCache::Uncached,
207 },
208 security_state,
209 });
210
211 security::file_system_post_init_security(kernel, &file_system);
214
215 Ok(file_system)
216 }
217
218 fn set_root(self: &FileSystemHandle, root: FsNodeHandle) {
219 let root_dir = DirEntry::new_uncached(root, None, FsString::default());
221 assert!(
222 self.root.set(root_dir).is_ok(),
223 "FileSystem::set_root can't be called more than once"
224 );
225 }
226
227 pub fn has_permanent_entries(&self) -> bool {
228 matches!(self.dcache, DirEntryCache::Permanent(_))
229 }
230
231 pub fn fs_lockdep_type(&self) -> FsLockDepType {
233 self.ops.fs_lockdep_type()
234 }
235
236 pub fn root(&self) -> &DirEntryHandle {
240 self.root.get().unwrap_or_else(|| panic!("FileSystem {} has no root", self.name()))
241 }
242
243 pub fn maybe_root(&self) -> Option<&DirEntryHandle> {
245 self.root.get()
246 }
247
248 pub fn get_or_create_node<F>(
249 &self,
250 node_key: ino_t,
251 create_fn: F,
252 ) -> Result<FsNodeHandle, Errno>
253 where
254 F: FnOnce() -> Result<FsNodeHandle, Errno>,
255 {
256 self.get_and_validate_or_create_node(node_key, |_| true, create_fn)
257 }
258
259 pub fn get_and_validate_or_create_node<V, C>(
274 &self,
275 node_key: ino_t,
276 validate_fn: V,
277 create_fn: C,
278 ) -> Result<FsNodeHandle, Errno>
279 where
280 V: Fn(&FsNodeHandle) -> bool,
281 C: FnOnce() -> Result<FsNodeHandle, Errno>,
282 {
283 self.node_cache.get_and_validate_or_create_node(node_key, validate_fn, create_fn)
284 }
285
286 pub fn create_node_with_flags(
290 self: &Arc<Self>,
291 ino: Option<ino_t>,
292 ops: impl Into<Box<dyn FsNodeOps>>,
293 info: FsNodeInfo,
294 flags: FsNodeFlags,
295 ) -> FsNodeHandle {
296 let ino = ino.unwrap_or_else(|| self.allocate_ino());
297 let node = FsNode::new_uncached(ino, ops, self, info, flags);
298 self.node_cache.insert_node(&node);
299 node
300 }
301
302 pub fn create_node(
303 self: &Arc<Self>,
304 ino: ino_t,
305 ops: impl Into<Box<dyn FsNodeOps>>,
306 info: FsNodeInfo,
307 ) -> FsNodeHandle {
308 self.create_node_with_flags(Some(ino), ops, info, FsNodeFlags::empty())
309 }
310
311 pub fn create_node_and_allocate_node_id(
312 self: &Arc<Self>,
313 ops: impl Into<Box<dyn FsNodeOps>>,
314 info: FsNodeInfo,
315 ) -> FsNodeHandle {
316 self.create_node_with_flags(None, ops, info, FsNodeFlags::empty())
317 }
318
319 pub fn create_detached_node(
321 self: &Arc<Self>,
322 ino: ino_t,
323 ops: impl Into<Box<dyn FsNodeOps>>,
324 info: FsNodeInfo,
325 ) -> FsNodeHandle {
326 assert!(info.mode.is_dir());
327 let node = FsNode::new_uncached(ino, ops, self, info, FsNodeFlags::empty());
328 self.node_cache.insert_node(&node);
329 node
330 }
331
332 pub fn create_root(self: &Arc<Self>, ino: ino_t, ops: impl Into<Box<dyn FsNodeOps>>) {
337 let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
338 self.create_root_with_info(ino, ops, info);
339 }
340
341 pub fn create_root_with_info(
342 self: &Arc<Self>,
343 ino: ino_t,
344 ops: impl Into<Box<dyn FsNodeOps>>,
345 info: FsNodeInfo,
346 ) {
347 let node = self.create_detached_node(ino, ops, info);
348 self.set_root(node);
349 }
350
351 pub fn remove_node(&self, node: &FsNode) {
355 self.node_cache.remove_node(node);
356 }
357
358 pub fn allocate_ino(&self) -> ino_t {
359 self.node_cache
360 .allocate_ino()
361 .expect("allocate_ino called on a filesystem that uses external node IDs")
362 }
363
364 pub fn allocate_ino_range(&self, size: usize) -> Range<ino_t> {
366 self.node_cache
367 .allocate_ino_range(size)
368 .expect("allocate_ino_range called on a filesystem that uses external node IDs")
369 }
370
371 pub fn rename<L>(
376 &self,
377 locked: &mut Locked<L>,
378 current_task: &CurrentTask,
379 old_parent: &FsNodeHandle,
380 old_name: &FsStr,
381 new_parent: &FsNodeHandle,
382 new_name: &FsStr,
383 renamed: &FsNodeHandle,
384 replaced: Option<&FsNodeHandle>,
385 ) -> Result<(), Errno>
386 where
387 L: LockEqualOrBefore<FileOpsCore>,
388 {
389 let locked = locked.cast_locked::<FileOpsCore>();
390 self.ops.rename(
391 locked,
392 self,
393 current_task,
394 old_parent,
395 old_name,
396 new_parent,
397 new_name,
398 renamed,
399 replaced,
400 )
401 }
402
403 pub fn exchange(
406 &self,
407 current_task: &CurrentTask,
408 node1: &FsNodeHandle,
409 parent1: &FsNodeHandle,
410 name1: &FsStr,
411 node2: &FsNodeHandle,
412 parent2: &FsNodeHandle,
413 name2: &FsStr,
414 ) -> Result<(), Errno> {
415 self.ops.exchange(self, current_task, node1, parent1, name1, node2, parent2, name2)
416 }
417
418 pub fn force_unmount_ops(&self) {
422 self.ops.unmount();
423 }
424
425 pub fn statfs<L>(
432 &self,
433 locked: &mut Locked<L>,
434 current_task: &CurrentTask,
435 ) -> Result<statfs, Errno>
436 where
437 L: LockEqualOrBefore<FileOpsCore>,
438 {
439 security::sb_statfs(current_task, &self)?;
440 let locked = locked.cast_locked::<FileOpsCore>();
441 let mut stat = self.ops.statfs(locked, self, current_task)?;
442 if stat.f_frsize == 0 {
443 stat.f_frsize = stat.f_bsize as i64;
444 }
445 Ok(stat)
446 }
447
448 pub fn sync<L>(&self, locked: &mut Locked<L>, current_task: &CurrentTask) -> Result<(), Errno>
449 where
450 L: LockEqualOrBefore<FileOpsCore>,
451 {
452 self.ops.sync(locked.cast_locked::<FileOpsCore>(), self, current_task)
453 }
454
455 pub fn did_create_dir_entry(&self, entry: &DirEntryHandle) {
456 match &self.dcache {
457 DirEntryCache::Permanent(p) => {
458 p.lock().insert(ArcKey(entry.clone()));
459 }
460 DirEntryCache::Lru(LruCache { entries, .. }) => {
461 entries.lock().insert(ArcKey(entry.clone()), ());
462 }
463 DirEntryCache::Uncached => {}
464 }
465 }
466
467 pub fn will_destroy_dir_entry(&self, entry: &DirEntryHandle) {
468 match &self.dcache {
469 DirEntryCache::Permanent(p) => {
470 p.lock().remove(ArcKey::ref_cast(entry));
471 }
472 DirEntryCache::Lru(LruCache { entries, .. }) => {
473 entries.lock().remove(ArcKey::ref_cast(entry));
474 }
475 DirEntryCache::Uncached => {}
476 };
477 }
478
479 pub fn did_access_dir_entry(&self, entry: &DirEntryHandle) {
481 if let DirEntryCache::Lru(LruCache { entries, .. }) = &self.dcache {
482 entries.lock().get_refresh(ArcKey::ref_cast(entry));
483 }
484 }
485
486 pub fn purge_old_entries(&self) {
491 if let DirEntryCache::Lru(l) = &self.dcache {
492 let mut purged = SmallVec::<[DirEntryHandle; 4]>::new();
493 {
494 let mut entries = l.entries.lock();
495 while entries.len() > l.capacity {
496 purged.push(entries.pop_front().unwrap().0.0);
497 }
498 }
499 std::mem::drop(purged);
501 }
502 }
503
504 pub fn downcast_ops<T: 'static>(&self) -> Option<&T> {
506 self.ops.as_ref().as_any().downcast_ref()
507 }
508
509 pub fn name(&self) -> &'static FsStr {
510 self.ops.name()
511 }
512
513 pub fn manages_timestamps(&self) -> bool {
514 self.ops.manages_timestamps()
515 }
516
517 pub fn crypt_service(&self) -> Option<Arc<CryptService>> {
521 self.ops.crypt_service()
522 }
523
524 pub fn update_flags(
528 &self,
529 current_task: &CurrentTask,
530 flags: FileSystemFlags,
531 ) -> Result<(), Errno> {
532 self.ops.update_flags(self, current_task, flags)
533 }
534}
535
536pub trait FileSystemOps: AsAny + Send + Sync + 'static {
538 fn fs_lockdep_type(&self) -> FsLockDepType {
543 FsLockDepType::Normal
544 }
545
546 fn statfs(
560 &self,
561 _locked: &mut Locked<FileOpsCore>,
562 _fs: &FileSystem,
563 _current_task: &CurrentTask,
564 ) -> Result<statfs, Errno>;
565
566 fn update_flags(
571 &self,
572 fs: &FileSystem,
573 _current_task: &CurrentTask,
574 new_flags: FileSystemFlags,
575 ) -> Result<(), Errno> {
576 fs.options.flags.store(new_flags, Ordering::Relaxed);
577 Ok(())
578 }
579
580 fn name(&self) -> &'static FsStr;
581
582 fn uses_external_node_ids(&self) -> bool {
587 false
588 }
589
590 fn rename(
605 &self,
606 _locked: &mut Locked<FileOpsCore>,
607 _fs: &FileSystem,
608 _current_task: &CurrentTask,
609 _old_parent: &FsNodeHandle,
610 _old_name: &FsStr,
611 _new_parent: &FsNodeHandle,
612 _new_name: &FsStr,
613 _renamed: &FsNodeHandle,
614 _replaced: Option<&FsNodeHandle>,
615 ) -> Result<(), Errno> {
616 error!(EROFS)
617 }
618
619 fn exchange(
620 &self,
621 _fs: &FileSystem,
622 _current_task: &CurrentTask,
623 _node1: &FsNodeHandle,
624 _parent1: &FsNodeHandle,
625 _name1: &FsStr,
626 _node2: &FsNodeHandle,
627 _parent2: &FsNodeHandle,
628 _name2: &FsStr,
629 ) -> Result<(), Errno> {
630 error!(EINVAL)
631 }
632
633 fn unmount(&self) {}
635
636 fn manages_timestamps(&self) -> bool {
642 false
643 }
644
645 fn crypt_service(&self) -> Option<Arc<CryptService>> {
647 None
648 }
649
650 fn sync(
651 &self,
652 _locked: &mut Locked<FileOpsCore>,
653 _fs: &FileSystem,
654 _current_task: &CurrentTask,
655 ) -> Result<(), Errno> {
656 Ok(())
657 }
658}
659
660impl Drop for FileSystem {
661 fn drop(&mut self) {
662 self.ops.unmount();
663 }
664}
665
666pub type FileSystemHandle = Arc<FileSystem>;