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;
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 node_cache = Arc::new(FsNodeCache::new(uses_external_node_ids));
172 assert_eq!(ops.uses_external_node_ids(), node_cache.uses_external_node_ids());
173
174 let mount_options = security::sb_eat_lsm_opts(&kernel, &mut options.params)?;
175 let security_state = security::file_system_init_security(&mount_options, &ops)?;
176
177 let fs_lockdep_type = ops.fs_lockdep_type();
178
179 let file_system = Arc::new(FileSystem {
180 kernel: kernel.weak_self.clone(),
181 root: OnceLock::new(),
182 ops: Box::new(ops),
183 options,
184 dev_id: kernel.device_registry.next_anonymous_dev_id(),
185 rename_mutex: match fs_lockdep_type {
186 FsLockDepType::Normal => {
187 DynamicLockDepMutex::new::<FsRename>(FileSystemRenameToken::default())
188 }
189 FsLockDepType::Recursive => {
190 DynamicLockDepMutex::new::<FsRenameRecursive>(FileSystemRenameToken::default())
191 }
192 FsLockDepType::Fuse => {
193 DynamicLockDepMutex::new::<FuseFsRenameLevel>(FileSystemRenameToken::default())
194 }
195 },
196 node_cache,
197 dcache: match cache_mode {
198 CacheMode::Permanent => DirEntryCache::Permanent(Default::default()),
199 CacheMode::Cached(CacheConfig { capacity }) => {
200 DirEntryCache::Lru(LruCache { capacity, entries: Default::default() })
201 }
202 CacheMode::Uncached => DirEntryCache::Uncached,
203 },
204 security_state,
205 });
206
207 security::file_system_post_init_security(kernel, &file_system);
210
211 Ok(file_system)
212 }
213
214 fn set_root(self: &FileSystemHandle, root: FsNodeHandle) {
215 let root_dir = DirEntry::new_uncached(root, None, FsString::default());
217 assert!(
218 self.root.set(root_dir).is_ok(),
219 "FileSystem::set_root can't be called more than once"
220 );
221 }
222
223 pub fn has_permanent_entries(&self) -> bool {
224 matches!(self.dcache, DirEntryCache::Permanent(_))
225 }
226
227 pub fn has_casefold_support(&self) -> bool {
229 self.ops.has_casefold_support()
230 }
231
232 pub fn fs_lockdep_type(&self) -> FsLockDepType {
234 self.ops.fs_lockdep_type()
235 }
236
237 pub fn root(&self) -> &DirEntryHandle {
241 self.root.get().unwrap_or_else(|| panic!("FileSystem {} has no root", self.name()))
242 }
243
244 pub fn maybe_root(&self) -> Option<&DirEntryHandle> {
246 self.root.get()
247 }
248
249 pub fn get_or_create_node<F>(
250 &self,
251 node_key: ino_t,
252 create_fn: F,
253 ) -> Result<FsNodeHandle, Errno>
254 where
255 F: FnOnce() -> Result<FsNodeHandle, Errno>,
256 {
257 self.get_and_validate_or_create_node(node_key, |_| true, create_fn)
258 }
259
260 pub fn get_and_validate_or_create_node<V, C>(
275 &self,
276 node_key: ino_t,
277 validate_fn: V,
278 create_fn: C,
279 ) -> Result<FsNodeHandle, Errno>
280 where
281 V: Fn(&FsNodeHandle) -> bool,
282 C: FnOnce() -> Result<FsNodeHandle, Errno>,
283 {
284 self.node_cache.get_and_validate_or_create_node(node_key, validate_fn, create_fn)
285 }
286
287 pub fn create_node_with_flags(
291 self: &Arc<Self>,
292 ino: Option<ino_t>,
293 ops: impl Into<Box<dyn FsNodeOps>>,
294 info: FsNodeInfo,
295 flags: FsNodeFlags,
296 ) -> FsNodeHandle {
297 let ino = ino.unwrap_or_else(|| self.allocate_ino());
298 let node = FsNode::new_uncached(ino, ops, self, info, flags);
299 self.node_cache.insert_node(&node);
300 node
301 }
302
303 pub fn create_node(
304 self: &Arc<Self>,
305 ino: ino_t,
306 ops: impl Into<Box<dyn FsNodeOps>>,
307 info: FsNodeInfo,
308 ) -> FsNodeHandle {
309 self.create_node_with_flags(Some(ino), ops, info, FsNodeFlags::empty())
310 }
311
312 pub fn create_node_and_allocate_node_id(
313 self: &Arc<Self>,
314 ops: impl Into<Box<dyn FsNodeOps>>,
315 info: FsNodeInfo,
316 ) -> FsNodeHandle {
317 self.create_node_with_flags(None, ops, info, FsNodeFlags::empty())
318 }
319
320 pub fn create_detached_node(
322 self: &Arc<Self>,
323 ino: ino_t,
324 ops: impl Into<Box<dyn FsNodeOps>>,
325 info: FsNodeInfo,
326 ) -> FsNodeHandle {
327 assert!(info.mode.is_dir());
328 let node = FsNode::new_uncached(ino, ops, self, info, FsNodeFlags::empty());
329 self.node_cache.insert_node(&node);
330 node
331 }
332
333 pub fn create_root(self: &Arc<Self>, ino: ino_t, ops: impl Into<Box<dyn FsNodeOps>>) {
338 let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
339 self.create_root_with_info(ino, ops, info);
340 }
341
342 pub fn create_root_with_info(
343 self: &Arc<Self>,
344 ino: ino_t,
345 ops: impl Into<Box<dyn FsNodeOps>>,
346 info: FsNodeInfo,
347 ) {
348 let node = self.create_detached_node(ino, ops, info);
349 self.set_root(node);
350 }
351
352 pub fn remove_node(&self, node: &FsNode) {
356 self.node_cache.remove_node(node);
357 }
358
359 pub fn allocate_ino(&self) -> ino_t {
360 self.node_cache
361 .allocate_ino()
362 .expect("allocate_ino called on a filesystem that uses external node IDs")
363 }
364
365 pub fn allocate_ino_range(&self, size: usize) -> Range<ino_t> {
367 self.node_cache
368 .allocate_ino_range(size)
369 .expect("allocate_ino_range called on a filesystem that uses external node IDs")
370 }
371
372 pub fn rename(
377 &self,
378 current_task: &CurrentTask,
379 context: &mut RenameContext<'_>,
380 old_name: &FsStr,
381 new_name: &FsStr,
382 ) -> Result<(), Errno> {
383 self.ops.rename(self, current_task, context, old_name, new_name)
384 }
385
386 pub fn exchange(
389 &self,
390 current_task: &CurrentTask,
391 context: &mut RenameContext<'_>,
392 name1: &FsStr,
393 name2: &FsStr,
394 ) -> Result<(), Errno> {
395 self.ops.exchange(self, current_task, context, name1, name2)
396 }
397
398 pub fn force_unmount_ops(&self) {
402 self.ops.unmount();
403 }
404
405 pub fn statfs(&self, current_task: &CurrentTask) -> Result<statfs, Errno> {
412 security::sb_statfs(current_task, &self)?;
413 let mut stat = self.ops.statfs(self, current_task)?;
414 if stat.f_frsize == 0 {
415 stat.f_frsize = stat.f_bsize as i64;
416 }
417 Ok(stat)
418 }
419
420 pub fn sync(&self, current_task: &CurrentTask) -> Result<(), Errno> {
421 self.ops.sync(self, current_task)
422 }
423
424 pub fn did_create_dir_entry(&self, entry: &DirEntryHandle) {
425 match &self.dcache {
426 DirEntryCache::Permanent(p) => {
427 p.lock().insert(ArcKey(entry.clone()));
428 }
429 DirEntryCache::Lru(LruCache { entries, .. }) => {
430 entries.lock().insert(ArcKey(entry.clone()), ());
431 }
432 DirEntryCache::Uncached => {}
433 }
434 }
435
436 pub fn will_destroy_dir_entry(&self, entry: &DirEntryHandle) {
437 match &self.dcache {
438 DirEntryCache::Permanent(p) => {
439 p.lock().remove(ArcKey::ref_cast(entry));
440 }
441 DirEntryCache::Lru(LruCache { entries, .. }) => {
442 entries.lock().remove(ArcKey::ref_cast(entry));
443 }
444 DirEntryCache::Uncached => {}
445 };
446 }
447
448 pub fn did_access_dir_entry(&self, entry: &DirEntryHandle) {
450 if let DirEntryCache::Lru(LruCache { entries, .. }) = &self.dcache {
451 entries.lock().get_refresh(ArcKey::ref_cast(entry));
452 }
453 }
454
455 pub fn purge_old_entries(&self) {
460 if let DirEntryCache::Lru(l) = &self.dcache {
461 let mut purged = SmallVec::<[DirEntryHandle; 4]>::new();
462 {
463 let mut entries = l.entries.lock();
464 while entries.len() > l.capacity {
465 purged.push(entries.pop_front().unwrap().0.0);
466 }
467 }
468 std::mem::drop(purged);
470 }
471 }
472
473 pub fn downcast_ops<T: 'static>(&self) -> Option<&T> {
475 self.ops.as_ref().as_any().downcast_ref()
476 }
477
478 pub fn name(&self) -> &'static FsStr {
479 self.ops.name()
480 }
481
482 pub fn manages_timestamps(&self) -> bool {
483 self.ops.manages_timestamps()
484 }
485
486 pub fn crypt_service(&self) -> Option<Arc<CryptService>> {
490 self.ops.crypt_service()
491 }
492
493 pub fn update_flags(
497 &self,
498 current_task: &CurrentTask,
499 flags: FileSystemFlags,
500 ) -> Result<(), Errno> {
501 self.ops.update_flags(self, current_task, flags)
502 }
503
504 pub fn sub_filesystems(&self) -> Vec<Arc<FileSystem>> {
505 self.ops.sub_filesystems()
506 }
507
508 pub fn purge_dcache(&self) {
509 if let DirEntryCache::Lru(l) = &self.dcache {
510 let entries = std::mem::take(&mut *l.entries.lock());
511 std::mem::drop(entries);
513 }
514 }
515
516 pub fn drop_backend_caches(&self) {
517 if let Err(e) = self.ops.drop_caches(self) {
518 log_warn!("drop_caches failed for filesystem {:?}: {:?}", self.name(), e);
519 }
520 }
521
522 pub fn purge_all_entries(&self) {
523 self.purge_dcache();
524 self.drop_backend_caches();
525 }
526}
527
528pub trait FileSystemOps: AsAny + Send + Sync + 'static {
530 fn fs_lockdep_type(&self) -> FsLockDepType {
535 FsLockDepType::Normal
536 }
537
538 fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno>;
552
553 fn update_flags(
558 &self,
559 fs: &FileSystem,
560 _current_task: &CurrentTask,
561 new_flags: FileSystemFlags,
562 ) -> Result<(), Errno> {
563 fs.options.flags.store(new_flags, Ordering::Relaxed);
564 Ok(())
565 }
566
567 fn name(&self) -> &'static FsStr;
568
569 fn uses_external_node_ids(&self) -> bool {
574 false
575 }
576
577 fn has_casefold_support(&self) -> bool {
579 false
580 }
581
582 fn rename(
597 &self,
598 _fs: &FileSystem,
599 _current_task: &CurrentTask,
600 _context: &mut RenameContext<'_>,
601 _old_name: &FsStr,
602 _new_name: &FsStr,
603 ) -> Result<(), Errno> {
604 error!(EROFS)
605 }
606
607 fn exchange(
614 &self,
615 _fs: &FileSystem,
616 _current_task: &CurrentTask,
617 _context: &mut RenameContext<'_>,
618 _name1: &FsStr,
619 _name2: &FsStr,
620 ) -> Result<(), Errno> {
621 error!(EINVAL)
622 }
623
624 fn unmount(&self) {}
626
627 fn manages_timestamps(&self) -> bool {
633 false
634 }
635
636 fn crypt_service(&self) -> Option<Arc<CryptService>> {
638 None
639 }
640
641 fn sync(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<(), Errno> {
642 Ok(())
643 }
644
645 fn drop_caches(&self, _fs: &FileSystem) -> Result<(), Errno> {
646 Ok(())
647 }
648
649 fn sub_filesystems(&self) -> Vec<Arc<FileSystem>> {
651 Vec::new()
652 }
653}
654
655impl Drop for FileSystem {
656 fn drop(&mut self) {
657 self.ops.unmount();
658 }
659}
660
661pub type FileSystemHandle = Arc<FileSystem>;