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_logging::{log_warn, track_stub};
19use starnix_sync::{
20 DynamicLockDepMutex, FileSystemEntriesLock, FileSystemPermanentLock, FsRename,
21 FsRenameRecursive, FuseFsRenameLevel, LockDepMutex,
22};
23use starnix_uapi::arc_key::ArcKey;
24use starnix_uapi::as_any::AsAny;
25use starnix_uapi::auth::FsCred;
26use starnix_uapi::device_id::DeviceId;
27use starnix_uapi::errors::Errno;
28use starnix_uapi::file_mode::mode;
29use starnix_uapi::mount_flags::{AtomicFileSystemFlags, FileSystemFlags};
30use starnix_uapi::{error, ino_t, statfs};
31use std::collections::HashSet;
32use std::ops::Range;
33use std::sync::atomic::Ordering;
34use std::sync::{Arc, OnceLock, Weak};
35
36#[derive(Debug, Default)]
37pub struct FileSystemRenameToken {}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum FsLockDepType {
48 Normal,
49 Recursive,
50 Fuse,
51}
52
53pub struct FileSystem {
55 pub kernel: Weak<Kernel>,
56 root: OnceLock<DirEntryHandle>,
57 ops: Box<dyn FileSystemOps>,
58
59 pub options: FileSystemOptions,
62
63 pub dev_id: DeviceId,
66
67 pub rename_mutex: DynamicLockDepMutex<FileSystemRenameToken>,
76
77 node_cache: Arc<FsNodeCache>,
87
88 dcache: DirEntryCache,
93
94 pub security_state: security::FileSystemState,
97}
98
99impl std::fmt::Debug for FileSystem {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 write!(f, "FileSystem")
102 }
103}
104
105#[derive(Debug, Default)]
106pub struct FileSystemOptions {
107 pub source: FlyByteStr,
109 pub flags: AtomicFileSystemFlags,
111 pub params: MountParams,
113}
114
115impl Clone for FileSystemOptions {
116 fn clone(&self) -> Self {
117 Self {
118 source: self.source.clone(),
119 flags: self.flags.load(Ordering::Relaxed).into(),
120 params: self.params.clone(),
121 }
122 }
123}
124
125impl FileSystemOptions {
126 pub fn source_for_display(&self) -> &FsStr {
127 if self.source.is_empty() {
128 return "none".into();
129 }
130 self.source.as_ref()
131 }
132}
133
134struct LruCache {
135 capacity: usize,
136 entries: LockDepMutex<LinkedHashMap<ArcKey<DirEntry>, ()>, FileSystemEntriesLock>,
137}
138
139enum DirEntryCache {
140 Permanent(LockDepMutex<HashSet<ArcKey<DirEntry>>, FileSystemPermanentLock>),
141 Lru(LruCache),
142 Uncached,
143}
144
145pub struct CacheConfig {
147 pub capacity: usize,
148}
149
150pub enum CacheMode {
151 Permanent,
155 Cached(CacheConfig),
157 Uncached,
160}
161
162impl FileSystem {
163 pub fn new(
165 kernel: &Kernel,
166 cache_mode: CacheMode,
167 ops: impl FileSystemOps,
168 mut options: FileSystemOptions,
169 ) -> Result<FileSystemHandle, Errno> {
170 let uses_external_node_ids = ops.uses_external_node_ids();
171 let fs_lockdep_type = ops.fs_lockdep_type();
172 let node_cache = Arc::new(FsNodeCache::new(uses_external_node_ids, fs_lockdep_type));
173 assert_eq!(ops.uses_external_node_ids(), node_cache.uses_external_node_ids());
174
175 let mount_options = security::sb_eat_lsm_opts(&kernel, &mut options.params)?;
176 let security_state = security::file_system_init_security(&mount_options, &ops)?;
177
178 let file_system = Arc::new(FileSystem {
179 kernel: kernel.weak_self.clone(),
180 root: OnceLock::new(),
181 ops: Box::new(ops),
182 options,
183 dev_id: kernel.device_registry.next_anonymous_dev_id(),
184 rename_mutex: match fs_lockdep_type {
185 FsLockDepType::Normal => {
186 DynamicLockDepMutex::new::<FsRename>(FileSystemRenameToken::default())
187 }
188 FsLockDepType::Recursive => {
189 DynamicLockDepMutex::new::<FsRenameRecursive>(FileSystemRenameToken::default())
190 }
191 FsLockDepType::Fuse => {
192 DynamicLockDepMutex::new::<FuseFsRenameLevel>(FileSystemRenameToken::default())
193 }
194 },
195 node_cache,
196 dcache: match cache_mode {
197 CacheMode::Permanent => DirEntryCache::Permanent(Default::default()),
198 CacheMode::Cached(CacheConfig { capacity }) => {
199 DirEntryCache::Lru(LruCache { capacity, entries: Default::default() })
200 }
201 CacheMode::Uncached => DirEntryCache::Uncached,
202 },
203 security_state,
204 });
205
206 security::file_system_post_init_security(kernel, &file_system);
209
210 Ok(file_system)
211 }
212
213 fn set_root(self: &FileSystemHandle, root: FsNodeHandle) {
214 let root_dir = DirEntry::new_uncached(root, None, FsString::default());
216 assert!(
217 self.root.set(root_dir).is_ok(),
218 "FileSystem::set_root can't be called more than once"
219 );
220 }
221
222 pub fn has_permanent_entries(&self) -> bool {
223 matches!(self.dcache, DirEntryCache::Permanent(_))
224 }
225
226 pub fn has_casefold_support(&self) -> bool {
228 self.ops.has_casefold_support()
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(
293 self: &Arc<Self>,
294 ino: Option<ino_t>,
295 ops: impl Into<Box<dyn FsNodeOps>>,
296 mut info: FsNodeInfo,
297 flags: FsNodeFlags,
298 ) -> FsNodeHandle {
299 if let Some(blksize) = self.ops.default_blksize() {
300 info.blksize = blksize;
301 }
302 let ino = ino.unwrap_or_else(|| self.allocate_ino());
303 let node = FsNode::new_uncached(ino, ops, self, info, flags);
304 self.node_cache.insert_node(&node);
305 node
306 }
307
308 pub fn create_node(
309 self: &Arc<Self>,
310 ino: ino_t,
311 ops: impl Into<Box<dyn FsNodeOps>>,
312 info: FsNodeInfo,
313 ) -> FsNodeHandle {
314 self.create_node_with_flags(Some(ino), ops, info, FsNodeFlags::empty())
315 }
316
317 pub fn create_node_and_allocate_node_id(
318 self: &Arc<Self>,
319 ops: impl Into<Box<dyn FsNodeOps>>,
320 info: FsNodeInfo,
321 ) -> FsNodeHandle {
322 self.create_node_with_flags(None, ops, info, FsNodeFlags::empty())
323 }
324
325 pub fn create_detached_node(
327 self: &Arc<Self>,
328 ino: ino_t,
329 ops: impl Into<Box<dyn FsNodeOps>>,
330 info: FsNodeInfo,
331 ) -> FsNodeHandle {
332 assert!(info.mode.is_dir());
333 self.create_node_with_flags(Some(ino), ops, info, FsNodeFlags::empty())
334 }
335
336 pub fn create_root(self: &Arc<Self>, ino: ino_t, ops: impl Into<Box<dyn FsNodeOps>>) {
341 let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
342 self.create_root_with_info(ino, ops, info);
343 }
344
345 pub fn create_root_with_info(
346 self: &Arc<Self>,
347 ino: ino_t,
348 ops: impl Into<Box<dyn FsNodeOps>>,
349 info: FsNodeInfo,
350 ) {
351 let node = self.create_detached_node(ino, ops, info);
352 self.set_root(node);
353 }
354
355 pub fn remove_node(&self, node: &FsNode) {
359 self.node_cache.remove_node(node);
360 }
361
362 pub fn allocate_ino(&self) -> ino_t {
363 self.node_cache
364 .allocate_ino()
365 .expect("allocate_ino called on a filesystem that uses external node IDs")
366 }
367
368 pub fn allocate_ino_range(&self, size: usize) -> Range<ino_t> {
370 self.node_cache
371 .allocate_ino_range(size)
372 .expect("allocate_ino_range called on a filesystem that uses external node IDs")
373 }
374
375 pub fn rename(
380 &self,
381 current_task: &CurrentTask,
382 context: &mut RenameContext<'_>,
383 old_name: &FsStr,
384 new_name: &FsStr,
385 ) -> Result<(), Errno> {
386 self.ops.rename(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(&self, current_task: &CurrentTask) -> Result<statfs, Errno> {
415 security::sb_statfs(current_task, &self)?;
416 let mut stat = self.ops.statfs(self, current_task)?;
417 if stat.f_frsize == 0 {
418 stat.f_frsize = stat.f_bsize as i64;
419 }
420 Ok(stat)
421 }
422
423 pub fn sync(&self, current_task: &CurrentTask) -> Result<(), Errno> {
424 self.ops.sync(self, current_task)
425 }
426
427 pub fn did_create_dir_entry(&self, entry: &DirEntryHandle) {
428 match &self.dcache {
429 DirEntryCache::Permanent(p) => {
430 p.lock().insert(ArcKey(entry.clone()));
431 }
432 DirEntryCache::Lru(LruCache { entries, .. }) => {
433 entries.lock().insert(ArcKey(entry.clone()), ());
434 }
435 DirEntryCache::Uncached => {}
436 }
437 }
438
439 pub fn will_destroy_dir_entry(&self, entry: &DirEntryHandle) {
440 match &self.dcache {
441 DirEntryCache::Permanent(p) => {
442 p.lock().remove(ArcKey::ref_cast(entry));
443 }
444 DirEntryCache::Lru(LruCache { entries, .. }) => {
445 entries.lock().remove(ArcKey::ref_cast(entry));
446 }
447 DirEntryCache::Uncached => {}
448 };
449 }
450
451 pub fn did_access_dir_entry(&self, entry: &DirEntryHandle) {
453 if let DirEntryCache::Lru(LruCache { entries, .. }) = &self.dcache {
454 entries.lock().get_refresh(ArcKey::ref_cast(entry));
455 }
456 }
457
458 pub fn purge_old_entries(&self) {
463 if let DirEntryCache::Lru(l) = &self.dcache {
464 let mut purged = SmallVec::<[DirEntryHandle; 4]>::new();
465 {
466 let mut entries = l.entries.lock();
467 while entries.len() > l.capacity {
468 purged.push(entries.pop_front().unwrap().0.0);
469 }
470 }
471 std::mem::drop(purged);
473 }
474 }
475
476 pub fn downcast_ops<T: 'static>(&self) -> Option<&T> {
478 self.ops.as_ref().as_any().downcast_ref()
479 }
480
481 pub fn name(&self) -> &'static FsStr {
482 self.ops.name()
483 }
484
485 pub fn format_options(&self, kernel: &Kernel) -> Result<FsString, Errno> {
489 let mut result = FsString::from(security::sb_show_options(kernel, self)?.to_string());
490 result.extend_from_slice(&self.show_options()?);
491 Ok(result)
492 }
493
494 pub fn manages_timestamps(&self) -> bool {
495 self.ops.manages_timestamps()
496 }
497
498 pub fn crypt_service(&self) -> Option<Arc<CryptService>> {
502 self.ops.crypt_service()
503 }
504
505 pub fn reconfigure(
511 &self,
512 current_task: &CurrentTask,
513 flags: FileSystemFlags,
514 params: &MountParams,
515 ) -> Result<(), Errno> {
516 self.ops.reconfigure(self, current_task, flags, params)
517 }
518
519 pub fn show_options(&self) -> Result<FsString, Errno> {
522 self.ops.show_options(self)
523 }
524
525 pub fn sub_filesystems(&self) -> Vec<Arc<FileSystem>> {
526 self.ops.sub_filesystems()
527 }
528
529 pub fn purge_dcache(&self) {
530 if let DirEntryCache::Lru(l) = &self.dcache {
531 let entries = std::mem::take(&mut *l.entries.lock());
532 std::mem::drop(entries);
534 }
535 }
536
537 pub fn drop_backend_caches(&self) {
538 if let Err(e) = self.ops.drop_caches(self) {
539 log_warn!("drop_caches failed for filesystem {:?}: {:?}", self.name(), e);
540 }
541 }
542
543 pub fn purge_all_entries(&self) {
544 self.purge_dcache();
545 self.drop_backend_caches();
546 }
547}
548
549pub trait FileSystemOps: AsAny + Send + Sync + 'static {
551 fn fs_lockdep_type(&self) -> FsLockDepType {
556 FsLockDepType::Normal
557 }
558
559 fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno>;
573
574 fn reconfigure(
579 &self,
580 fs: &FileSystem,
581 _current_task: &CurrentTask,
582 new_flags: FileSystemFlags,
583 params: &MountParams,
584 ) -> Result<(), Errno> {
585 if !params.is_empty() {
586 track_stub!(TODO("https://fxbug.dev/322875506"), "MS_REMOUNT: Updating data");
587 }
588 fs.options.flags.store(new_flags, Ordering::Relaxed);
589 Ok(())
590 }
591
592 fn show_options(&self, fs: &FileSystem) -> Result<FsString, Errno> {
595 Ok(fs.options.params.format_options())
596 }
597
598 fn name(&self) -> &'static FsStr;
599
600 fn uses_external_node_ids(&self) -> bool {
605 false
606 }
607
608 fn has_casefold_support(&self) -> bool {
610 false
611 }
612
613 fn default_blksize(&self) -> Option<usize> {
615 None
616 }
617
618 fn rename(
633 &self,
634 _fs: &FileSystem,
635 _current_task: &CurrentTask,
636 _context: &mut RenameContext<'_>,
637 _old_name: &FsStr,
638 _new_name: &FsStr,
639 ) -> Result<(), Errno> {
640 error!(EROFS)
641 }
642
643 fn exchange(
650 &self,
651 _fs: &FileSystem,
652 _current_task: &CurrentTask,
653 _context: &mut RenameContext<'_>,
654 _name1: &FsStr,
655 _name2: &FsStr,
656 ) -> Result<(), Errno> {
657 error!(EINVAL)
658 }
659
660 fn unmount(&self) {}
662
663 fn manages_timestamps(&self) -> bool {
669 false
670 }
671
672 fn crypt_service(&self) -> Option<Arc<CryptService>> {
674 None
675 }
676
677 fn sync(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<(), Errno> {
678 Ok(())
679 }
680
681 fn drop_caches(&self, _fs: &FileSystem) -> Result<(), Errno> {
682 Ok(())
683 }
684
685 fn sub_filesystems(&self) -> Vec<Arc<FileSystem>> {
687 Vec::new()
688 }
689}
690
691impl Drop for FileSystem {
692 fn drop(&mut self) {
693 self.ops.unmount();
694 }
695}
696
697pub type FileSystemHandle = Arc<FileSystem>;