Skip to main content

starnix_modules_overlayfs/
lib.rs

1// Copyright 2023 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
5#![recursion_limit = "512"]
6
7use fuchsia_rcu::RcuReadScope;
8use once_cell::sync::OnceCell;
9use rand::Rng;
10use starnix_core::fs::tmpfs::{TmpFs, TmpFsDirectory};
11use starnix_core::mm::memory::MemoryObject;
12use starnix_core::security::{self, PermissionFlags};
13use starnix_core::task::{CurrentTask, Kernel};
14use starnix_core::vfs::fs_args::MountParams;
15use starnix_core::vfs::rw_queue::{RwQueueReadGuard, RwQueueWriteGuard};
16use starnix_core::vfs::{
17    AppendLockWriteGuard, CacheMode, CheckAccessReason, DirEntry, DirEntryHandle,
18    DirectoryEntryType, DirentSink, FallocMode, FileHandle, FileObject, FileOps, FileSystem,
19    FileSystemHandle, FileSystemOps, FileSystemOptions, FsLockDepType, FsNode, FsNodeFlags,
20    FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr, FsString, InputBuffer, MountInfo, OutputBuffer,
21    RenameContext, RenameFlags, SeekTarget, SymlinkTarget, UnlinkKind, ValueOrSize, VecInputBuffer,
22    VecOutputBuffer, XattrOp, default_seek, emit_dotdot, fileops_impl_directory,
23    fileops_impl_noop_sync, fileops_impl_seekable,
24};
25use starnix_logging::{log_error, log_warn, track_stub};
26use starnix_sync::{
27    BeforeFsNodeAppend, DynamicLockDepRwLock, FileOpsCore, FsNodeAppend, LockDepReadGuard,
28    LockDepRwLock, LockDepWriteGuard, LockEqualOrBefore, Locked, OverlayFsDirEntriesLock,
29    OverlayFsStateLock, Unlocked,
30};
31use starnix_uapi::auth::{Credentials, FsCred};
32use starnix_uapi::device_id::DeviceId;
33use starnix_uapi::errors::{EEXIST, ENOENT, Errno};
34use starnix_uapi::file_mode::{FileMode, mode};
35use starnix_uapi::open_flags::OpenFlags;
36use starnix_uapi::{errno, error, ino_t, off_t, statfs};
37use std::collections::BTreeSet;
38use std::sync::Arc;
39use syncio::zxio_node_attr_has_t;
40
41// Name and value for the xattr used to mark opaque directories in the upper FS.
42// See https://docs.kernel.org/filesystems/overlayfs.html#whiteouts-and-opaque-directories
43const OPAQUE_DIR_XATTR: &str = "trusted.overlay.opaque";
44const OPAQUE_DIR_XATTR_VALUE: &str = "y";
45
46#[derive(Clone)]
47struct DirEntryInfo {
48    name: FsString,
49    inode_num: ino_t,
50    entry_type: DirectoryEntryType,
51}
52
53type DirEntries = Vec<DirEntryInfo>;
54
55#[derive(Default)]
56struct DirentSinkAdapter {
57    items: Vec<DirEntryInfo>,
58    offset: off_t,
59}
60
61impl DirentSink for DirentSinkAdapter {
62    fn add(
63        &mut self,
64        inode_num: ino_t,
65        offset: off_t,
66        entry_type: DirectoryEntryType,
67        name: &FsStr,
68    ) -> Result<(), Errno> {
69        if !DirEntry::is_reserved_name(name) {
70            self.items.push(DirEntryInfo { name: name.to_owned(), inode_num, entry_type });
71        }
72        self.offset = offset;
73        Ok(())
74    }
75
76    fn offset(&self) -> off_t {
77        self.offset
78    }
79}
80
81#[derive(Copy, Clone, Eq, PartialEq)]
82enum UpperCopyMode {
83    MetadataOnly,
84    CopyAll,
85}
86
87/// An `DirEntry` associated with the mount options. This is required because OverlayFs mostly
88/// works at the `DirEntry` level (mounts on the lower, upper and work directories are ignored),
89/// but operation must still depend on mount options.
90#[derive(Clone)]
91struct ActiveEntry {
92    entry: DirEntryHandle,
93    mount: MountInfo,
94}
95
96impl ActiveEntry {
97    fn mapper<'a>(entry: &'a ActiveEntry) -> impl Fn(DirEntryHandle) -> ActiveEntry + 'a {
98        |dir_entry| ActiveEntry { entry: dir_entry, mount: entry.mount.clone() }
99    }
100
101    fn entry(&self) -> &DirEntryHandle {
102        &self.entry
103    }
104
105    fn mount(&self) -> &MountInfo {
106        &self.mount
107    }
108
109    fn component_lookup<L>(
110        &self,
111        locked: &mut Locked<L>,
112        current_task: &CurrentTask,
113        name: &FsStr,
114    ) -> Result<Self, Errno>
115    where
116        L: LockEqualOrBefore<FileOpsCore>,
117    {
118        self.entry()
119            .component_lookup(locked, current_task, self.mount(), name)
120            .map(ActiveEntry::mapper(self))
121    }
122
123    fn create_entry<L>(
124        &self,
125        locked: &mut Locked<L>,
126        current_task: &CurrentTask,
127        name: &FsStr,
128        create_node_fn: impl FnOnce(
129            &mut Locked<L>,
130            &FsNodeHandle,
131            &MountInfo,
132            &FsStr,
133        ) -> Result<FsNodeHandle, Errno>,
134    ) -> Result<Self, Errno>
135    where
136        L: LockEqualOrBefore<FileOpsCore>,
137    {
138        self.entry()
139            .create_entry(locked, current_task, self.mount(), name, create_node_fn)
140            .map(ActiveEntry::mapper(self))
141    }
142
143    /// Sets an xattr to mark the directory referenced by `entry` as opaque. Directories that are
144    /// marked as opaque in the upper FS are not merged with the corresponding directories in the
145    /// lower FS.
146    fn set_opaque_xattr<L>(
147        &self,
148        locked: &mut Locked<L>,
149        current_task: &CurrentTask,
150    ) -> Result<(), Errno>
151    where
152        L: LockEqualOrBefore<FileOpsCore>,
153    {
154        self.entry().node.set_xattr(
155            locked,
156            current_task,
157            self.mount(),
158            OPAQUE_DIR_XATTR.into(),
159            OPAQUE_DIR_XATTR_VALUE.into(),
160            XattrOp::Set,
161        )
162    }
163
164    /// Checks if the `entry` is marked as opaque.
165    fn is_opaque_node<L>(&self, locked: &mut Locked<L>, current_task: &CurrentTask) -> bool
166    where
167        L: LockEqualOrBefore<FileOpsCore>,
168    {
169        match self.entry().node.get_xattr(
170            locked,
171            current_task,
172            self.mount(),
173            OPAQUE_DIR_XATTR.into(),
174            OPAQUE_DIR_XATTR_VALUE.len(),
175        ) {
176            Ok(ValueOrSize::Value(v)) if v == OPAQUE_DIR_XATTR_VALUE => true,
177            _ => false,
178        }
179    }
180
181    /// Creates a "whiteout" entry in the directory called `name`. Whiteouts are created by
182    /// overlayfs to denote files and directories that were removed and should not be listed in the
183    /// directory. This is necessary because we cannot remove entries from the lower FS.
184    fn create_whiteout<L>(
185        &self,
186        locked: &mut Locked<L>,
187        current_task: &CurrentTask,
188        name: &FsStr,
189    ) -> Result<ActiveEntry, Errno>
190    where
191        L: LockEqualOrBefore<FileOpsCore>,
192    {
193        self.create_entry(locked, current_task, name, |locked, dir, mount, name| {
194            dir.create_node(
195                locked,
196                current_task,
197                mount,
198                name,
199                FileMode::IFCHR,
200                DeviceId::NONE,
201                FsCred::root(),
202            )
203        })
204    }
205
206    /// Returns `true` if this is a "whiteout".
207    fn is_whiteout(&self) -> bool {
208        let info = self.entry().node.info();
209        info.mode.is_chr() && info.rdev == DeviceId::NONE
210    }
211
212    /// Checks whether the child of this entry represented by `info` is a "whiteout".
213    ///
214    /// Only looks up the corresponding `DirEntry` when necessary.
215    fn is_whiteout_child<L>(
216        &self,
217        locked: &mut Locked<L>,
218        current_task: &CurrentTask,
219        info: &DirEntryInfo,
220    ) -> Result<bool, Errno>
221    where
222        L: LockEqualOrBefore<FileOpsCore>,
223    {
224        // We need to lookup the node only if the file is a char device.
225        if info.entry_type != DirectoryEntryType::CHR {
226            return Ok(false);
227        }
228        let entry = self.component_lookup(locked, current_task, info.name.as_ref())?;
229        Ok(entry.is_whiteout())
230    }
231
232    fn read_dir_entries<L>(
233        &self,
234        locked: &mut Locked<L>,
235        current_task: &CurrentTask,
236    ) -> Result<Vec<DirEntryInfo>, Errno>
237    where
238        L: LockEqualOrBefore<FileOpsCore>,
239    {
240        let mut sink = DirentSinkAdapter::default();
241        self.entry().open_anonymous(locked, current_task, OpenFlags::DIRECTORY)?.readdir(
242            locked,
243            current_task,
244            &mut sink,
245        )?;
246        Ok(sink.items)
247    }
248}
249
250struct OverlayNode {
251    stack: Arc<OverlayStack>,
252
253    // Corresponding `DirEntries` in the lower and the upper filesystems. At least one must be
254    // set. Note that we don't care about `NamespaceNode`: overlayfs overlays filesystems
255    // (i.e. not namespace subtrees). These directories may not be mounted anywhere.
256    // `upper` may be created dynamically whenever write access is required.
257    upper: OnceCell<ActiveEntry>,
258    lower: Option<ActiveEntry>,
259
260    // `prepare_to_unlink()` may mark `upper` as opaque. In that case we want to skip merging
261    // with `lower` in `readdir()`.
262    upper_is_opaque: OnceCell<()>,
263
264    parent: Option<Arc<OverlayNode>>,
265}
266
267impl OverlayNode {
268    fn new(
269        stack: Arc<OverlayStack>,
270        lower: Option<ActiveEntry>,
271        upper: Option<ActiveEntry>,
272        parent: Option<Arc<OverlayNode>>,
273    ) -> Arc<Self> {
274        assert!(upper.is_some() || parent.is_some());
275
276        let upper = match upper {
277            Some(entry) => OnceCell::with_value(entry),
278            None => OnceCell::new(),
279        };
280
281        Arc::new(OverlayNode { stack, upper, lower, upper_is_opaque: OnceCell::new(), parent })
282    }
283
284    fn from_fs_node(node: &FsNodeHandle) -> Result<&Arc<Self>, Errno> {
285        Ok(&node.downcast_ops::<OverlayNodeOps>().ok_or_else(|| errno!(EIO))?.node)
286    }
287
288    fn main_entry(&self) -> &ActiveEntry {
289        self.upper.get().or(self.lower.as_ref()).expect("Expected either upper or lower node")
290    }
291
292    fn init_fs_node_for_child(
293        self: &Arc<OverlayNode>,
294        node: &FsNode,
295        lower: Option<ActiveEntry>,
296        upper: Option<ActiveEntry>,
297    ) -> FsNodeHandle {
298        let entry = upper.as_ref().or(lower.as_ref()).expect("expect either lower or upper node");
299        let ino = entry.entry().node.ino;
300        let info = entry.entry().node.info().clone();
301
302        // Parent may be needed to initialize `upper`. We don't need to pass it if we have `upper`.
303        let parent = if upper.is_some() { None } else { Some(self.clone()) };
304
305        let overlay_node =
306            OverlayNodeOps { node: OverlayNode::new(self.stack.clone(), lower, upper, parent) };
307        FsNode::new_uncached(ino, overlay_node, &node.fs(), info, FsNodeFlags::empty())
308    }
309
310    /// If the file is currently in the lower FS, then promote it to the upper FS. No-op if the
311    /// file is already in the upper FS.
312    fn ensure_upper<L>(
313        &self,
314        locked: &mut Locked<L>,
315        current_task: &CurrentTask,
316        fs: &FileSystem,
317    ) -> Result<&ActiveEntry, Errno>
318    where
319        L: LockEqualOrBefore<FileOpsCore>,
320    {
321        self.ensure_upper_maybe_copy(locked, current_task, UpperCopyMode::CopyAll, fs)
322    }
323
324    /// Same as `ensure_upper()`, but allows to skip copying of the file content.
325    fn ensure_upper_maybe_copy<L>(
326        &self,
327        locked: &mut Locked<L>,
328        current_task: &CurrentTask,
329        copy_mode: UpperCopyMode,
330        fs: &FileSystem,
331    ) -> Result<&ActiveEntry, Errno>
332    where
333        L: LockEqualOrBefore<FileOpsCore>,
334    {
335        self.upper.get_or_try_init(|| {
336            let lower = self.lower.as_ref().expect("lower is expected when upper is missing");
337            let parent = self.parent.as_ref().expect("Parent is expected when upper is missing");
338            let parent_upper = parent.ensure_upper(locked, current_task, fs)?;
339            let name = lower.entry.local_name(&RcuReadScope::new()).to_owned();
340            let info = {
341                let info = lower.entry.node.info();
342                info.clone()
343            };
344            let cred = info.cred();
345
346            let mut copy_up_creds = Credentials::clone(&self.stack.mounter);
347            security::fs_node_copy_up(current_task, &lower.entry.node, fs, &mut copy_up_creds);
348            let res = current_task.override_creds(Arc::new(copy_up_creds), || {
349                if info.mode.is_lnk() {
350                    let link_target = lower.entry.node.readlink(locked, current_task)?;
351                    let link_path = match &link_target {
352                        SymlinkTarget::Node(_) => return error!(EIO),
353                        SymlinkTarget::Path(path) => path,
354                    };
355                    parent_upper.create_entry(
356                        locked,
357                        current_task,
358                        name.as_ref(),
359                        |locked, dir, mount, name| {
360                            dir.create_symlink(
361                                locked,
362                                current_task,
363                                mount,
364                                name,
365                                link_path.as_ref(),
366                                cred,
367                            )
368                        },
369                    )
370                } else if info.mode.is_reg() && copy_mode == UpperCopyMode::CopyAll {
371                    // Regular files need to be copied from lower FS to upper FS.
372                    self.stack.create_upper_entry(
373                        locked,
374                        current_task,
375                        parent_upper,
376                        name.as_ref(),
377                        |locked, dir, name| {
378                            dir.create_entry(
379                                locked,
380                                current_task,
381                                name,
382                                |locked, dir_node, mount, name| {
383                                    dir_node.create_node(
384                                        locked,
385                                        current_task,
386                                        mount,
387                                        name,
388                                        info.mode,
389                                        DeviceId::NONE,
390                                        cred,
391                                    )
392                                },
393                            )
394                        },
395                        |locked, entry| copy_file_content(locked, current_task, lower, &entry),
396                    )
397                } else {
398                    parent_upper.create_entry(
399                        locked,
400                        current_task,
401                        name.as_ref(),
402                        |locked, dir, mount, name| {
403                            dir.create_node(
404                                locked,
405                                current_task,
406                                mount,
407                                name,
408                                info.mode,
409                                info.rdev,
410                                cred,
411                            )
412                        },
413                    )
414                }
415            });
416
417            track_stub!(TODO("https://fxbug.dev/322874151"), "overlayfs copy xattrs");
418            res
419        })
420    }
421
422    /// Checks if this node exists in the lower FS.
423    fn has_lower(&self) -> bool {
424        self.lower.is_some()
425    }
426
427    /// Check that an item isn't present in the lower FS.
428    fn lower_entry_exists<L>(
429        &self,
430        locked: &mut Locked<L>,
431        current_task: &CurrentTask,
432        name: &FsStr,
433    ) -> Result<bool, Errno>
434    where
435        L: LockEqualOrBefore<FileOpsCore>,
436    {
437        match &self.lower {
438            Some(lower) => match lower.component_lookup(locked, current_task, name) {
439                Ok(entry) => Ok(!entry.is_whiteout()),
440                Err(err) if err.code == ENOENT => Ok(false),
441                Err(err) => Err(err),
442            },
443            None => Ok(false),
444        }
445    }
446
447    /// Helper used to create a new entry in the directory. It first checks that the target node
448    /// doesn't exist. Then `do_create` is called to create the new node in the work dir, which
449    /// is then moved to the target dir in the upper file system.
450    ///
451    /// It's assumed that the calling `DirEntry` has the current directory locked, so it is not
452    /// supposed to change while this method is executed. Note that OveralayFS doesn't handle
453    /// the case when the underlying file systems are changed directly, but that restriction
454    /// is not enforced.
455    fn create_entry<F, L>(
456        self: &Arc<OverlayNode>,
457        locked: &mut Locked<L>,
458        node: &FsNode,
459        current_task: &CurrentTask,
460        name: &FsStr,
461        do_create: F,
462    ) -> Result<ActiveEntry, Errno>
463    where
464        F: Fn(&mut Locked<L>, &ActiveEntry, &FsStr) -> Result<ActiveEntry, Errno>,
465        L: LockEqualOrBefore<FileOpsCore>,
466    {
467        let upper = self.ensure_upper(locked, current_task, &node.fs())?;
468
469        match upper.component_lookup(locked, current_task, name) {
470            Ok(existing) => {
471                // If there is an entry in the upper dir, then it must be a whiteout.
472                if !existing.is_whiteout() {
473                    return error!(EEXIST);
474                }
475            }
476
477            Err(e) if e.code == ENOENT => {
478                // If we don't have the entry in the upper fs, then check lower.
479                if self.lower_entry_exists(locked, current_task, name)? {
480                    return error!(EEXIST);
481                }
482            }
483            Err(e) => return Err(e),
484        };
485
486        self.stack.create_upper_entry(
487            locked,
488            current_task,
489            upper,
490            name,
491            |locked, entry, fs| do_create(locked, entry, fs),
492            |_, _entry| Ok(()),
493        )
494    }
495
496    /// An overlay directory may appear empty when the corresponding upper dir isn't empty:
497    /// it may contain a number of whiteout entries. In that case the whiteouts need to be
498    /// unlinked before the upper directory can be unlinked as well.
499    /// `prepare_to_unlink()` checks that the directory doesn't contain anything other
500    /// than whiteouts and if that is the case then it unlinks all of them.
501    fn prepare_to_unlink<L>(
502        self: &Arc<OverlayNode>,
503        locked: &mut Locked<L>,
504        current_task: &CurrentTask,
505    ) -> Result<(), Errno>
506    where
507        L: LockEqualOrBefore<FileOpsCore>,
508    {
509        if self.main_entry().entry().node.is_dir() {
510            let mut lower_entries = BTreeSet::new();
511            if let Some(dir) = &self.lower {
512                for item in dir.read_dir_entries(locked, current_task)?.drain(..) {
513                    if !dir.is_whiteout_child(locked, current_task, &item)? {
514                        lower_entries.insert(item.name);
515                    }
516                }
517            }
518
519            if let Some(dir) = self.upper.get() {
520                let mut to_remove = Vec::<FsString>::new();
521                for item in dir.read_dir_entries(locked, current_task)?.drain(..) {
522                    if !dir.is_whiteout_child(locked, current_task, &item)? {
523                        return error!(ENOTEMPTY);
524                    }
525                    lower_entries.remove(&item.name);
526                    to_remove.push(item.name);
527                }
528
529                if !lower_entries.is_empty() {
530                    return error!(ENOTEMPTY);
531                }
532
533                // Mark the directory as opaque. Children can be removed after this.
534                dir.set_opaque_xattr(locked, current_task)?;
535                let _ = self.upper_is_opaque.set(());
536
537                // Finally, remove the children.
538                for name in to_remove.iter() {
539                    dir.entry().unlink(
540                        locked,
541                        current_task,
542                        dir.mount(),
543                        name.as_ref(),
544                        UnlinkKind::NonDirectory,
545                        false,
546                    )?;
547                }
548            }
549        }
550
551        Ok(())
552    }
553
554    fn as_mounter<R, F: FnOnce() -> R>(&self, current_task: &CurrentTask, do_work: F) -> R {
555        current_task.override_creds(self.stack.mounter.clone(), do_work)
556    }
557}
558
559struct OverlayNodeOps {
560    node: Arc<OverlayNode>,
561}
562
563impl FsNodeOps for OverlayNodeOps {
564    fn check_access(
565        &self,
566        locked: &mut Locked<FileOpsCore>,
567        node: &FsNode,
568        current_task: &CurrentTask,
569        access: security::PermissionFlags,
570        info: &DynamicLockDepRwLock<FsNodeInfo>,
571        reason: CheckAccessReason,
572        audit_context: security::Auditable<'_>,
573    ) -> Result<(), Errno> {
574        node.default_check_access_impl(current_task, access, reason, info.read(), audit_context)?;
575
576        self.node.as_mounter(current_task, || {
577            if let Some(entry) = self.node.upper.get() {
578                entry.entry.node.check_access(
579                    locked,
580                    current_task,
581                    entry.mount(),
582                    access,
583                    reason,
584                    audit_context,
585                )
586            } else {
587                let entry = self.node.lower.as_ref().expect("Either upper or lower node is set");
588                let lower_node = &entry.entry.node;
589
590                // If the lower node is a regular file, directory or symlink then opening it for
591                // write access will cause it to be copied-up, so the mounter only requires read
592                // access to the underlying node.
593                //
594                // If the lower node is "special" (i.e. a device, FIFO or socket) then writes will
595                // affect the underlying resource, so to avoid privilege escalation via overlays,
596                // the mounter is still required to have write access to the node. This works
597                // even if the lower filesystem is readonly because special nodes remain writable
598                // in that case (though they may not be modified or unlinked, which would require
599                // actually writing to the filesystem).
600                let mut access = access;
601                if access.contains(PermissionFlags::WRITE) && !lower_node.info().mode.is_special() {
602                    // Verify that the mounter will be able to write to copy-up the node.
603                    // TODO: https://fxbug.dev/403260093 - Fix this to also verify discretionary
604                    // write access to the mounter, while correctly taking into account the
605                    // `context=` mount option (if any) for the mandatory write access check.
606                    security::fs_node_permission(
607                        current_task,
608                        node,
609                        PermissionFlags::WRITE,
610                        audit_context,
611                    )?;
612
613                    access |= PermissionFlags::READ;
614                    access &= !(PermissionFlags::WRITE | PermissionFlags::APPEND);
615                }
616
617                lower_node.check_access(
618                    locked,
619                    current_task,
620                    &entry.mount,
621                    access,
622                    reason,
623                    audit_context,
624                )
625            }
626        })
627    }
628
629    fn create_file_ops(
630        &self,
631        locked: &mut Locked<FileOpsCore>,
632        node: &FsNode,
633        current_task: &CurrentTask,
634        flags: OpenFlags,
635    ) -> Result<Box<dyn FileOps>, Errno> {
636        self.node.as_mounter(current_task, || {
637            if flags.can_write() {
638                // Only upper FS can be writable.
639                let copy_mode = if flags.contains(OpenFlags::TRUNC) {
640                    UpperCopyMode::MetadataOnly
641                } else {
642                    UpperCopyMode::CopyAll
643                };
644                self.node.ensure_upper_maybe_copy(locked, current_task, copy_mode, &node.fs())?;
645            }
646
647            let ops: Box<dyn FileOps> =
648                if node.is_dir() {
649                    Box::new(OverlayDirectory {
650                        node: self.node.clone(),
651                        dir_entries: Default::default(),
652                    })
653                } else {
654                    let state =
655                        match (self.node.upper.get(), &self.node.lower) {
656                            (Some(upper), _) => OverlayFileState::Upper(
657                                upper.entry().open_anonymous(locked, current_task, flags)?,
658                            ),
659                            (None, Some(lower)) => OverlayFileState::Lower(
660                                lower.entry().open_anonymous(locked, current_task, flags)?,
661                            ),
662                            _ => panic!("Expected either upper or lower node"),
663                        };
664
665                    Box::new(OverlayFile { node: self.node.clone(), flags, state: state.into() })
666                };
667
668            Ok(ops)
669        })
670    }
671
672    fn lookup(
673        &self,
674        locked: &mut Locked<FileOpsCore>,
675        node: &FsNode,
676        current_task: &CurrentTask,
677        name: &FsStr,
678    ) -> Result<FsNodeHandle, Errno> {
679        self.node.as_mounter(current_task, || {
680            let resolve_child = |locked: &mut Locked<FileOpsCore>,
681                                 dir_opt: Option<&ActiveEntry>| {
682                // TODO(sergeyu): lookup() checks access, but we don't need that here.
683                dir_opt
684                    .as_ref()
685                    .map(|dir| match dir.component_lookup(locked, current_task, name) {
686                        Ok(entry) => Some(Ok(entry)),
687                        Err(e) if e.code == ENOENT => None,
688                        Err(e) => Some(Err(e)),
689                    })
690                    .flatten()
691                    .transpose()
692            };
693
694            let upper: Option<ActiveEntry> = resolve_child(locked, self.node.upper.get())?;
695
696            let (upper_is_dir, upper_is_opaque) = match &upper {
697                Some(upper) if upper.is_whiteout() => return error!(ENOENT),
698                Some(upper) => {
699                    let is_dir = upper.entry().node.is_dir();
700                    let is_opaque = !is_dir || upper.is_opaque_node(locked, current_task);
701                    (is_dir, is_opaque)
702                }
703                None => (false, false),
704            };
705
706            let parent_upper_is_opaque = self.node.upper_is_opaque.get().is_some();
707
708            // We don't need to resolve the lower node if we have an opaque node in the upper dir.
709            let lookup_lower = !parent_upper_is_opaque && !upper_is_opaque;
710            let lower: Option<ActiveEntry> = if lookup_lower {
711                match resolve_child(locked, self.node.lower.as_ref())? {
712                    // If the upper node is a directory and the lower isn't then ignore the lower node.
713                    Some(lower) if upper_is_dir && !lower.entry().node.is_dir() => None,
714                    Some(lower) if lower.is_whiteout() => None,
715                    result => result,
716                }
717            } else {
718                None
719            };
720
721            if upper.is_none() && lower.is_none() {
722                return error!(ENOENT);
723            }
724
725            Ok(self.node.init_fs_node_for_child(node, lower, upper))
726        })
727    }
728
729    fn mknod(
730        &self,
731        locked: &mut Locked<FileOpsCore>,
732        node: &FsNode,
733        current_task: &CurrentTask,
734        name: &FsStr,
735        mode: FileMode,
736        dev: DeviceId,
737        owner: FsCred,
738    ) -> Result<FsNodeHandle, Errno> {
739        if mode.fmt() == FileMode::IFCHR && dev == DeviceId::NONE {
740            // Callers are blocked from creating character device nodes with Id zero, which would
741            // be indistuinguishable from those created to represent whiteouts.
742            return error!(EPERM);
743        }
744        let mut creds = Credentials::clone(&self.node.stack.mounter);
745        security::dentry_create_files_as(current_task, node, mode, name, &mut creds)?;
746        current_task.override_creds(Arc::new(creds), || {
747            let new_upper_node = self.node.create_entry(
748                locked,
749                node,
750                current_task,
751                name,
752                |locked, dir, temp_name| {
753                    dir.create_entry(
754                        locked,
755                        current_task,
756                        temp_name,
757                        |locked, dir_node, mount, name| {
758                            dir_node.create_node(
759                                locked,
760                                current_task,
761                                mount,
762                                name,
763                                mode,
764                                dev,
765                                owner.clone(),
766                            )
767                        },
768                    )
769                },
770            )?;
771            Ok(self.node.init_fs_node_for_child(node, None, Some(new_upper_node)))
772        })
773    }
774
775    fn mkdir(
776        &self,
777        locked: &mut Locked<FileOpsCore>,
778        node: &FsNode,
779        current_task: &CurrentTask,
780        name: &FsStr,
781        mode: FileMode,
782        owner: FsCred,
783    ) -> Result<FsNodeHandle, Errno> {
784        let mut creds = Credentials::clone(&self.node.stack.mounter);
785        security::dentry_create_files_as(current_task, node, mode, name, &mut creds)?;
786        current_task.override_creds(Arc::new(creds), || {
787            let new_upper_node = self.node.create_entry(
788                locked,
789                node,
790                current_task,
791                name,
792                |locked, dir, temp_name| {
793                    let entry = dir.create_entry(
794                        locked,
795                        current_task,
796                        temp_name,
797                        |locked, dir_node, mount, name| {
798                            dir_node.create_node(
799                                locked,
800                                current_task,
801                                mount,
802                                name,
803                                mode,
804                                DeviceId::NONE,
805                                owner.clone(),
806                            )
807                        },
808                    )?;
809
810                    // Set opaque attribute to ensure the new directory is not merged with lower.
811                    entry.set_opaque_xattr(locked, current_task)?;
812
813                    Ok(entry)
814                },
815            )?;
816
817            Ok(self.node.init_fs_node_for_child(node, None, Some(new_upper_node)))
818        })
819    }
820
821    fn create_symlink(
822        &self,
823        locked: &mut Locked<FileOpsCore>,
824        node: &FsNode,
825        current_task: &CurrentTask,
826        name: &FsStr,
827        target: &FsStr,
828        owner: FsCred,
829    ) -> Result<FsNodeHandle, Errno> {
830        let mut creds = Credentials::clone(&self.node.stack.mounter);
831        security::dentry_create_files_as(current_task, node, FileMode::IFLNK, name, &mut creds)?;
832        current_task.override_creds(Arc::new(creds), || {
833            let new_upper_node = self.node.create_entry(
834                locked,
835                node,
836                current_task,
837                name,
838                |locked, dir, temp_name| {
839                    dir.create_entry(
840                        locked,
841                        current_task,
842                        temp_name,
843                        |locked, dir_node, mount, name| {
844                            dir_node.create_symlink(
845                                locked,
846                                current_task,
847                                mount,
848                                name,
849                                target,
850                                owner.clone(),
851                            )
852                        },
853                    )
854                },
855            )?;
856            Ok(self.node.init_fs_node_for_child(node, None, Some(new_upper_node)))
857        })
858    }
859
860    fn readlink(
861        &self,
862        locked: &mut Locked<FileOpsCore>,
863        _node: &FsNode,
864        current_task: &CurrentTask,
865    ) -> Result<SymlinkTarget, Errno> {
866        self.node.as_mounter(current_task, || {
867            self.node.main_entry().entry().node.readlink(locked, current_task)
868        })
869    }
870
871    fn link(
872        &self,
873        locked: &mut Locked<FileOpsCore>,
874        node: &FsNode,
875        current_task: &CurrentTask,
876        name: &FsStr,
877        child: &FsNodeHandle,
878    ) -> Result<(), Errno> {
879        self.node.as_mounter(current_task, || {
880            let child_overlay = OverlayNode::from_fs_node(child)?;
881            let upper_child = child_overlay.ensure_upper(locked, current_task, &node.fs())?;
882            self.node.create_entry(
883                locked,
884                node,
885                current_task,
886                name,
887                |locked, dir, temp_name| {
888                    dir.create_entry(
889                        locked,
890                        current_task,
891                        temp_name,
892                        |locked, dir_node, mount, name| {
893                            dir_node.link(
894                                locked,
895                                current_task,
896                                mount,
897                                name,
898                                &upper_child.entry().node,
899                            )
900                        },
901                    )
902                },
903            )?;
904            Ok(())
905        })
906    }
907
908    fn unlink(
909        &self,
910        locked: &mut Locked<FileOpsCore>,
911        node: &FsNode,
912        current_task: &CurrentTask,
913        name: &FsStr,
914        child: &FsNodeHandle,
915    ) -> Result<(), Errno> {
916        self.node.as_mounter(current_task, || {
917            let upper = self.node.ensure_upper(locked, current_task, &node.fs())?;
918            let child_overlay = OverlayNode::from_fs_node(child)?;
919            child_overlay.prepare_to_unlink(locked, current_task)?;
920
921            let need_whiteout = self.node.lower_entry_exists(locked, current_task, name)?;
922            if need_whiteout {
923                self.node.stack.create_upper_entry(
924                    locked,
925                    current_task,
926                    &upper,
927                    &name,
928                    |locked, work, name| work.create_whiteout(locked, current_task, name),
929                    |_, _entry| Ok(()),
930                )?;
931            } else if let Some(child_upper) = child_overlay.upper.get() {
932                let kind = if child_upper.entry().node.is_dir() {
933                    UnlinkKind::Directory
934                } else {
935                    UnlinkKind::NonDirectory
936                };
937                upper.entry().unlink(locked, current_task, upper.mount(), name, kind, false)?;
938            }
939
940            Ok(())
941        })
942    }
943
944    fn fetch_and_refresh_info<'a>(
945        &self,
946        locked: &mut Locked<FileOpsCore>,
947        _node: &FsNode,
948        current_task: &CurrentTask,
949        info: &'a DynamicLockDepRwLock<FsNodeInfo>,
950    ) -> Result<LockDepReadGuard<'a, FsNodeInfo>, Errno> {
951        self.node.as_mounter(current_task, || {
952            let underlying_node = &self.node.main_entry().entry().node;
953            // Work-around to ensure that mounter `getattr` access is required when a caller tries
954            // to `stat()` a file.
955            security::check_fs_node_getattr_access(current_task, underlying_node)?;
956            let real_info = underlying_node.fetch_and_refresh_info(locked, current_task)?.clone();
957            let mut lock = info.write();
958            *lock = real_info;
959            Ok(LockDepWriteGuard::downgrade(lock))
960        })
961    }
962
963    // Work-around to allow the append-only writes to proceed without `getattr` access checks,
964    // which `fetch_and_refresh_info()`, above, would otherwise introduce.
965    fn get_size(
966        &self,
967        locked: &mut Locked<FileOpsCore>,
968        _node: &FsNode,
969        current_task: &CurrentTask,
970    ) -> Result<usize, Errno> {
971        self.node.as_mounter(current_task, || {
972            self.node.main_entry().entry().node.get_size(locked, current_task)
973        })
974    }
975
976    fn update_attributes(
977        &self,
978        locked: &mut Locked<FileOpsCore>,
979        node: &FsNode,
980        current_task: &CurrentTask,
981        new_info: &FsNodeInfo,
982        has: zxio_node_attr_has_t,
983    ) -> Result<(), Errno> {
984        self.node.as_mounter(current_task, || {
985            let upper = self.node.ensure_upper(locked, current_task, &node.fs())?.entry();
986            upper.node.update_attributes(locked, current_task, |info| {
987                if has.modification_time {
988                    info.time_modify = new_info.time_modify;
989                }
990                if has.access_time {
991                    info.time_access = new_info.time_access;
992                }
993                if has.mode {
994                    info.mode = new_info.mode;
995                }
996                if has.uid {
997                    info.uid = new_info.uid;
998                }
999                if has.gid {
1000                    info.gid = new_info.gid;
1001                }
1002                if has.rdev {
1003                    info.rdev = new_info.rdev;
1004                }
1005                Ok(())
1006            })
1007        })
1008    }
1009
1010    fn append_lock_read<'a>(
1011        &'a self,
1012        locked: &'a mut Locked<BeforeFsNodeAppend>,
1013        node: &'a FsNode,
1014        current_task: &CurrentTask,
1015    ) -> Result<(RwQueueReadGuard<'a, FsNodeAppend>, &'a mut Locked<FsNodeAppend>), Errno> {
1016        self.node.as_mounter(current_task, || {
1017            let upper_node =
1018                self.node.ensure_upper(locked, current_task, &node.fs())?.entry.node.as_ref();
1019            upper_node.ops().append_lock_read(locked, upper_node, current_task)
1020        })
1021    }
1022
1023    fn append_lock_write<'a>(
1024        &'a self,
1025        locked: &'a mut Locked<BeforeFsNodeAppend>,
1026        node: &'a FsNode,
1027        current_task: &CurrentTask,
1028    ) -> Result<(RwQueueWriteGuard<'a, FsNodeAppend>, &'a mut Locked<FsNodeAppend>), Errno> {
1029        self.node.as_mounter(current_task, || {
1030            let upper_node =
1031                self.node.ensure_upper(locked, current_task, &node.fs())?.entry.node.as_ref();
1032            upper_node.ops().append_lock_write(locked, upper_node, current_task)
1033        })
1034    }
1035
1036    fn truncate(
1037        &self,
1038        locked: &mut Locked<FileOpsCore>,
1039        guard: &AppendLockWriteGuard<'_>,
1040        node: &FsNode,
1041        current_task: &CurrentTask,
1042        length: u64,
1043    ) -> Result<(), Errno> {
1044        self.node.as_mounter(current_task, || {
1045            let upper = self.node.ensure_upper(locked, current_task, &node.fs())?;
1046
1047            upper.entry().node.truncate_locked(locked, guard, current_task, length)
1048        })
1049    }
1050
1051    fn allocate(
1052        &self,
1053        locked: &mut Locked<FileOpsCore>,
1054        guard: &AppendLockWriteGuard<'_>,
1055        node: &FsNode,
1056        current_task: &CurrentTask,
1057        mode: FallocMode,
1058        offset: u64,
1059        length: u64,
1060    ) -> Result<(), Errno> {
1061        self.node.as_mounter(current_task, || {
1062            let node = &self.node.ensure_upper(locked, current_task, &node.fs())?.entry().node;
1063            node.fallocate_locked(locked, guard, current_task, mode, offset, length)
1064        })
1065    }
1066
1067    fn get_xattr(
1068        &self,
1069        locked: &mut Locked<FileOpsCore>,
1070        _node: &FsNode,
1071        current_task: &CurrentTask,
1072        name: &FsStr,
1073        max_size: usize,
1074    ) -> Result<ValueOrSize<FsString>, Errno> {
1075        let entry = self
1076            .node
1077            .upper
1078            .get()
1079            .or(self.node.lower.as_ref())
1080            .expect("expect either lower or upper node");
1081        self.node.as_mounter(current_task, || {
1082            entry.entry().node.get_xattr(locked, current_task, &entry.mount, name, max_size)
1083        })
1084    }
1085
1086    fn set_xattr(
1087        &self,
1088        locked: &mut Locked<FileOpsCore>,
1089        node: &FsNode,
1090        current_task: &CurrentTask,
1091        name: &FsStr,
1092        value: &FsStr,
1093        op: XattrOp,
1094    ) -> Result<(), Errno> {
1095        self.node.as_mounter(current_task, || {
1096            let upper = self.node.ensure_upper(locked, current_task, &node.fs())?;
1097            upper.entry().node.set_xattr(locked, current_task, &upper.mount, name, value, op)
1098        })
1099    }
1100
1101    fn remove_xattr(
1102        &self,
1103        locked: &mut Locked<FileOpsCore>,
1104        node: &FsNode,
1105        current_task: &CurrentTask,
1106        name: &FsStr,
1107    ) -> Result<(), Errno> {
1108        self.node.as_mounter(current_task, || {
1109            let upper = self.node.ensure_upper(locked, current_task, &node.fs())?;
1110            upper.entry().node.remove_xattr(locked, current_task, &upper.mount, name)
1111        })
1112    }
1113
1114    fn list_xattrs(
1115        &self,
1116        locked: &mut Locked<FileOpsCore>,
1117        _node: &FsNode,
1118        current_task: &CurrentTask,
1119        max_size: usize,
1120    ) -> Result<ValueOrSize<Vec<FsString>>, Errno> {
1121        self.node.as_mounter(current_task, || {
1122            let entry = self
1123                .node
1124                .upper
1125                .get()
1126                .or(self.node.lower.as_ref())
1127                .expect("expect either lower or upper node");
1128            entry.entry().node.list_xattrs(locked, current_task, max_size)
1129        })
1130    }
1131}
1132struct OverlayDirectory {
1133    node: Arc<OverlayNode>,
1134    dir_entries: LockDepRwLock<DirEntries, OverlayFsDirEntriesLock>,
1135}
1136
1137impl OverlayDirectory {
1138    fn refresh_dir_entries<L>(
1139        &self,
1140        locked: &mut Locked<L>,
1141        current_task: &CurrentTask,
1142    ) -> Result<(), Errno>
1143    where
1144        L: LockEqualOrBefore<FileOpsCore>,
1145    {
1146        let mut entries = DirEntries::new();
1147
1148        let upper_is_opaque = self.node.upper_is_opaque.get().is_some();
1149        let merge_with_lower = self.node.lower.is_some() && !upper_is_opaque;
1150
1151        // First enumerate entries in the upper dir. Then enumerate the lower dir and add only
1152        // items that are not present in the upper.
1153        let mut upper_set = BTreeSet::new();
1154        if let Some(dir) = self.node.upper.get() {
1155            for item in dir.read_dir_entries(locked, current_task)?.drain(..) {
1156                // Fill `upper_set` only if we will need it later.
1157                if merge_with_lower {
1158                    upper_set.insert(item.name.clone());
1159                }
1160                if !dir.is_whiteout_child(locked, current_task, &item)? {
1161                    entries.push(item);
1162                }
1163            }
1164        }
1165
1166        if merge_with_lower {
1167            if let Some(dir) = &self.node.lower {
1168                for item in dir.read_dir_entries(locked, current_task)?.drain(..) {
1169                    if !upper_set.contains(&item.name)
1170                        && !dir.is_whiteout_child(locked, current_task, &item)?
1171                    {
1172                        entries.push(item);
1173                    }
1174                }
1175            }
1176        }
1177
1178        *self.dir_entries.write() = entries;
1179
1180        Ok(())
1181    }
1182}
1183
1184impl FileOps for OverlayDirectory {
1185    fileops_impl_directory!();
1186    fileops_impl_noop_sync!();
1187
1188    fn seek(
1189        &self,
1190        _locked: &mut Locked<FileOpsCore>,
1191        _file: &FileObject,
1192        current_task: &CurrentTask,
1193        current_offset: off_t,
1194        target: SeekTarget,
1195    ) -> Result<off_t, Errno> {
1196        self.node
1197            .as_mounter(current_task, || default_seek(current_offset, target, || error!(EINVAL)))
1198    }
1199
1200    fn readdir(
1201        &self,
1202        locked: &mut Locked<FileOpsCore>,
1203        file: &FileObject,
1204        current_task: &CurrentTask,
1205        sink: &mut dyn DirentSink,
1206    ) -> Result<(), Errno> {
1207        self.node.as_mounter(current_task, || {
1208            if sink.offset() == 0 {
1209                self.refresh_dir_entries(locked, current_task)?;
1210            }
1211
1212            emit_dotdot(file, sink)?;
1213
1214            for item in self.dir_entries.read().iter().skip(sink.offset() as usize - 2) {
1215                sink.add(item.inode_num, sink.offset() + 1, item.entry_type, item.name.as_ref())?;
1216            }
1217
1218            Ok(())
1219        })
1220    }
1221}
1222
1223enum OverlayFileState {
1224    Lower(FileHandle),
1225    Upper(FileHandle),
1226}
1227
1228impl OverlayFileState {
1229    fn file(&self) -> &FileHandle {
1230        match self {
1231            Self::Lower(f) | Self::Upper(f) => f,
1232        }
1233    }
1234}
1235
1236struct OverlayFile {
1237    node: Arc<OverlayNode>,
1238    flags: OpenFlags,
1239    state: LockDepRwLock<OverlayFileState, OverlayFsStateLock>,
1240}
1241
1242impl FileOps for OverlayFile {
1243    fileops_impl_seekable!();
1244
1245    fn read(
1246        &self,
1247        locked: &mut Locked<FileOpsCore>,
1248        _file: &FileObject,
1249        current_task: &CurrentTask,
1250        offset: usize,
1251        data: &mut dyn OutputBuffer,
1252    ) -> Result<usize, Errno> {
1253        self.node.as_mounter(current_task, || {
1254            let mut state = self.state.read();
1255
1256            // Check if the file was promoted to the upper FS. In that case we need to reopen it
1257            // from there.
1258            if let Some(upper) = self.node.upper.get() {
1259                if matches!(*state, OverlayFileState::Lower(_)) {
1260                    std::mem::drop(state);
1261
1262                    {
1263                        let mut write_state = self.state.write();
1264
1265                        // TODO(mariagl): don't hold write_state while calling open_anonymous.
1266                        // It may call back into read(), causing lock order inversion.
1267                        *write_state = OverlayFileState::Upper(upper.entry().open_anonymous(
1268                            locked,
1269                            current_task,
1270                            self.flags,
1271                        )?);
1272                    }
1273                    state = self.state.read();
1274                }
1275            }
1276
1277            // TODO(mariagl): Drop state here
1278            let file = state.file();
1279            security::file_permission(current_task, &file, security::PermissionFlags::READ)?;
1280            file.ops().read(locked, file, current_task, offset, data)
1281        })
1282    }
1283
1284    fn write(
1285        &self,
1286        locked: &mut Locked<FileOpsCore>,
1287        _file: &FileObject,
1288        current_task: &CurrentTask,
1289        offset: usize,
1290        data: &mut dyn InputBuffer,
1291    ) -> Result<usize, Errno> {
1292        self.node.as_mounter(current_task, || {
1293            let state = self.state.read();
1294            let file = match &*state {
1295                OverlayFileState::Upper(f) => f.clone(),
1296
1297                // `write()` should be called only for files that were opened for write, and that
1298                // required the file to be promoted to the upper FS.
1299                OverlayFileState::Lower(_) => panic!("write() called for a lower FS file."),
1300            };
1301            std::mem::drop(state);
1302            security::file_permission(current_task, &file, security::PermissionFlags::WRITE)?;
1303            file.ops().write(locked, &file, current_task, offset, data)
1304        })
1305    }
1306
1307    fn sync(&self, _file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
1308        self.node.as_mounter(current_task, || {
1309            let state = self.state.read();
1310            let file = state.file();
1311            file.ops().sync(file, current_task)
1312        })
1313    }
1314
1315    fn get_memory(
1316        &self,
1317        locked: &mut Locked<FileOpsCore>,
1318        _file: &FileObject,
1319        current_task: &CurrentTask,
1320        length: Option<usize>,
1321        prot: starnix_core::mm::ProtectionFlags,
1322    ) -> Result<Arc<MemoryObject>, Errno> {
1323        self.node.as_mounter(current_task, || {
1324            let state = self.state.read();
1325            let file = state.file();
1326            // Not that the VMO returned here will not updated if the file is promoted to upper FS
1327            // later. This is consistent with OverlayFS behavior on Linux, see
1328            // https://docs.kernel.org/filesystems/overlayfs.html#non-standard-behavior .
1329            file.ops().get_memory(locked, file, current_task, length, prot)
1330        })
1331    }
1332}
1333
1334pub fn new_overlay_fs(
1335    locked: &mut Locked<Unlocked>,
1336    current_task: &CurrentTask,
1337    options: FileSystemOptions,
1338) -> Result<FileSystemHandle, Errno> {
1339    OverlayStack::new_fs(locked, current_task, options)
1340}
1341
1342pub struct OverlayStack {
1343    // Keep references to the underlying file systems to ensure they outlive `overlayfs` since
1344    // they may be unmounted before overlayfs.
1345    #[allow(unused)]
1346    lower_fs: FileSystemHandle,
1347    upper_fs: FileSystemHandle,
1348
1349    work: ActiveEntry,
1350
1351    // Used when interacting with the `upper_fs`, `lower_fs` or `work` directories.
1352    mounter: Arc<Credentials>,
1353}
1354
1355impl OverlayStack {
1356    fn new_fs(
1357        locked: &mut Locked<Unlocked>,
1358        current_task: &CurrentTask,
1359        options: FileSystemOptions,
1360    ) -> Result<FileSystemHandle, Errno> {
1361        match options.params.get("redirect_dir".as_bytes()) {
1362            None => (),
1363            Some(o) if o == "off" => (),
1364            Some(_) => {
1365                track_stub!(TODO("https://fxbug.dev/322874205"), "overlayfs redirect_dir");
1366                return error!(ENOTSUP);
1367            }
1368        }
1369
1370        let lower = resolve_dir_param(locked, current_task, &options.params, "lowerdir".into())?;
1371        let upper = resolve_dir_param(locked, current_task, &options.params, "upperdir".into())?;
1372        let work = resolve_dir_param(locked, current_task, &options.params, "workdir".into())?;
1373
1374        let lower_fs = lower.entry().node.fs();
1375        let upper_fs = upper.entry().node.fs();
1376
1377        if upper_fs.fs_lockdep_type() == FsLockDepType::Recursive {
1378            // Recursive filesystems (like OverlayFS itself) are not supported as upper filesystems.
1379            return error!(EINVAL);
1380        }
1381
1382        if !Arc::ptr_eq(&upper_fs, &work.entry().node.fs()) {
1383            log_error!("overlayfs: upperdir and workdir must be on the same FS");
1384            return error!(EINVAL);
1385        }
1386
1387        let kernel = current_task.kernel();
1388        let mounter = current_task.current_creds().clone();
1389        let stack = Arc::new(OverlayStack { lower_fs, upper_fs, work, mounter });
1390        let root_node = OverlayNode::new(stack.clone(), Some(lower), Some(upper), None);
1391        let fs =
1392            FileSystem::new(locked, kernel, CacheMode::Uncached, OverlayFs { stack }, options)?;
1393        let root_ino = fs.allocate_ino();
1394        fs.create_root(root_ino, OverlayNodeOps { node: root_node });
1395        Ok(fs)
1396    }
1397
1398    /// Given a filesystem, wraps it in a tmpfs-backed writable overlayfs.
1399    pub fn wrap_fs_in_writable_layer<L>(
1400        locked: &mut Locked<L>,
1401        kernel: &Kernel,
1402        rootfs: FileSystemHandle,
1403    ) -> Result<FileSystemHandle, Errno>
1404    where
1405        L: LockEqualOrBefore<FileOpsCore>,
1406    {
1407        let lower = ActiveEntry { entry: rootfs.root().clone(), mount: MountInfo::detached() };
1408
1409        // Create upper and work directories in an invisible tmpfs.
1410        let invisible_tmp = TmpFs::new_fs(locked, kernel);
1411
1412        let create_directory = |fs: &FileSystemHandle| {
1413            let ino = fs.allocate_ino();
1414            let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
1415            let node = fs.create_detached_node(ino, TmpFsDirectory::new(), info);
1416            let dir_entry = DirEntry::new(node, None, FsString::default());
1417
1418            // TODO: https://fxbug.dev/455771186 - Revise FsNode initialization to better ensure
1419            // that all the things are appropriately labeled.
1420            security::fs_node_init_with_dentry_deferred(kernel, &dir_entry);
1421
1422            dir_entry
1423        };
1424
1425        let upper =
1426            ActiveEntry { entry: create_directory(&invisible_tmp), mount: MountInfo::detached() };
1427        let work =
1428            ActiveEntry { entry: create_directory(&invisible_tmp), mount: MountInfo::detached() };
1429
1430        let lower_fs = rootfs;
1431        let upper_fs = invisible_tmp;
1432
1433        let mounter = Credentials::root();
1434        let stack = Arc::new(OverlayStack { lower_fs, upper_fs, work, mounter });
1435        let root_node = OverlayNode::new(stack.clone(), Some(lower), Some(upper), None);
1436        let fs = FileSystem::new(
1437            locked,
1438            kernel,
1439            CacheMode::Uncached,
1440            OverlayFs { stack },
1441            FileSystemOptions::default(),
1442        )?;
1443        let root_ino = fs.allocate_ino();
1444        fs.create_root(root_ino, OverlayNodeOps { node: root_node });
1445        Ok(fs)
1446    }
1447
1448    // Helper used to create new entry called `name` in `target_dir` in the upper FS.
1449    // 1. Calls `try_create` to create a new entry in `work`. It is called repeateadly with a
1450    //    new name until it returns any result other than `EEXIST`.
1451    // 2. `do_init` is called to initilize the contents and the attributes of the new entry, etc.
1452    // 3. The new entry is moved to `target_dir`. If there is an existing entry called `name` in
1453    //    `target_dir` then it's replaced with the new entry.
1454    // The temp file is cleared from the work dir if either of the last two steps fails.
1455    fn create_upper_entry<FCreate, FInit, L>(
1456        &self,
1457        locked: &mut Locked<L>,
1458        current_task: &CurrentTask,
1459        target_dir: &ActiveEntry,
1460        name: &FsStr,
1461        try_create: FCreate,
1462        do_init: FInit,
1463    ) -> Result<ActiveEntry, Errno>
1464    where
1465        L: LockEqualOrBefore<FileOpsCore>,
1466        FCreate: Fn(&mut Locked<L>, &ActiveEntry, &FsStr) -> Result<ActiveEntry, Errno>,
1467        FInit: FnOnce(&mut Locked<L>, &ActiveEntry) -> Result<(), Errno>,
1468    {
1469        let mut rng = rand::rng();
1470        let (temp_name, entry) = loop {
1471            let x: u64 = rng.random();
1472            let temp_name = FsString::from(format!("tmp{:x}", x));
1473            match try_create(locked, &self.work, temp_name.as_ref()) {
1474                Err(err) if err.code == EEXIST => continue,
1475                Err(err) => return Err(err),
1476                Ok(entry) => break (temp_name, entry),
1477            }
1478        };
1479
1480        do_init(locked, &entry)
1481            .and_then(|()| {
1482                DirEntry::rename(
1483                    locked,
1484                    current_task,
1485                    self.work.entry(),
1486                    self.work.mount(),
1487                    temp_name.as_ref(),
1488                    target_dir.entry(),
1489                    target_dir.mount(),
1490                    name,
1491                    RenameFlags::REPLACE_ANY,
1492                )
1493            })
1494            .map_err(|e| {
1495                // Remove the temp entry in case of a failure.
1496                self.work
1497                    .entry()
1498                    .unlink(
1499                        locked,
1500                        current_task,
1501                        self.work.mount(),
1502                        temp_name.as_ref(),
1503                        UnlinkKind::NonDirectory,
1504                        false,
1505                    )
1506                    .unwrap_or_else(|e| {
1507                        log_error!("Failed to cleanup work dir after an error: {}", e)
1508                    });
1509                e
1510            })?;
1511
1512        Ok(entry)
1513    }
1514}
1515
1516struct OverlayFs {
1517    stack: Arc<OverlayStack>,
1518}
1519
1520impl FileSystemOps for OverlayFs {
1521    fn fs_lockdep_type(&self) -> FsLockDepType {
1522        FsLockDepType::Recursive
1523    }
1524
1525    fn statfs(
1526        &self,
1527        locked: &mut Locked<FileOpsCore>,
1528        _fs: &FileSystem,
1529        current_task: &CurrentTask,
1530    ) -> Result<statfs, Errno> {
1531        current_task.override_creds(self.stack.mounter.clone(), || {
1532            self.stack.upper_fs.statfs(locked, current_task)
1533        })
1534    }
1535
1536    fn name(&self) -> &'static FsStr {
1537        "overlay".into()
1538    }
1539
1540    fn rename(
1541        &self,
1542        locked: &mut Locked<FileOpsCore>,
1543        _fs: &FileSystem,
1544        current_task: &CurrentTask,
1545        context: &mut RenameContext<'_>,
1546        old_name: &FsStr,
1547        new_name: &FsStr,
1548    ) -> Result<(), Errno> {
1549        let old_parent = &context.old_parent().node;
1550        let new_parent = &context.new_parent().node;
1551        let renamed = &context.renamed.node;
1552        current_task.override_creds(self.stack.mounter.clone(), || {
1553            let renamed_overlay = OverlayNode::from_fs_node(renamed)?;
1554            if renamed_overlay.has_lower() && renamed_overlay.main_entry().entry().node.is_dir() {
1555                // Return EXDEV for directory renames. Potentially they may be handled with the
1556                // `redirect_dir` feature, but it's not implemented here yet.
1557                // See https://docs.kernel.org/filesystems/overlayfs.html#renaming-directories
1558                return error!(EXDEV);
1559            }
1560            renamed_overlay.ensure_upper(locked, current_task, &renamed.fs())?;
1561
1562            let old_parent_overlay = OverlayNode::from_fs_node(old_parent)?;
1563            let old_parent_upper =
1564                old_parent_overlay.ensure_upper(locked, current_task, &renamed.fs())?;
1565
1566            let new_parent_overlay = OverlayNode::from_fs_node(new_parent)?;
1567            let new_parent_upper =
1568                new_parent_overlay.ensure_upper(locked, current_task, &renamed.fs())?;
1569
1570            let need_whiteout =
1571                old_parent_overlay.lower_entry_exists(locked, current_task, old_name)?;
1572
1573            DirEntry::rename(
1574                locked,
1575                current_task,
1576                old_parent_upper.entry(),
1577                old_parent_upper.mount(),
1578                old_name,
1579                new_parent_upper.entry(),
1580                new_parent_upper.mount(),
1581                new_name,
1582                RenameFlags::REPLACE_ANY,
1583            )?;
1584
1585            // If the old node existed in lower FS, then override it in the upper FS with a
1586            // whiteout.
1587            if need_whiteout {
1588                match old_parent_upper.create_whiteout(locked, current_task, old_name) {
1589                    Err(e) => log_warn!("overlayfs: failed to create whiteout for {old_name}: {e}"),
1590                    Ok(_) => (),
1591                }
1592            }
1593
1594            Ok(())
1595        })
1596    }
1597
1598    fn unmount(&self) {}
1599}
1600
1601/// Helper used to resolve directories passed in mount options. The directory is resolved in the
1602/// namespace of the calling process, but only `DirEntry` is returned (detached from the
1603/// namespace). The corresponding file systems may be unmounted before overlayfs that uses them.
1604fn resolve_dir_param(
1605    locked: &mut Locked<Unlocked>,
1606    current_task: &CurrentTask,
1607    params: &MountParams,
1608    name: &FsStr,
1609) -> Result<ActiveEntry, Errno> {
1610    let path = params.get(&**name).ok_or_else(|| {
1611        log_error!("overlayfs: {name} was not specified");
1612        errno!(EINVAL)
1613    })?;
1614
1615    current_task
1616        .open_file(locked, path.as_ref(), OpenFlags::RDONLY | OpenFlags::DIRECTORY)
1617        .map(|f| ActiveEntry { entry: f.name.entry.clone(), mount: f.name.mount.clone() })
1618        .map_err(|e| {
1619            log_error!("overlayfs: Failed to lookup {path}: {}", e);
1620            e
1621        })
1622}
1623
1624/// Copies file content from one file to another.
1625fn copy_file_content<L>(
1626    locked: &mut Locked<L>,
1627    current_task: &CurrentTask,
1628    from: &ActiveEntry,
1629    to: &ActiveEntry,
1630) -> Result<(), Errno>
1631where
1632    L: LockEqualOrBefore<FileOpsCore>,
1633{
1634    let locked = locked.cast_locked::<FileOpsCore>();
1635    let from_file = from.entry().open_anonymous(locked, current_task, OpenFlags::RDONLY)?;
1636    let to_file = to.entry().open_anonymous(locked, current_task, OpenFlags::WRONLY)?;
1637
1638    security::fs_node_permission(
1639        current_task,
1640        from_file.node().as_ref(),
1641        security::PermissionFlags::READ,
1642        (&**from_file).into(),
1643    )?;
1644    security::fs_node_permission(
1645        current_task,
1646        to_file.node().as_ref(),
1647        security::PermissionFlags::WRITE,
1648        (&**to_file).into(),
1649    )?;
1650
1651    const BUFFER_SIZE: usize = 4096;
1652
1653    let mut read_offset = 0;
1654    let mut write_offset = 0;
1655    loop {
1656        // TODO(sergeyu): Reuse buffer between iterations.
1657
1658        let mut output_buffer = VecOutputBuffer::new(BUFFER_SIZE);
1659        let bytes_read = from_file.ops().read(
1660            locked,
1661            &from_file,
1662            current_task,
1663            read_offset,
1664            &mut output_buffer,
1665        )?;
1666        if bytes_read == 0 {
1667            break;
1668        }
1669        read_offset += bytes_read;
1670
1671        let buffer: Vec<u8> = output_buffer.into();
1672        let mut input_buffer = VecInputBuffer::from(buffer);
1673        while input_buffer.available() > 0 {
1674            write_offset += to_file.ops().write(
1675                locked,
1676                &to_file,
1677                current_task,
1678                write_offset,
1679                &mut input_buffer,
1680            )?;
1681        }
1682    }
1683
1684    to_file.ops().data_sync(&to_file, current_task)?;
1685
1686    Ok(())
1687}