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, FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr, FsString,
11};
12use flyweights::FlyByteStr;
13use linked_hash_map::LinkedHashMap;
14use ref_cast::RefCast;
15use smallvec::SmallVec;
16use starnix_crypt::CryptService;
17use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Mutex};
18use starnix_uapi::arc_key::ArcKey;
19use starnix_uapi::as_any::AsAny;
20use starnix_uapi::auth::FsCred;
21use starnix_uapi::device_id::DeviceId;
22use starnix_uapi::errors::Errno;
23use starnix_uapi::file_mode::mode;
24use starnix_uapi::mount_flags::FileSystemFlags;
25use starnix_uapi::{error, ino_t, statfs};
26use std::collections::HashSet;
27use std::ops::Range;
28use std::sync::{Arc, OnceLock, Weak};
29
30pub struct FileSystem {
32 pub kernel: Weak<Kernel>,
33 root: OnceLock<DirEntryHandle>,
34 ops: Box<dyn FileSystemOps>,
35
36 pub options: FileSystemOptions,
39
40 pub dev_id: DeviceId,
43
44 pub rename_mutex: Mutex<()>,
53
54 node_cache: Arc<FsNodeCache>,
64
65 dcache: DirEntryCache,
70
71 pub security_state: security::FileSystemState,
74}
75
76impl std::fmt::Debug for FileSystem {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 write!(f, "FileSystem")
79 }
80}
81
82#[derive(Clone, Debug, Default)]
83pub struct FileSystemOptions {
84 pub source: FlyByteStr,
86 pub flags: FileSystemFlags,
88 pub params: MountParams,
90}
91
92impl FileSystemOptions {
93 pub fn source_for_display(&self) -> &FsStr {
94 if self.source.is_empty() {
95 return "none".into();
96 }
97 self.source.as_ref()
98 }
99}
100
101struct LruCache {
102 capacity: usize,
103 entries: Mutex<LinkedHashMap<ArcKey<DirEntry>, ()>>,
104}
105
106enum DirEntryCache {
107 Permanent(Mutex<HashSet<ArcKey<DirEntry>>>),
108 Lru(LruCache),
109 Uncached,
110}
111
112pub struct CacheConfig {
114 pub capacity: usize,
115}
116
117pub enum CacheMode {
118 Permanent,
122 Cached(CacheConfig),
124 Uncached,
127}
128
129impl FileSystem {
130 pub fn new<L>(
132 locked: &mut Locked<L>,
133 kernel: &Kernel,
134 cache_mode: CacheMode,
135 ops: impl FileSystemOps,
136 mut options: FileSystemOptions,
137 ) -> Result<FileSystemHandle, Errno>
138 where
139 L: LockEqualOrBefore<FileOpsCore>,
140 {
141 let uses_external_node_ids = ops.uses_external_node_ids();
142 let node_cache = Arc::new(FsNodeCache::new(uses_external_node_ids));
143 assert_eq!(ops.uses_external_node_ids(), node_cache.uses_external_node_ids());
144
145 let mount_options = security::sb_eat_lsm_opts(&kernel, &mut options.params)?;
146 let security_state = security::file_system_init_security(&mount_options, &ops)?;
147
148 if !ops.is_readonly() {
151 options.flags &= !FileSystemFlags::RDONLY;
154 }
155
156 let file_system = Arc::new(FileSystem {
157 kernel: kernel.weak_self.clone(),
158 root: OnceLock::new(),
159 ops: Box::new(ops),
160 options,
161 dev_id: kernel.device_registry.next_anonymous_dev_id(locked),
162 rename_mutex: Mutex::new(()),
163 node_cache,
164 dcache: match cache_mode {
165 CacheMode::Permanent => DirEntryCache::Permanent(Mutex::new(HashSet::new())),
166 CacheMode::Cached(CacheConfig { capacity }) => DirEntryCache::Lru(LruCache {
167 capacity,
168 entries: Mutex::new(LinkedHashMap::new()),
169 }),
170 CacheMode::Uncached => DirEntryCache::Uncached,
171 },
172 security_state,
173 });
174
175 security::file_system_post_init_security(kernel, &file_system);
178
179 Ok(file_system)
180 }
181
182 fn set_root(self: &FileSystemHandle, root: FsNodeHandle) {
183 let root_dir = DirEntry::new_uncached(root, None, FsString::default());
185 assert!(
186 self.root.set(root_dir).is_ok(),
187 "FileSystem::set_root can't be called more than once"
188 );
189 }
190
191 pub fn has_permanent_entries(&self) -> bool {
192 matches!(self.dcache, DirEntryCache::Permanent(_))
193 }
194
195 pub fn root(&self) -> &DirEntryHandle {
199 self.root.get().unwrap_or_else(|| panic!("FileSystem {} has no root", self.name()))
200 }
201
202 pub fn maybe_root(&self) -> Option<&DirEntryHandle> {
204 self.root.get()
205 }
206
207 pub fn get_or_create_node<F>(
208 &self,
209 node_key: ino_t,
210 create_fn: F,
211 ) -> Result<FsNodeHandle, Errno>
212 where
213 F: FnOnce() -> Result<FsNodeHandle, Errno>,
214 {
215 self.get_and_validate_or_create_node(node_key, |_| true, create_fn)
216 }
217
218 pub fn get_and_validate_or_create_node<V, C>(
233 &self,
234 node_key: ino_t,
235 validate_fn: V,
236 create_fn: C,
237 ) -> Result<FsNodeHandle, Errno>
238 where
239 V: Fn(&FsNodeHandle) -> bool,
240 C: FnOnce() -> Result<FsNodeHandle, Errno>,
241 {
242 self.node_cache.get_and_validate_or_create_node(node_key, validate_fn, create_fn)
243 }
244
245 pub fn create_node(
249 self: &Arc<Self>,
250 ino: ino_t,
251 ops: impl Into<Box<dyn FsNodeOps>>,
252 info: FsNodeInfo,
253 ) -> FsNodeHandle {
254 let node = FsNode::new_uncached(ino, ops, self, info);
255 self.node_cache.insert_node(&node);
256 node
257 }
258
259 pub fn create_node_and_allocate_node_id(
260 self: &Arc<Self>,
261 ops: impl Into<Box<dyn FsNodeOps>>,
262 info: FsNodeInfo,
263 ) -> FsNodeHandle {
264 let ino = self.allocate_ino();
265 self.create_node(ino, ops, info)
266 }
267
268 pub fn create_detached_node(
270 self: &Arc<Self>,
271 ino: ino_t,
272 ops: impl Into<Box<dyn FsNodeOps>>,
273 info: FsNodeInfo,
274 ) -> FsNodeHandle {
275 assert!(info.mode.is_dir());
276 let node = FsNode::new_uncached(ino, ops, self, info);
277 self.node_cache.insert_node(&node);
278 node
279 }
280
281 pub fn create_root(self: &Arc<Self>, ino: ino_t, ops: impl Into<Box<dyn FsNodeOps>>) {
286 let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
287 self.create_root_with_info(ino, ops, info);
288 }
289
290 pub fn create_root_with_info(
291 self: &Arc<Self>,
292 ino: ino_t,
293 ops: impl Into<Box<dyn FsNodeOps>>,
294 info: FsNodeInfo,
295 ) {
296 let node = self.create_detached_node(ino, ops, info);
297 self.set_root(node);
298 }
299
300 pub fn remove_node(&self, node: &FsNode) {
304 self.node_cache.remove_node(node);
305 }
306
307 pub fn allocate_ino(&self) -> ino_t {
308 self.node_cache
309 .allocate_ino()
310 .expect("allocate_ino called on a filesystem that uses external node IDs")
311 }
312
313 pub fn allocate_ino_range(&self, size: usize) -> Range<ino_t> {
315 self.node_cache
316 .allocate_ino_range(size)
317 .expect("allocate_ino_range called on a filesystem that uses external node IDs")
318 }
319
320 pub fn rename<L>(
325 &self,
326 locked: &mut Locked<L>,
327 current_task: &CurrentTask,
328 old_parent: &FsNodeHandle,
329 old_name: &FsStr,
330 new_parent: &FsNodeHandle,
331 new_name: &FsStr,
332 renamed: &FsNodeHandle,
333 replaced: Option<&FsNodeHandle>,
334 ) -> Result<(), Errno>
335 where
336 L: LockEqualOrBefore<FileOpsCore>,
337 {
338 let locked = locked.cast_locked::<FileOpsCore>();
339 self.ops.rename(
340 locked,
341 self,
342 current_task,
343 old_parent,
344 old_name,
345 new_parent,
346 new_name,
347 renamed,
348 replaced,
349 )
350 }
351
352 pub fn exchange(
355 &self,
356 current_task: &CurrentTask,
357 node1: &FsNodeHandle,
358 parent1: &FsNodeHandle,
359 name1: &FsStr,
360 node2: &FsNodeHandle,
361 parent2: &FsNodeHandle,
362 name2: &FsStr,
363 ) -> Result<(), Errno> {
364 self.ops.exchange(self, current_task, node1, parent1, name1, node2, parent2, name2)
365 }
366
367 pub fn force_unmount_ops(&self) {
371 self.ops.unmount();
372 }
373
374 pub fn statfs<L>(
381 &self,
382 locked: &mut Locked<L>,
383 current_task: &CurrentTask,
384 ) -> Result<statfs, Errno>
385 where
386 L: LockEqualOrBefore<FileOpsCore>,
387 {
388 security::sb_statfs(current_task, &self)?;
389 let locked = locked.cast_locked::<FileOpsCore>();
390 let mut stat = self.ops.statfs(locked, self, current_task)?;
391 if stat.f_frsize == 0 {
392 stat.f_frsize = stat.f_bsize as i64;
393 }
394 Ok(stat)
395 }
396
397 pub fn sync<L>(&self, locked: &mut Locked<L>, current_task: &CurrentTask) -> Result<(), Errno>
398 where
399 L: LockEqualOrBefore<FileOpsCore>,
400 {
401 self.ops.sync(locked.cast_locked::<FileOpsCore>(), self, current_task)
402 }
403
404 pub fn did_create_dir_entry(&self, entry: &DirEntryHandle) {
405 match &self.dcache {
406 DirEntryCache::Permanent(p) => {
407 p.lock().insert(ArcKey(entry.clone()));
408 }
409 DirEntryCache::Lru(LruCache { entries, .. }) => {
410 entries.lock().insert(ArcKey(entry.clone()), ());
411 }
412 DirEntryCache::Uncached => {}
413 }
414 }
415
416 pub fn will_destroy_dir_entry(&self, entry: &DirEntryHandle) {
417 match &self.dcache {
418 DirEntryCache::Permanent(p) => {
419 p.lock().remove(ArcKey::ref_cast(entry));
420 }
421 DirEntryCache::Lru(LruCache { entries, .. }) => {
422 entries.lock().remove(ArcKey::ref_cast(entry));
423 }
424 DirEntryCache::Uncached => {}
425 };
426 }
427
428 pub fn did_access_dir_entry(&self, entry: &DirEntryHandle) {
430 if let DirEntryCache::Lru(LruCache { entries, .. }) = &self.dcache {
431 entries.lock().get_refresh(ArcKey::ref_cast(entry));
432 }
433 }
434
435 pub fn purge_old_entries(&self) {
440 if let DirEntryCache::Lru(l) = &self.dcache {
441 let mut purged = SmallVec::<[DirEntryHandle; 4]>::new();
442 {
443 let mut entries = l.entries.lock();
444 while entries.len() > l.capacity {
445 purged.push(entries.pop_front().unwrap().0.0);
446 }
447 }
448 std::mem::drop(purged);
450 }
451 }
452
453 pub fn downcast_ops<T: 'static>(&self) -> Option<&T> {
455 self.ops.as_ref().as_any().downcast_ref()
456 }
457
458 pub fn name(&self) -> &'static FsStr {
459 self.ops.name()
460 }
461
462 pub fn manages_timestamps(&self) -> bool {
463 self.ops.manages_timestamps()
464 }
465
466 pub fn crypt_service(&self) -> Option<Arc<CryptService>> {
470 self.ops.crypt_service()
471 }
472}
473
474pub trait FileSystemOps: AsAny + Send + Sync + 'static {
476 fn statfs(
490 &self,
491 _locked: &mut Locked<FileOpsCore>,
492 _fs: &FileSystem,
493 _current_task: &CurrentTask,
494 ) -> Result<statfs, Errno>;
495
496 fn name(&self) -> &'static FsStr;
497
498 fn uses_external_node_ids(&self) -> bool {
503 false
504 }
505
506 fn rename(
521 &self,
522 _locked: &mut Locked<FileOpsCore>,
523 _fs: &FileSystem,
524 _current_task: &CurrentTask,
525 _old_parent: &FsNodeHandle,
526 _old_name: &FsStr,
527 _new_parent: &FsNodeHandle,
528 _new_name: &FsStr,
529 _renamed: &FsNodeHandle,
530 _replaced: Option<&FsNodeHandle>,
531 ) -> Result<(), Errno> {
532 error!(EROFS)
533 }
534
535 fn exchange(
536 &self,
537 _fs: &FileSystem,
538 _current_task: &CurrentTask,
539 _node1: &FsNodeHandle,
540 _parent1: &FsNodeHandle,
541 _name1: &FsStr,
542 _node2: &FsNodeHandle,
543 _parent2: &FsNodeHandle,
544 _name2: &FsStr,
545 ) -> Result<(), Errno> {
546 error!(EINVAL)
547 }
548
549 fn unmount(&self) {}
551
552 fn manages_timestamps(&self) -> bool {
558 false
559 }
560
561 fn crypt_service(&self) -> Option<Arc<CryptService>> {
563 None
564 }
565
566 fn sync(
567 &self,
568 _locked: &mut Locked<FileOpsCore>,
569 _fs: &FileSystem,
570 _current_task: &CurrentTask,
571 ) -> Result<(), Errno> {
572 Ok(())
573 }
574
575 fn is_readonly(&self) -> bool {
580 false
581 }
582}
583
584impl Drop for FileSystem {
585 fn drop(&mut self) {
586 self.ops.unmount();
587 }
588}
589
590pub type FileSystemHandle = Arc<FileSystem>;