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_type::DeviceType;
22use starnix_uapi::errors::Errno;
23use starnix_uapi::file_mode::mode;
24use starnix_uapi::mount_flags::MountFlags;
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: DeviceType,
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: MountFlags,
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 let file_system = Arc::new(FileSystem {
149 kernel: kernel.weak_self.clone(),
150 root: OnceLock::new(),
151 ops: Box::new(ops),
152 options,
153 dev_id: kernel.device_registry.next_anonymous_dev_id(locked),
154 rename_mutex: Mutex::new(()),
155 node_cache,
156 dcache: match cache_mode {
157 CacheMode::Permanent => DirEntryCache::Permanent(Mutex::new(HashSet::new())),
158 CacheMode::Cached(CacheConfig { capacity }) => DirEntryCache::Lru(LruCache {
159 capacity,
160 entries: Mutex::new(LinkedHashMap::new()),
161 }),
162 CacheMode::Uncached => DirEntryCache::Uncached,
163 },
164 security_state,
165 });
166
167 security::file_system_post_init_security(kernel, &file_system);
170
171 Ok(file_system)
172 }
173
174 fn set_root(self: &FileSystemHandle, root: FsNodeHandle) {
175 let root_dir = DirEntry::new_uncached(root, None, FsString::default());
177 assert!(
178 self.root.set(root_dir).is_ok(),
179 "FileSystem::set_root can't be called more than once"
180 );
181 }
182
183 pub fn has_permanent_entries(&self) -> bool {
184 matches!(self.dcache, DirEntryCache::Permanent(_))
185 }
186
187 pub fn root(&self) -> &DirEntryHandle {
191 self.root.get().unwrap_or_else(|| panic!("FileSystem {} has no root", self.name()))
192 }
193
194 pub fn maybe_root(&self) -> Option<&DirEntryHandle> {
196 self.root.get()
197 }
198
199 pub fn get_or_create_node<F>(
200 &self,
201 node_key: ino_t,
202 create_fn: F,
203 ) -> Result<FsNodeHandle, Errno>
204 where
205 F: FnOnce() -> Result<FsNodeHandle, Errno>,
206 {
207 self.get_and_validate_or_create_node(node_key, |_| true, create_fn)
208 }
209
210 pub fn get_and_validate_or_create_node<V, C>(
225 &self,
226 node_key: ino_t,
227 validate_fn: V,
228 create_fn: C,
229 ) -> Result<FsNodeHandle, Errno>
230 where
231 V: Fn(&FsNodeHandle) -> bool,
232 C: FnOnce() -> Result<FsNodeHandle, Errno>,
233 {
234 self.node_cache.get_and_validate_or_create_node(node_key, validate_fn, create_fn)
235 }
236
237 pub fn create_node(
241 self: &Arc<Self>,
242 ino: ino_t,
243 ops: impl Into<Box<dyn FsNodeOps>>,
244 info: FsNodeInfo,
245 ) -> FsNodeHandle {
246 let node = FsNode::new_uncached(ino, ops, self, info);
247 self.node_cache.insert_node(&node);
248 node
249 }
250
251 pub fn create_node_and_allocate_node_id(
252 self: &Arc<Self>,
253 ops: impl Into<Box<dyn FsNodeOps>>,
254 info: FsNodeInfo,
255 ) -> FsNodeHandle {
256 let ino = self.allocate_ino();
257 self.create_node(ino, ops, info)
258 }
259
260 pub fn create_detached_node(
262 self: &Arc<Self>,
263 ino: ino_t,
264 ops: impl Into<Box<dyn FsNodeOps>>,
265 info: FsNodeInfo,
266 ) -> FsNodeHandle {
267 assert!(info.mode.is_dir());
268 let node = FsNode::new_uncached(ino, ops, self, info);
269 self.node_cache.insert_node(&node);
270 node
271 }
272
273 pub fn create_root(self: &Arc<Self>, ino: ino_t, ops: impl Into<Box<dyn FsNodeOps>>) {
278 let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
279 self.create_root_with_info(ino, ops, info);
280 }
281
282 pub fn create_root_with_info(
283 self: &Arc<Self>,
284 ino: ino_t,
285 ops: impl Into<Box<dyn FsNodeOps>>,
286 info: FsNodeInfo,
287 ) {
288 let node = self.create_detached_node(ino, ops, info);
289 self.set_root(node);
290 }
291
292 pub fn remove_node(&self, node: &FsNode) {
296 self.node_cache.remove_node(node);
297 }
298
299 pub fn allocate_ino(&self) -> ino_t {
300 self.node_cache
301 .allocate_ino()
302 .expect("allocate_ino called on a filesystem that uses external node IDs")
303 }
304
305 pub fn allocate_ino_range(&self, size: usize) -> Range<ino_t> {
307 self.node_cache
308 .allocate_ino_range(size)
309 .expect("allocate_ino_range called on a filesystem that uses external node IDs")
310 }
311
312 pub fn rename<L>(
317 &self,
318 locked: &mut Locked<L>,
319 current_task: &CurrentTask,
320 old_parent: &FsNodeHandle,
321 old_name: &FsStr,
322 new_parent: &FsNodeHandle,
323 new_name: &FsStr,
324 renamed: &FsNodeHandle,
325 replaced: Option<&FsNodeHandle>,
326 ) -> Result<(), Errno>
327 where
328 L: LockEqualOrBefore<FileOpsCore>,
329 {
330 let locked = locked.cast_locked::<FileOpsCore>();
331 self.ops.rename(
332 locked,
333 self,
334 current_task,
335 old_parent,
336 old_name,
337 new_parent,
338 new_name,
339 renamed,
340 replaced,
341 )
342 }
343
344 pub fn exchange(
347 &self,
348 current_task: &CurrentTask,
349 node1: &FsNodeHandle,
350 parent1: &FsNodeHandle,
351 name1: &FsStr,
352 node2: &FsNodeHandle,
353 parent2: &FsNodeHandle,
354 name2: &FsStr,
355 ) -> Result<(), Errno> {
356 self.ops.exchange(self, current_task, node1, parent1, name1, node2, parent2, name2)
357 }
358
359 pub fn force_unmount_ops(&self) {
363 self.ops.unmount();
364 }
365
366 pub fn statfs<L>(
373 &self,
374 locked: &mut Locked<L>,
375 current_task: &CurrentTask,
376 ) -> Result<statfs, Errno>
377 where
378 L: LockEqualOrBefore<FileOpsCore>,
379 {
380 security::sb_statfs(current_task, &self)?;
381 let locked = locked.cast_locked::<FileOpsCore>();
382 let mut stat = self.ops.statfs(locked, self, current_task)?;
383 if stat.f_frsize == 0 {
384 stat.f_frsize = stat.f_bsize as i64;
385 }
386 Ok(stat)
387 }
388
389 pub fn did_create_dir_entry(&self, entry: &DirEntryHandle) {
390 match &self.dcache {
391 DirEntryCache::Permanent(p) => {
392 p.lock().insert(ArcKey(entry.clone()));
393 }
394 DirEntryCache::Lru(LruCache { entries, .. }) => {
395 entries.lock().insert(ArcKey(entry.clone()), ());
396 }
397 DirEntryCache::Uncached => {}
398 }
399 }
400
401 pub fn will_destroy_dir_entry(&self, entry: &DirEntryHandle) {
402 match &self.dcache {
403 DirEntryCache::Permanent(p) => {
404 p.lock().remove(ArcKey::ref_cast(entry));
405 }
406 DirEntryCache::Lru(LruCache { entries, .. }) => {
407 entries.lock().remove(ArcKey::ref_cast(entry));
408 }
409 DirEntryCache::Uncached => {}
410 };
411 }
412
413 pub fn did_access_dir_entry(&self, entry: &DirEntryHandle) {
415 if let DirEntryCache::Lru(LruCache { entries, .. }) = &self.dcache {
416 entries.lock().get_refresh(ArcKey::ref_cast(entry));
417 }
418 }
419
420 pub fn purge_old_entries(&self) {
425 if let DirEntryCache::Lru(l) = &self.dcache {
426 let mut purged = SmallVec::<[DirEntryHandle; 4]>::new();
427 {
428 let mut entries = l.entries.lock();
429 while entries.len() > l.capacity {
430 purged.push(entries.pop_front().unwrap().0.0);
431 }
432 }
433 std::mem::drop(purged);
435 }
436 }
437
438 pub fn downcast_ops<T: 'static>(&self) -> Option<&T> {
440 self.ops.as_ref().as_any().downcast_ref()
441 }
442
443 pub fn name(&self) -> &'static FsStr {
444 self.ops.name()
445 }
446
447 pub fn manages_timestamps(&self) -> bool {
448 self.ops.manages_timestamps()
449 }
450
451 pub fn crypt_service(&self) -> Option<Arc<CryptService>> {
455 self.ops.crypt_service()
456 }
457}
458
459pub trait FileSystemOps: AsAny + Send + Sync + 'static {
461 fn statfs(
475 &self,
476 _locked: &mut Locked<FileOpsCore>,
477 _fs: &FileSystem,
478 _current_task: &CurrentTask,
479 ) -> Result<statfs, Errno>;
480
481 fn name(&self) -> &'static FsStr;
482
483 fn uses_external_node_ids(&self) -> bool {
488 false
489 }
490
491 fn rename(
506 &self,
507 _locked: &mut Locked<FileOpsCore>,
508 _fs: &FileSystem,
509 _current_task: &CurrentTask,
510 _old_parent: &FsNodeHandle,
511 _old_name: &FsStr,
512 _new_parent: &FsNodeHandle,
513 _new_name: &FsStr,
514 _renamed: &FsNodeHandle,
515 _replaced: Option<&FsNodeHandle>,
516 ) -> Result<(), Errno> {
517 error!(EROFS)
518 }
519
520 fn exchange(
521 &self,
522 _fs: &FileSystem,
523 _current_task: &CurrentTask,
524 _node1: &FsNodeHandle,
525 _parent1: &FsNodeHandle,
526 _name1: &FsStr,
527 _node2: &FsNodeHandle,
528 _parent2: &FsNodeHandle,
529 _name2: &FsStr,
530 ) -> Result<(), Errno> {
531 error!(EINVAL)
532 }
533
534 fn unmount(&self) {}
536
537 fn manages_timestamps(&self) -> bool {
543 false
544 }
545
546 fn crypt_service(&self) -> Option<Arc<CryptService>> {
548 None
549 }
550}
551
552impl Drop for FileSystem {
553 fn drop(&mut self) {
554 self.ops.unmount();
555 }
556}
557
558pub type FileSystemHandle = Arc<FileSystem>;