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