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_root(self: &Arc<Self>, ino: ino_t, ops: impl Into<Box<dyn FsNodeOps>>) {
330 let info = FsNodeInfo::new(mode!(IFDIR, 0o755), FsCred::root());
331 self.create_root_with_info(ino, ops, info);
332 }
333
334 pub fn create_root_with_info(
335 self: &Arc<Self>,
336 ino: ino_t,
337 ops: impl Into<Box<dyn FsNodeOps>>,
338 info: FsNodeInfo,
339 ) {
340 assert!(info.mode.is_dir());
341 let node = self.create_node(ino, ops, info);
342 self.set_root(node);
343 }
344
345 pub fn remove_node(&self, node: &FsNode) {
349 self.node_cache.remove_node(node);
350 }
351
352 pub fn allocate_ino(&self) -> ino_t {
353 self.node_cache
354 .allocate_ino()
355 .expect("allocate_ino called on a filesystem that uses external node IDs")
356 }
357
358 pub fn allocate_ino_range(&self, size: usize) -> Range<ino_t> {
360 self.node_cache
361 .allocate_ino_range(size)
362 .expect("allocate_ino_range called on a filesystem that uses external node IDs")
363 }
364
365 pub fn rename(
370 &self,
371 current_task: &CurrentTask,
372 context: &mut RenameContext<'_>,
373 old_name: &FsStr,
374 new_name: &FsStr,
375 ) -> Result<(), Errno> {
376 self.ops.rename(self, current_task, context, old_name, new_name)
377 }
378
379 pub fn exchange(
382 &self,
383 current_task: &CurrentTask,
384 context: &mut RenameContext<'_>,
385 name1: &FsStr,
386 name2: &FsStr,
387 ) -> Result<(), Errno> {
388 self.ops.exchange(self, current_task, context, name1, name2)
389 }
390
391 pub fn force_unmount_ops(&self) {
395 self.ops.unmount();
396 }
397
398 pub fn statfs(&self, current_task: &CurrentTask) -> Result<statfs, Errno> {
405 security::sb_statfs(current_task, &self)?;
406 let mut stat = self.ops.statfs(self, current_task)?;
407 if stat.f_frsize == 0 {
408 stat.f_frsize = stat.f_bsize as i64;
409 }
410 Ok(stat)
411 }
412
413 pub fn sync(&self, current_task: &CurrentTask) -> Result<(), Errno> {
414 self.ops.sync(self, current_task)
415 }
416
417 pub fn did_create_dir_entry(&self, entry: &DirEntryHandle) {
418 match &self.dcache {
419 DirEntryCache::Permanent(p) => {
420 p.lock().insert(ArcKey(entry.clone()));
421 }
422 DirEntryCache::Lru(LruCache { entries, .. }) => {
423 entries.lock().insert(ArcKey(entry.clone()), ());
424 }
425 DirEntryCache::Uncached => {}
426 }
427 }
428
429 pub fn will_destroy_dir_entry(&self, entry: &DirEntryHandle) {
430 match &self.dcache {
431 DirEntryCache::Permanent(p) => {
432 p.lock().remove(ArcKey::ref_cast(entry));
433 }
434 DirEntryCache::Lru(LruCache { entries, .. }) => {
435 entries.lock().remove(ArcKey::ref_cast(entry));
436 }
437 DirEntryCache::Uncached => {}
438 };
439 }
440
441 pub fn did_access_dir_entry(&self, entry: &DirEntryHandle) {
443 if let DirEntryCache::Lru(LruCache { entries, .. }) = &self.dcache {
444 entries.lock().get_refresh(ArcKey::ref_cast(entry));
445 }
446 }
447
448 pub fn purge_old_entries(&self) {
453 if let DirEntryCache::Lru(l) = &self.dcache {
454 let mut purged = SmallVec::<[DirEntryHandle; 4]>::new();
455 {
456 let mut entries = l.entries.lock();
457 while entries.len() > l.capacity {
458 purged.push(entries.pop_front().unwrap().0.0);
459 }
460 }
461 std::mem::drop(purged);
463 }
464 }
465
466 pub fn downcast_ops<T: 'static>(&self) -> Option<&T> {
468 self.ops.as_ref().as_any().downcast_ref()
469 }
470
471 pub fn name(&self) -> &'static FsStr {
472 self.ops.name()
473 }
474
475 pub fn format_options(&self, kernel: &Kernel) -> Result<FsString, Errno> {
479 let mut result = FsString::from(security::sb_show_options(kernel, self)?.to_string());
480 result.extend_from_slice(&self.show_options()?);
481 Ok(result)
482 }
483
484 pub fn manages_timestamps(&self) -> bool {
485 self.ops.manages_timestamps()
486 }
487
488 pub fn crypt_service(&self) -> Option<Arc<CryptService>> {
492 self.ops.crypt_service()
493 }
494
495 pub fn reconfigure(
501 &self,
502 current_task: &CurrentTask,
503 flags: FileSystemFlags,
504 params: &MountParams,
505 ) -> Result<(), Errno> {
506 self.ops.reconfigure(self, current_task, flags, params)
507 }
508
509 pub fn show_options(&self) -> Result<FsString, Errno> {
512 self.ops.show_options(self)
513 }
514
515 pub fn sub_filesystems(&self) -> Vec<Arc<FileSystem>> {
516 self.ops.sub_filesystems()
517 }
518
519 pub fn purge_dcache(&self) {
520 if let DirEntryCache::Lru(l) = &self.dcache {
521 let entries = std::mem::take(&mut *l.entries.lock());
522 std::mem::drop(entries);
524 }
525 }
526
527 pub fn drop_backend_caches(&self) {
528 if let Err(e) = self.ops.drop_caches(self) {
529 log_warn!("drop_caches failed for filesystem {:?}: {:?}", self.name(), e);
530 }
531 }
532
533 pub fn purge_all_entries(&self) {
534 self.purge_dcache();
535 self.drop_backend_caches();
536 }
537}
538
539pub trait FileSystemOps: AsAny + Send + Sync + 'static {
541 fn fs_lockdep_type(&self) -> FsLockDepType {
546 FsLockDepType::Normal
547 }
548
549 fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno>;
563
564 fn reconfigure(
569 &self,
570 fs: &FileSystem,
571 _current_task: &CurrentTask,
572 new_flags: FileSystemFlags,
573 params: &MountParams,
574 ) -> Result<(), Errno> {
575 if !params.is_empty() {
576 track_stub!(TODO("https://fxbug.dev/322875506"), "MS_REMOUNT: Updating data");
577 }
578 fs.options.flags.store(new_flags, Ordering::Relaxed);
579 Ok(())
580 }
581
582 fn show_options(&self, fs: &FileSystem) -> Result<FsString, Errno> {
585 Ok(fs.options.params.format_options())
586 }
587
588 fn name(&self) -> &'static FsStr;
589
590 fn uses_external_node_ids(&self) -> bool {
595 false
596 }
597
598 fn has_casefold_support(&self) -> bool {
600 false
601 }
602
603 fn default_blksize(&self) -> Option<usize> {
605 None
606 }
607
608 fn rename(
623 &self,
624 _fs: &FileSystem,
625 _current_task: &CurrentTask,
626 _context: &mut RenameContext<'_>,
627 _old_name: &FsStr,
628 _new_name: &FsStr,
629 ) -> Result<(), Errno> {
630 error!(EROFS)
631 }
632
633 fn exchange(
640 &self,
641 _fs: &FileSystem,
642 _current_task: &CurrentTask,
643 _context: &mut RenameContext<'_>,
644 _name1: &FsStr,
645 _name2: &FsStr,
646 ) -> Result<(), Errno> {
647 error!(EINVAL)
648 }
649
650 fn unmount(&self) {}
652
653 fn manages_timestamps(&self) -> bool {
659 false
660 }
661
662 fn crypt_service(&self) -> Option<Arc<CryptService>> {
664 None
665 }
666
667 fn sync(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<(), Errno> {
668 Ok(())
669 }
670
671 fn drop_caches(&self, _fs: &FileSystem) -> Result<(), Errno> {
672 Ok(())
673 }
674
675 fn sub_filesystems(&self) -> Vec<Arc<FileSystem>> {
677 Vec::new()
678 }
679}
680
681impl Drop for FileSystem {
682 fn drop(&mut self) {
683 self.ops.unmount();
684 }
685}
686
687pub type FileSystemHandle = Arc<FileSystem>;