Skip to main content

starnix_core/vfs/
file_system.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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/// The type of the filesystem for LockDep purposes.
40///
41/// `Normal` filesystems use standard lock levels.
42/// `Recursive` filesystems (like OverlayFS) use lock levels that precede normal ones,
43/// allowing them to lock the underlying filesystem without violating the hierarchy.
44/// `Fuse` filesystems do blocking calls while holding locks and require specific lock
45/// ordering because of this.
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum FsLockDepType {
48    Normal,
49    Recursive,
50    Fuse,
51}
52
53/// A file system that can be mounted in a namespace.
54pub struct FileSystem {
55    pub kernel: Weak<Kernel>,
56    root: OnceLock<DirEntryHandle>,
57    ops: Box<dyn FileSystemOps>,
58
59    /// The options specified when mounting the filesystem. Saved here for display in
60    /// /proc/[pid]/mountinfo.
61    pub options: FileSystemOptions,
62
63    /// The device ID of this filesystem. Returned in the st_dev field when stating an inode in
64    /// this filesystem.
65    pub dev_id: DeviceId,
66
67    /// A file-system global mutex to serialize rename operations.
68    ///
69    /// This mutex is useful because the invariants enforced during a rename
70    /// operation involve many DirEntry objects. In the future, we might be
71    /// able to remove this mutex, but we will need to think carefully about
72    /// how rename operations can interleave.
73    ///
74    /// See DirEntry::rename.
75    pub rename_mutex: DynamicLockDepMutex<FileSystemRenameToken>,
76
77    /// The FsNode cache for this file system.
78    ///
79    /// When two directory entries are hard links to the same underlying inode,
80    /// this cache lets us re-use the same FsNode object for both directory
81    /// entries.
82    ///
83    /// Rather than calling FsNode::new directly, file systems should call
84    /// FileSystem::get_or_create_node to see if the FsNode already exists in
85    /// the cache.
86    node_cache: Arc<FsNodeCache>,
87
88    /// DirEntryHandle cache for the filesystem. Holds strong references to DirEntry objects. For
89    /// filesystems with permanent entries, this will hold a strong reference to every node to make
90    /// sure it doesn't get freed without being explicitly unlinked. Otherwise, entries are
91    /// maintained in an LRU cache.
92    dcache: DirEntryCache,
93
94    /// Holds security state for this file system, which is created and used by the Linux Security
95    /// Modules subsystem hooks.
96    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    /// The source string passed as the first argument to mount(), e.g. a block device.
108    pub source: FlyByteStr,
109    /// Flags kept per-superblock.
110    pub flags: AtomicFileSystemFlags,
111    /// Filesystem options passed as the last argument to mount().
112    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
145/// Configuration for CacheMode::Cached.
146pub struct CacheConfig {
147    pub capacity: usize,
148}
149
150pub enum CacheMode {
151    /// Entries are pemanent, instead of a cache of the backing storage. An example is tmpfs: the
152    /// DirEntry tree *is* the backing storage, as opposed to ext4, which uses the DirEntry tree as
153    /// a cache and removes unused nodes from it.
154    Permanent,
155    /// Entries are cached.
156    Cached(CacheConfig),
157    /// Entries are uncached. This can be appropriate in cases where it is difficult for the
158    /// filesystem to keep the cache coherent: e.g. the /proc/<pid>/task directory.
159    Uncached,
160}
161
162impl FileSystem {
163    /// Create a new filesystem.
164    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        // TODO: https://fxbug.dev/366405587 - Workaround to allow SELinux to note that this
207        // `FileSystem` needs labeling, once a policy has been loaded.
208        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        // No need to cache the root directory, it is owned by the filesystem.
215        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    /// Returns whether this filesystem supports casefolded directory lookups.
227    pub fn has_casefold_support(&self) -> bool {
228        self.ops.has_casefold_support()
229    }
230
231    /// Returns the `FsLockDepType` of this filesystem, delegated from `FileSystemOps`.
232    pub fn fs_lockdep_type(&self) -> FsLockDepType {
233        self.ops.fs_lockdep_type()
234    }
235
236    /// The root directory entry of this file system.
237    ///
238    /// Panics if this file system does not have a root directory.
239    pub fn root(&self) -> &DirEntryHandle {
240        self.root.get().unwrap_or_else(|| panic!("FileSystem {} has no root", self.name()))
241    }
242
243    /// The root directory entry of this `FileSystem`, if it has one.
244    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    /// Get a node that is validated with the callback, or create an FsNode for
260    /// this file system.
261    ///
262    /// If node_id is Some, then this function checks the node cache to
263    /// determine whether this node is already open. If so, the function
264    /// returns the existing FsNode if it passes the validation check. If no
265    /// node exists, or a node does but fails the validation check, the function
266    /// calls the given create_fn function to create the FsNode.
267    ///
268    /// If node_id is None, then this function assigns a new identifier number
269    /// and calls the given create_fn function to create the FsNode with the
270    /// assigned number.
271    ///
272    /// Returns Err only if create_fn returns Err.
273    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    /// File systems that produce their own IDs for nodes should invoke this
287    /// function. The ones who leave to this object to assign the IDs should
288    /// call |create_node_and_allocate_node_id|.
289    ///
290    /// `info.blksize` will be overriden for some virtual filesystems based on
291    /// `ops.default_blksize()`.
292    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    /// Create a node for a directory that has no parent.
326    pub fn create_detached_node(
327        self: &Arc<Self>,
328        ino: ino_t,
329        ops: impl Into<Box<dyn FsNodeOps>>,
330        info: FsNodeInfo,
331    ) -> FsNodeHandle {
332        assert!(info.mode.is_dir());
333        self.create_node_with_flags(Some(ino), ops, info, FsNodeFlags::empty())
334    }
335
336    /// Create a root node for the filesystem.
337    ///
338    /// This is a convenience function that creates a root node with the default
339    /// directory mode and root credentials.
340    pub fn create_root(self: &Arc<Self>, ino: ino_t, ops: impl Into<Box<dyn FsNodeOps>>) {
341        let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
342        self.create_root_with_info(ino, ops, info);
343    }
344
345    pub fn create_root_with_info(
346        self: &Arc<Self>,
347        ino: ino_t,
348        ops: impl Into<Box<dyn FsNodeOps>>,
349        info: FsNodeInfo,
350    ) {
351        let node = self.create_detached_node(ino, ops, info);
352        self.set_root(node);
353    }
354
355    /// Remove the given FsNode from the node cache.
356    ///
357    /// Called from the Release trait of FsNode.
358    pub fn remove_node(&self, node: &FsNode) {
359        self.node_cache.remove_node(node);
360    }
361
362    pub fn allocate_ino(&self) -> ino_t {
363        self.node_cache
364            .allocate_ino()
365            .expect("allocate_ino called on a filesystem that uses external node IDs")
366    }
367
368    /// Allocate a contiguous block of node ids.
369    pub fn allocate_ino_range(&self, size: usize) -> Range<ino_t> {
370        self.node_cache
371            .allocate_ino_range(size)
372            .expect("allocate_ino_range called on a filesystem that uses external node IDs")
373    }
374
375    /// Move |renamed| that is at |old_name| in |old_parent| to |new_name| in |new_parent|
376    /// replacing |replaced|.
377    /// If |replaced| exists and is a directory, this function must check that |renamed| is n
378    /// directory and that |replaced| is empty.
379    pub fn rename(
380        &self,
381        current_task: &CurrentTask,
382        context: &mut RenameContext<'_>,
383        old_name: &FsStr,
384        new_name: &FsStr,
385    ) -> Result<(), Errno> {
386        self.ops.rename(self, current_task, context, old_name, new_name)
387    }
388
389    /// Exchanges the two nodes identified by `name1` and `name2` in the context.
390    /// The parent directories and other metadata are contained within the `context`.
391    pub fn exchange(
392        &self,
393        current_task: &CurrentTask,
394        context: &mut RenameContext<'_>,
395        name1: &FsStr,
396        name2: &FsStr,
397    ) -> Result<(), Errno> {
398        self.ops.exchange(self, current_task, context, name1, name2)
399    }
400
401    /// Forces a FileSystem unmount.
402    // TODO(https://fxbug.dev/394694891): kernel shutdown should ideally unmount FileSystems via
403    // their drop impl, which should be triggered by Mount.unmount().
404    pub fn force_unmount_ops(&self) {
405        self.ops.unmount();
406    }
407
408    /// Returns the `statfs` for this filesystem.
409    ///
410    /// Each `FileSystemOps` impl is expected to override this to return the specific statfs for
411    /// the filesystem.
412    ///
413    /// Returns `ENOSYS` if the `FileSystemOps` don't implement `stat`.
414    pub fn statfs(&self, current_task: &CurrentTask) -> Result<statfs, Errno> {
415        security::sb_statfs(current_task, &self)?;
416        let mut stat = self.ops.statfs(self, current_task)?;
417        if stat.f_frsize == 0 {
418            stat.f_frsize = stat.f_bsize as i64;
419        }
420        Ok(stat)
421    }
422
423    pub fn sync(&self, current_task: &CurrentTask) -> Result<(), Errno> {
424        self.ops.sync(self, current_task)
425    }
426
427    pub fn did_create_dir_entry(&self, entry: &DirEntryHandle) {
428        match &self.dcache {
429            DirEntryCache::Permanent(p) => {
430                p.lock().insert(ArcKey(entry.clone()));
431            }
432            DirEntryCache::Lru(LruCache { entries, .. }) => {
433                entries.lock().insert(ArcKey(entry.clone()), ());
434            }
435            DirEntryCache::Uncached => {}
436        }
437    }
438
439    pub fn will_destroy_dir_entry(&self, entry: &DirEntryHandle) {
440        match &self.dcache {
441            DirEntryCache::Permanent(p) => {
442                p.lock().remove(ArcKey::ref_cast(entry));
443            }
444            DirEntryCache::Lru(LruCache { entries, .. }) => {
445                entries.lock().remove(ArcKey::ref_cast(entry));
446            }
447            DirEntryCache::Uncached => {}
448        };
449    }
450
451    /// Informs the cache that the entry was used.
452    pub fn did_access_dir_entry(&self, entry: &DirEntryHandle) {
453        if let DirEntryCache::Lru(LruCache { entries, .. }) = &self.dcache {
454            entries.lock().get_refresh(ArcKey::ref_cast(entry));
455        }
456    }
457
458    /// Purges old entries from the cache. This is done as a separate step to avoid potential
459    /// deadlocks that could occur if done at admission time (where locks might be held that are
460    /// required when dropping old entries). This should be called after any new entries are
461    /// admitted with no locks held that might be required for dropping entries.
462    pub fn purge_old_entries(&self) {
463        if let DirEntryCache::Lru(l) = &self.dcache {
464            let mut purged = SmallVec::<[DirEntryHandle; 4]>::new();
465            {
466                let mut entries = l.entries.lock();
467                while entries.len() > l.capacity {
468                    purged.push(entries.pop_front().unwrap().0.0);
469                }
470            }
471            // Entries will get dropped here whilst we're not holding a lock.
472            std::mem::drop(purged);
473        }
474    }
475
476    /// Returns the `FileSystem`'s `FileSystemOps` as a `&T`, or `None` if the downcast fails.
477    pub fn downcast_ops<T: 'static>(&self) -> Option<&T> {
478        self.ops.as_ref().as_any().downcast_ref()
479    }
480
481    pub fn name(&self) -> &'static FsStr {
482        self.ops.name()
483    }
484
485    /// Returns the filesystem-specific mount options for display in /proc/mounts and
486    /// /proc/[pid]/mountinfo, each preceded by a comma: the LSM options followed by the remaining
487    /// mount parameters.
488    pub fn format_options(&self, kernel: &Kernel) -> Result<FsString, Errno> {
489        let mut result = FsString::from(security::sb_show_options(kernel, self)?.to_string());
490        result.extend_from_slice(&self.show_options()?);
491        Ok(result)
492    }
493
494    pub fn manages_timestamps(&self) -> bool {
495        self.ops.manages_timestamps()
496    }
497
498    /// Returns the crypt service associated with this filesystem, if any. The crypt service
499    /// implements the fuchsia.fxfs.Crypt protocol and maintains an internal structure that maps
500    /// each encryption key id to the actual key.
501    pub fn crypt_service(&self) -> Option<Arc<CryptService>> {
502        self.ops.crypt_service()
503    }
504
505    /// Reconfigure the filesystem with the given flags and mount parameters.
506    ///
507    /// This is called during a remount operation (`MS_REMOUNT`), to allow the filesystem to update
508    /// internal resources as necessary to support the new flags and parameters, or to reject
509    /// reconfiguration.
510    pub fn reconfigure(
511        &self,
512        current_task: &CurrentTask,
513        flags: FileSystemFlags,
514        params: &MountParams,
515    ) -> Result<(), Errno> {
516        self.ops.reconfigure(self, current_task, flags, params)
517    }
518
519    /// Returns filesystem-specific mount options to display in `/proc/mounts` and
520    /// `/proc/[pid]/mountinfo`.
521    pub fn show_options(&self) -> Result<FsString, Errno> {
522        self.ops.show_options(self)
523    }
524
525    pub fn sub_filesystems(&self) -> Vec<Arc<FileSystem>> {
526        self.ops.sub_filesystems()
527    }
528
529    pub fn purge_dcache(&self) {
530        if let DirEntryCache::Lru(l) = &self.dcache {
531            let entries = std::mem::take(&mut *l.entries.lock());
532            // Entries will get dropped here outside of the lock.
533            std::mem::drop(entries);
534        }
535    }
536
537    pub fn drop_backend_caches(&self) {
538        if let Err(e) = self.ops.drop_caches(self) {
539            log_warn!("drop_caches failed for filesystem {:?}: {:?}", self.name(), e);
540        }
541    }
542
543    pub fn purge_all_entries(&self) {
544        self.purge_dcache();
545        self.drop_backend_caches();
546    }
547}
548
549/// The filesystem-implementation-specific data for FileSystem.
550pub trait FileSystemOps: AsAny + Send + Sync + 'static {
551    /// Returns the `FsLockDepType` of this filesystem.
552    ///
553    /// Defaults to `FsLockDepType::Normal`. Filesystems that can be stacked (like OverlayFS)
554    /// should override this to return `FsLockDepType::Recursive`.
555    fn fs_lockdep_type(&self) -> FsLockDepType {
556        FsLockDepType::Normal
557    }
558
559    /// Return information about this filesystem.
560    ///
561    /// A typical implementation looks like this:
562    /// ```
563    /// Ok(statfs::default(FILE_SYSTEM_MAGIC))
564    /// ```
565    /// or, if the filesystem wants to customize fields:
566    /// ```
567    /// Ok(statfs {
568    ///     f_blocks: self.blocks,
569    ///     ..statfs::default(FILE_SYSTEM_MAGIC)
570    /// })
571    /// ```
572    fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno>;
573
574    /// Reconfigure the filesystem with the given flags and mount parameters.
575    ///
576    /// This is called during a remount operation (`MS_REMOUNT`), to allow the filesystem to update
577    /// internal resources as necessary to support the new flags and parameters.
578    fn reconfigure(
579        &self,
580        fs: &FileSystem,
581        _current_task: &CurrentTask,
582        new_flags: FileSystemFlags,
583        params: &MountParams,
584    ) -> Result<(), Errno> {
585        if !params.is_empty() {
586            track_stub!(TODO("https://fxbug.dev/322875506"), "MS_REMOUNT: Updating data");
587        }
588        fs.options.flags.store(new_flags, Ordering::Relaxed);
589        Ok(())
590    }
591
592    /// Returns filesystem-specific mount options to display in `/proc/mounts` and
593    /// `/proc/[pid]/mountinfo` (each preceded by a leading comma, e.g. `",gid=3009,hidepid=invisible"`).
594    fn show_options(&self, fs: &FileSystem) -> Result<FsString, Errno> {
595        Ok(fs.options.params.format_options())
596    }
597
598    fn name(&self) -> &'static FsStr;
599
600    /// Whether this file system uses external node IDs.
601    ///
602    /// If this is true, then the file system is responsible for assigning node IDs to its nodes.
603    /// Otherwise, the VFS will assign node IDs to the nodes.
604    fn uses_external_node_ids(&self) -> bool {
605        false
606    }
607
608    /// Returns whether this filesystem supports casefolded directory lookups.
609    fn has_casefold_support(&self) -> bool {
610        false
611    }
612
613    /// Returns the default block size (`st_blksize`) for nodes in this filesystem, if any.
614    fn default_blksize(&self) -> Option<usize> {
615        None
616    }
617
618    /// Rename the given node.
619    ///
620    /// The node to be renamed is passed as "renamed". It currently has
621    /// old_name in old_parent. After the rename operation, it should have
622    /// new_name in new_parent.
623    ///
624    /// If new_parent already has a child named new_name, that node is passed as
625    /// "replaced". In that case, both "renamed" and "replaced" will be
626    /// directories and the rename operation should succeed only if "replaced"
627    /// is empty. The VFS will check that there are no children of "replaced" in
628    /// the DirEntry cache, but the implementation of this function is
629    /// responsible for checking that there are no children of replaced that are
630    /// known only to the file system implementation (e.g., present on-disk but
631    /// not in the DirEntry cache).
632    fn rename(
633        &self,
634        _fs: &FileSystem,
635        _current_task: &CurrentTask,
636        _context: &mut RenameContext<'_>,
637        _old_name: &FsStr,
638        _new_name: &FsStr,
639    ) -> Result<(), Errno> {
640        error!(EROFS)
641    }
642
643    /// Exchanges the two nodes identified by `name1` and `name2` in the context.
644    ///
645    /// Semantically, this is an atomic exchange of two paths (similar to two
646    /// renames, one in each direction). It uses `RenameContext` because the
647    /// locking requirements and metadata needed (parent directories, node info)
648    /// are identical to a rename operation involving two paths.
649    fn exchange(
650        &self,
651        _fs: &FileSystem,
652        _current_task: &CurrentTask,
653        _context: &mut RenameContext<'_>,
654        _name1: &FsStr,
655        _name2: &FsStr,
656    ) -> Result<(), Errno> {
657        error!(EINVAL)
658    }
659
660    /// Called when the filesystem is unmounted.
661    fn unmount(&self) {}
662
663    /// Indicates if the filesystem can manage the timestamps (i.e. ctime and mtime).
664    ///
665    /// Starnix updates the timestamps in FsNode's `info` directly. However, if the filesystem can
666    /// manage the timestamps, then Starnix does not need to do so. `info` will be refreshed with
667    /// the timestamps from the filesystem by calling `fetch_and_refresh_info(..)` on the FsNode.
668    fn manages_timestamps(&self) -> bool {
669        false
670    }
671
672    /// Returns the crypt service associated with this filesystem, if any.
673    fn crypt_service(&self) -> Option<Arc<CryptService>> {
674        None
675    }
676
677    fn sync(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<(), Errno> {
678        Ok(())
679    }
680
681    fn drop_caches(&self, _fs: &FileSystem) -> Result<(), Errno> {
682        Ok(())
683    }
684
685    /// Returns any underlying filesystems wrapped by this filesystem (e.g. OverlayFs lower/upper).
686    fn sub_filesystems(&self) -> Vec<Arc<FileSystem>> {
687        Vec::new()
688    }
689}
690
691impl Drop for FileSystem {
692    fn drop(&mut self) {
693        self.ops.unmount();
694    }
695}
696
697pub type FileSystemHandle = Arc<FileSystem>;