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(LockDepMutex::new(HashSet::new())),
202 CacheMode::Cached(CacheConfig { capacity }) => DirEntryCache::Lru(LruCache {
203 capacity,
204 entries: LockDepMutex::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 context: &mut RenameContext<'_>,
380 old_name: &FsStr,
381 new_name: &FsStr,
382 ) -> Result<(), Errno>
383 where
384 L: LockEqualOrBefore<FileOpsCore>,
385 {
386 let locked = locked.cast_locked::<FileOpsCore>();
387 self.ops.rename(locked, self, current_task, context, old_name, new_name)
388 }
389
390 pub fn exchange(
393 &self,
394 current_task: &CurrentTask,
395 context: &mut RenameContext<'_>,
396 name1: &FsStr,
397 name2: &FsStr,
398 ) -> Result<(), Errno> {
399 self.ops.exchange(self, current_task, context, name1, name2)
400 }
401
402 pub fn force_unmount_ops(&self) {
406 self.ops.unmount();
407 }
408
409 pub fn statfs<L>(
416 &self,
417 locked: &mut Locked<L>,
418 current_task: &CurrentTask,
419 ) -> Result<statfs, Errno>
420 where
421 L: LockEqualOrBefore<FileOpsCore>,
422 {
423 security::sb_statfs(current_task, &self)?;
424 let locked = locked.cast_locked::<FileOpsCore>();
425 let mut stat = self.ops.statfs(locked, self, current_task)?;
426 if stat.f_frsize == 0 {
427 stat.f_frsize = stat.f_bsize as i64;
428 }
429 Ok(stat)
430 }
431
432 pub fn sync<L>(&self, locked: &mut Locked<L>, current_task: &CurrentTask) -> Result<(), Errno>
433 where
434 L: LockEqualOrBefore<FileOpsCore>,
435 {
436 self.ops.sync(locked.cast_locked::<FileOpsCore>(), self, current_task)
437 }
438
439 pub fn did_create_dir_entry(&self, entry: &DirEntryHandle) {
440 match &self.dcache {
441 DirEntryCache::Permanent(p) => {
442 p.lock().insert(ArcKey(entry.clone()));
443 }
444 DirEntryCache::Lru(LruCache { entries, .. }) => {
445 entries.lock().insert(ArcKey(entry.clone()), ());
446 }
447 DirEntryCache::Uncached => {}
448 }
449 }
450
451 pub fn will_destroy_dir_entry(&self, entry: &DirEntryHandle) {
452 match &self.dcache {
453 DirEntryCache::Permanent(p) => {
454 p.lock().remove(ArcKey::ref_cast(entry));
455 }
456 DirEntryCache::Lru(LruCache { entries, .. }) => {
457 entries.lock().remove(ArcKey::ref_cast(entry));
458 }
459 DirEntryCache::Uncached => {}
460 };
461 }
462
463 pub fn did_access_dir_entry(&self, entry: &DirEntryHandle) {
465 if let DirEntryCache::Lru(LruCache { entries, .. }) = &self.dcache {
466 entries.lock().get_refresh(ArcKey::ref_cast(entry));
467 }
468 }
469
470 pub fn purge_old_entries(&self) {
475 if let DirEntryCache::Lru(l) = &self.dcache {
476 let mut purged = SmallVec::<[DirEntryHandle; 4]>::new();
477 {
478 let mut entries = l.entries.lock();
479 while entries.len() > l.capacity {
480 purged.push(entries.pop_front().unwrap().0.0);
481 }
482 }
483 std::mem::drop(purged);
485 }
486 }
487
488 pub fn downcast_ops<T: 'static>(&self) -> Option<&T> {
490 self.ops.as_ref().as_any().downcast_ref()
491 }
492
493 pub fn name(&self) -> &'static FsStr {
494 self.ops.name()
495 }
496
497 pub fn manages_timestamps(&self) -> bool {
498 self.ops.manages_timestamps()
499 }
500
501 pub fn crypt_service(&self) -> Option<Arc<CryptService>> {
505 self.ops.crypt_service()
506 }
507
508 pub fn update_flags(
512 &self,
513 current_task: &CurrentTask,
514 flags: FileSystemFlags,
515 ) -> Result<(), Errno> {
516 self.ops.update_flags(self, current_task, flags)
517 }
518}
519
520pub trait FileSystemOps: AsAny + Send + Sync + 'static {
522 fn fs_lockdep_type(&self) -> FsLockDepType {
527 FsLockDepType::Normal
528 }
529
530 fn statfs(
544 &self,
545 _locked: &mut Locked<FileOpsCore>,
546 _fs: &FileSystem,
547 _current_task: &CurrentTask,
548 ) -> Result<statfs, Errno>;
549
550 fn update_flags(
555 &self,
556 fs: &FileSystem,
557 _current_task: &CurrentTask,
558 new_flags: FileSystemFlags,
559 ) -> Result<(), Errno> {
560 fs.options.flags.store(new_flags, Ordering::Relaxed);
561 Ok(())
562 }
563
564 fn name(&self) -> &'static FsStr;
565
566 fn uses_external_node_ids(&self) -> bool {
571 false
572 }
573
574 fn rename(
589 &self,
590 _locked: &mut Locked<FileOpsCore>,
591 _fs: &FileSystem,
592 _current_task: &CurrentTask,
593 _context: &mut RenameContext<'_>,
594 _old_name: &FsStr,
595 _new_name: &FsStr,
596 ) -> Result<(), Errno> {
597 error!(EROFS)
598 }
599
600 fn exchange(
607 &self,
608 _fs: &FileSystem,
609 _current_task: &CurrentTask,
610 _context: &mut RenameContext<'_>,
611 _name1: &FsStr,
612 _name2: &FsStr,
613 ) -> Result<(), Errno> {
614 error!(EINVAL)
615 }
616
617 fn unmount(&self) {}
619
620 fn manages_timestamps(&self) -> bool {
626 false
627 }
628
629 fn crypt_service(&self) -> Option<Arc<CryptService>> {
631 None
632 }
633
634 fn sync(
635 &self,
636 _locked: &mut Locked<FileOpsCore>,
637 _fs: &FileSystem,
638 _current_task: &CurrentTask,
639 ) -> Result<(), Errno> {
640 Ok(())
641 }
642}
643
644impl Drop for FileSystem {
645 fn drop(&mut self) {
646 self.ops.unmount();
647 }
648}
649
650pub type FileSystemHandle = Arc<FileSystem>;