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, SymlinkTarget, UnlinkKind, ValueOrSize,
22    VecInputBuffer, 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    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        node: &FsNode,
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(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    ) -> Result<LockDepReadGuard<'a, FsNodeInfo>, Errno> {
765        self.node.as_mounter(current_task, || {
766            let underlying_node = &self.node.main_entry().entry().node;
767            // Work-around to ensure that mounter `getattr` access is required when a caller tries
768            // to `stat()` a file.
769            security::check_fs_node_getattr_access(current_task, underlying_node)?;
770            let real_info = underlying_node.fetch_and_refresh_info(current_task)?.clone();
771            let mut lock = info.write();
772            *lock = real_info;
773            Ok(LockDepWriteGuard::downgrade(lock))
774        })
775    }
776
777    // Work-around to allow the append-only writes to proceed without `getattr` access checks,
778    // which `fetch_and_refresh_info()`, above, would otherwise introduce.
779    fn get_size(&self, _node: &FsNode, current_task: &CurrentTask) -> Result<usize, Errno> {
780        self.node
781            .as_mounter(current_task, || self.node.main_entry().entry().node.get_size(current_task))
782    }
783
784    fn update_attributes(
785        &self,
786        node: &FsNode,
787        current_task: &CurrentTask,
788        new_info: &FsNodeInfo,
789        has: zxio_node_attr_has_t,
790    ) -> Result<(), Errno> {
791        self.node.as_mounter(current_task, || {
792            let upper = self.node.ensure_upper(current_task, &node.fs())?.entry();
793            upper.node.update_attributes(current_task, |info| {
794                if has.modification_time {
795                    info.time_modify = new_info.time_modify;
796                }
797                if has.access_time {
798                    info.time_access = new_info.time_access;
799                }
800                if has.mode {
801                    info.mode = new_info.mode;
802                }
803                if has.uid {
804                    info.uid = new_info.uid;
805                }
806                if has.gid {
807                    info.gid = new_info.gid;
808                }
809                if has.rdev {
810                    info.rdev = new_info.rdev;
811                }
812                Ok(())
813            })
814        })
815    }
816
817    fn append_lock_read<'a>(
818        &'a self,
819        node: &'a FsNode,
820        current_task: &CurrentTask,
821    ) -> Result<RwQueueReadGuard<'a, FsNodeAppend>, Errno> {
822        self.node.as_mounter(current_task, || {
823            let upper_node = self.node.ensure_upper(current_task, &node.fs())?.entry.node.as_ref();
824            upper_node.ops().append_lock_read(upper_node, current_task)
825        })
826    }
827
828    fn append_lock_write<'a>(
829        &'a self,
830        node: &'a FsNode,
831        current_task: &CurrentTask,
832    ) -> Result<RwQueueWriteGuard<'a, FsNodeAppend>, Errno> {
833        self.node.as_mounter(current_task, || {
834            let upper_node = self.node.ensure_upper(current_task, &node.fs())?.entry.node.as_ref();
835            upper_node.ops().append_lock_write(upper_node, current_task)
836        })
837    }
838
839    fn truncate(
840        &self,
841        guard: &AppendLockWriteGuard<'_>,
842        node: &FsNode,
843        current_task: &CurrentTask,
844        length: u64,
845    ) -> Result<(), Errno> {
846        self.node.as_mounter(current_task, || {
847            let upper = self.node.ensure_upper(current_task, &node.fs())?;
848
849            upper.entry().node.truncate_locked(guard, current_task, length)
850        })
851    }
852
853    fn allocate(
854        &self,
855        guard: &AppendLockWriteGuard<'_>,
856        node: &FsNode,
857        current_task: &CurrentTask,
858        mode: FallocMode,
859        offset: u64,
860        length: u64,
861    ) -> Result<(), Errno> {
862        self.node.as_mounter(current_task, || {
863            let node = &self.node.ensure_upper(current_task, &node.fs())?.entry().node;
864            node.fallocate_locked(guard, current_task, mode, offset, length)
865        })
866    }
867
868    fn get_xattr(
869        &self,
870        _node: &FsNode,
871        current_task: &CurrentTask,
872        name: &FsStr,
873        max_size: usize,
874    ) -> Result<ValueOrSize<FsString>, Errno> {
875        let entry = self
876            .node
877            .upper
878            .get()
879            .or(self.node.lower.as_ref())
880            .expect("expect either lower or upper node");
881        self.node.as_mounter(current_task, || {
882            entry.entry().node.get_xattr(current_task, &entry.mount, name, max_size)
883        })
884    }
885
886    fn set_xattr(
887        &self,
888        node: &FsNode,
889        current_task: &CurrentTask,
890        name: &FsStr,
891        value: &FsStr,
892        op: XattrOp,
893    ) -> Result<(), Errno> {
894        self.node.as_mounter(current_task, || {
895            let upper = self.node.ensure_upper(current_task, &node.fs())?;
896            upper.entry().node.set_xattr(current_task, &upper.mount, name, value, op)
897        })
898    }
899
900    fn remove_xattr(
901        &self,
902        node: &FsNode,
903        current_task: &CurrentTask,
904        name: &FsStr,
905    ) -> Result<(), Errno> {
906        self.node.as_mounter(current_task, || {
907            let upper = self.node.ensure_upper(current_task, &node.fs())?;
908            upper.entry().node.remove_xattr(current_task, &upper.mount, name)
909        })
910    }
911
912    fn list_xattrs(
913        &self,
914        _node: &FsNode,
915        current_task: &CurrentTask,
916        max_size: usize,
917    ) -> Result<ValueOrSize<Vec<FsString>>, Errno> {
918        self.node.as_mounter(current_task, || {
919            let entry = self
920                .node
921                .upper
922                .get()
923                .or(self.node.lower.as_ref())
924                .expect("expect either lower or upper node");
925            entry.entry().node.list_xattrs(current_task, max_size)
926        })
927    }
928}
929struct OverlayDirectory {
930    node: Arc<OverlayNode>,
931    dir_entries: LockDepRwLock<DirEntries, OverlayFsDirEntriesLock>,
932}
933
934impl OverlayDirectory {
935    fn refresh_dir_entries(&self, current_task: &CurrentTask) -> Result<(), Errno> {
936        let mut entries = DirEntries::new();
937
938        let upper_is_opaque = self.node.upper_is_opaque.get().is_some();
939        let merge_with_lower = self.node.lower.is_some() && !upper_is_opaque;
940
941        // First enumerate entries in the upper dir. Then enumerate the lower dir and add only
942        // items that are not present in the upper.
943        let mut upper_set = BTreeSet::new();
944        if let Some(dir) = self.node.upper.get() {
945            for item in dir.read_dir_entries(current_task)?.drain(..) {
946                // Fill `upper_set` only if we will need it later.
947                if merge_with_lower {
948                    upper_set.insert(item.name.clone());
949                }
950                if !dir.is_whiteout_child(current_task, &item)? {
951                    entries.push(item);
952                }
953            }
954        }
955
956        if merge_with_lower {
957            if let Some(dir) = &self.node.lower {
958                for item in dir.read_dir_entries(current_task)?.drain(..) {
959                    if !upper_set.contains(&item.name)
960                        && !dir.is_whiteout_child(current_task, &item)?
961                    {
962                        entries.push(item);
963                    }
964                }
965            }
966        }
967
968        *self.dir_entries.write() = entries;
969
970        Ok(())
971    }
972}
973
974impl FileOps for OverlayDirectory {
975    fileops_impl_directory!();
976    fileops_impl_noop_sync!();
977
978    fn seek(
979        &self,
980        _file: &FileObject,
981        current_task: &CurrentTask,
982        current_offset: off_t,
983        target: SeekTarget,
984    ) -> Result<off_t, Errno> {
985        self.node
986            .as_mounter(current_task, || default_seek(current_offset, target, || error!(EINVAL)))
987    }
988
989    fn readdir(
990        &self,
991        file: &FileObject,
992        current_task: &CurrentTask,
993        sink: &mut dyn DirentSink,
994    ) -> Result<(), Errno> {
995        self.node.as_mounter(current_task, || {
996            if sink.offset() == 0 {
997                self.refresh_dir_entries(current_task)?;
998            }
999
1000            emit_dotdot(file, sink)?;
1001
1002            for item in self.dir_entries.read().iter().skip(sink.offset() as usize - 2) {
1003                sink.add(item.inode_num, sink.offset() + 1, item.entry_type, item.name.as_ref())?;
1004            }
1005
1006            Ok(())
1007        })
1008    }
1009}
1010
1011enum OverlayFileState {
1012    Lower(FileHandle),
1013    Upper(FileHandle),
1014}
1015
1016impl OverlayFileState {
1017    fn file(&self) -> &FileHandle {
1018        match self {
1019            Self::Lower(f) | Self::Upper(f) => f,
1020        }
1021    }
1022}
1023
1024struct OverlayFile {
1025    node: Arc<OverlayNode>,
1026    flags: OpenFlags,
1027    state: LockDepRwLock<OverlayFileState, OverlayFsStateLock>,
1028}
1029
1030impl FileOps for OverlayFile {
1031    fileops_impl_seekable!();
1032
1033    fn read(
1034        &self,
1035        _file: &FileObject,
1036        current_task: &CurrentTask,
1037        offset: usize,
1038        data: &mut dyn OutputBuffer,
1039    ) -> Result<usize, Errno> {
1040        self.node.as_mounter(current_task, || {
1041            let mut state = self.state.read();
1042
1043            // Check if the file was promoted to the upper FS. In that case we need to reopen it
1044            // from there.
1045            if let Some(upper) = self.node.upper.get() {
1046                if matches!(*state, OverlayFileState::Lower(_)) {
1047                    std::mem::drop(state);
1048
1049                    {
1050                        let mut write_state = self.state.write();
1051
1052                        // TODO(mariagl): don't hold write_state while calling open_anonymous.
1053                        // It may call back into read(), causing lock order inversion.
1054                        *write_state = OverlayFileState::Upper(
1055                            upper.entry().open_anonymous(current_task, self.flags)?,
1056                        );
1057                    }
1058                    state = self.state.read();
1059                }
1060            }
1061
1062            // TODO(mariagl): Drop state here
1063            let file = state.file();
1064            security::file_permission(current_task, &file, security::PermissionFlags::READ)?;
1065            file.ops().read(file, current_task, offset, data)
1066        })
1067    }
1068
1069    fn write(
1070        &self,
1071        _file: &FileObject,
1072        current_task: &CurrentTask,
1073        offset: usize,
1074        data: &mut dyn InputBuffer,
1075    ) -> Result<usize, Errno> {
1076        self.node.as_mounter(current_task, || {
1077            let state = self.state.read();
1078            let file = match &*state {
1079                OverlayFileState::Upper(f) => f.clone(),
1080
1081                // `write()` should be called only for files that were opened for write, and that
1082                // required the file to be promoted to the upper FS.
1083                OverlayFileState::Lower(_) => panic!("write() called for a lower FS file."),
1084            };
1085            std::mem::drop(state);
1086            security::file_permission(current_task, &file, security::PermissionFlags::WRITE)?;
1087            file.ops().write(&file, current_task, offset, data)
1088        })
1089    }
1090
1091    fn sync(&self, _file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
1092        self.node.as_mounter(current_task, || {
1093            let state = self.state.read();
1094            let file = state.file();
1095            file.ops().sync(file, current_task)
1096        })
1097    }
1098
1099    fn get_memory(
1100        &self,
1101        _file: &FileObject,
1102        current_task: &CurrentTask,
1103        length: Option<usize>,
1104        prot: starnix_core::mm::ProtectionFlags,
1105    ) -> Result<Arc<MemoryObject>, Errno> {
1106        self.node.as_mounter(current_task, || {
1107            let state = self.state.read();
1108            let file = state.file();
1109            // Not that the VMO returned here will not updated if the file is promoted to upper FS
1110            // later. This is consistent with OverlayFS behavior on Linux, see
1111            // https://docs.kernel.org/filesystems/overlayfs.html#non-standard-behavior .
1112            file.ops().get_memory(file, current_task, length, prot)
1113        })
1114    }
1115}
1116
1117pub fn new_overlay_fs(
1118    current_task: &CurrentTask,
1119    options: FileSystemOptions,
1120) -> Result<FileSystemHandle, Errno> {
1121    OverlayStack::new_fs(current_task, options)
1122}
1123
1124pub struct OverlayStack {
1125    // Keep references to the underlying file systems to ensure they outlive `overlayfs` since
1126    // they may be unmounted before overlayfs.
1127    #[allow(unused)]
1128    lower_fs: FileSystemHandle,
1129    upper_fs: FileSystemHandle,
1130
1131    work: ActiveEntry,
1132
1133    // Used when interacting with the `upper_fs`, `lower_fs` or `work` directories.
1134    mounter: Arc<Credentials>,
1135}
1136
1137impl OverlayStack {
1138    fn new_fs(
1139        current_task: &CurrentTask,
1140        options: FileSystemOptions,
1141    ) -> Result<FileSystemHandle, Errno> {
1142        match options.params.get("redirect_dir".as_bytes()) {
1143            None => (),
1144            Some(o) if o == "off" => (),
1145            Some(_) => {
1146                track_stub!(TODO("https://fxbug.dev/322874205"), "overlayfs redirect_dir");
1147                return error!(ENOTSUP);
1148            }
1149        }
1150
1151        let lower = resolve_dir_param(current_task, &options.params, "lowerdir".into())?;
1152        let upper = resolve_dir_param(current_task, &options.params, "upperdir".into())?;
1153        let work = resolve_dir_param(current_task, &options.params, "workdir".into())?;
1154
1155        let lower_fs = lower.entry().node.fs();
1156        let upper_fs = upper.entry().node.fs();
1157
1158        if upper_fs.fs_lockdep_type() == FsLockDepType::Recursive {
1159            // Recursive filesystems (like OverlayFS itself) are not supported as upper filesystems.
1160            return error!(EINVAL);
1161        }
1162
1163        if !Arc::ptr_eq(&upper_fs, &work.entry().node.fs()) {
1164            log_error!("overlayfs: upperdir and workdir must be on the same FS");
1165            return error!(EINVAL);
1166        }
1167
1168        let kernel = current_task.kernel();
1169        let mounter = current_task.current_creds().clone();
1170        let stack = Arc::new(OverlayStack { lower_fs, upper_fs, work, mounter });
1171        let root_node = OverlayNode::new(stack.clone(), Some(lower), Some(upper), None);
1172        let fs = FileSystem::new(kernel, CacheMode::Uncached, OverlayFs { stack }, options)?;
1173        let root_ino = fs.allocate_ino();
1174        fs.create_root(root_ino, OverlayNodeOps { node: root_node });
1175        Ok(fs)
1176    }
1177
1178    /// Given a filesystem, wraps it in a tmpfs-backed writable overlayfs.
1179    pub fn wrap_fs_in_writable_layer(
1180        kernel: &Kernel,
1181        rootfs: FileSystemHandle,
1182    ) -> Result<FileSystemHandle, Errno> {
1183        let lower = ActiveEntry { entry: rootfs.root().clone(), mount: MountInfo::detached() };
1184
1185        // Create upper and work directories in an invisible tmpfs.
1186        let invisible_tmp = TmpFs::new_fs(kernel);
1187
1188        let create_directory = |fs: &FileSystemHandle| {
1189            let ino = fs.allocate_ino();
1190            let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
1191            let node = fs.create_detached_node(ino, TmpFsDirectory::new(), info);
1192            let dir_entry = DirEntry::new(node, None, FsString::default());
1193
1194            // TODO: https://fxbug.dev/455771186 - Revise FsNode initialization to better ensure
1195            // that all the things are appropriately labeled.
1196            security::fs_node_init_with_dentry_deferred(kernel, &dir_entry);
1197
1198            dir_entry
1199        };
1200
1201        let upper =
1202            ActiveEntry { entry: create_directory(&invisible_tmp), mount: MountInfo::detached() };
1203        let work =
1204            ActiveEntry { entry: create_directory(&invisible_tmp), mount: MountInfo::detached() };
1205
1206        let lower_fs = rootfs;
1207        let upper_fs = invisible_tmp;
1208
1209        let mounter = Credentials::root();
1210        let stack = Arc::new(OverlayStack { lower_fs, upper_fs, work, mounter });
1211        let root_node = OverlayNode::new(stack.clone(), Some(lower), Some(upper), None);
1212        let fs = FileSystem::new(
1213            kernel,
1214            CacheMode::Uncached,
1215            OverlayFs { stack },
1216            FileSystemOptions::default(),
1217        )?;
1218        let root_ino = fs.allocate_ino();
1219        fs.create_root(root_ino, OverlayNodeOps { node: root_node });
1220        Ok(fs)
1221    }
1222
1223    // Helper used to create new entry called `name` in `target_dir` in the upper FS.
1224    // 1. Calls `try_create` to create a new entry in `work`. It is called repeateadly with a
1225    //    new name until it returns any result other than `EEXIST`.
1226    // 2. `do_init` is called to initilize the contents and the attributes of the new entry, etc.
1227    // 3. The new entry is moved to `target_dir`. If there is an existing entry called `name` in
1228    //    `target_dir` then it's replaced with the new entry.
1229    // The temp file is cleared from the work dir if either of the last two steps fails.
1230    fn create_upper_entry<FCreate, FInit>(
1231        &self,
1232        current_task: &CurrentTask,
1233        target_dir: &ActiveEntry,
1234        name: &FsStr,
1235        try_create: FCreate,
1236        do_init: FInit,
1237    ) -> Result<ActiveEntry, Errno>
1238    where
1239        FCreate: Fn(&ActiveEntry, &FsStr) -> Result<ActiveEntry, Errno>,
1240        FInit: FnOnce(&ActiveEntry) -> Result<(), Errno>,
1241    {
1242        let mut rng = rand::rng();
1243        let (temp_name, entry) = loop {
1244            let x: u64 = rng.random();
1245            let temp_name = FsString::from(format!("tmp{:x}", x));
1246            match try_create(&self.work, temp_name.as_ref()) {
1247                Err(err) if err.code == EEXIST => continue,
1248                Err(err) => return Err(err),
1249                Ok(entry) => break (temp_name, entry),
1250            }
1251        };
1252
1253        do_init(&entry)
1254            .and_then(|()| {
1255                DirEntry::rename(
1256                    current_task,
1257                    self.work.entry(),
1258                    self.work.mount(),
1259                    temp_name.as_ref(),
1260                    target_dir.entry(),
1261                    target_dir.mount(),
1262                    name,
1263                    RenameFlags::REPLACE_ANY,
1264                )
1265            })
1266            .map_err(|e| {
1267                // Remove the temp entry in case of a failure.
1268                self.work
1269                    .entry()
1270                    .unlink(
1271                        current_task,
1272                        self.work.mount(),
1273                        temp_name.as_ref(),
1274                        UnlinkKind::NonDirectory,
1275                        DirectoryMode::AllowAny,
1276                    )
1277                    .unwrap_or_else(|e| {
1278                        log_error!("Failed to cleanup work dir after an error: {}", e)
1279                    });
1280                e
1281            })?;
1282
1283        Ok(entry)
1284    }
1285}
1286
1287struct OverlayFs {
1288    stack: Arc<OverlayStack>,
1289}
1290
1291impl FileSystemOps for OverlayFs {
1292    fn fs_lockdep_type(&self) -> FsLockDepType {
1293        FsLockDepType::Recursive
1294    }
1295
1296    fn statfs(&self, _fs: &FileSystem, current_task: &CurrentTask) -> Result<statfs, Errno> {
1297        current_task
1298            .override_creds(self.stack.mounter.clone(), || self.stack.upper_fs.statfs(current_task))
1299    }
1300
1301    fn name(&self) -> &'static FsStr {
1302        "overlay".into()
1303    }
1304
1305    fn rename(
1306        &self,
1307        _fs: &FileSystem,
1308        current_task: &CurrentTask,
1309        context: &mut RenameContext<'_>,
1310        old_name: &FsStr,
1311        new_name: &FsStr,
1312    ) -> Result<(), Errno> {
1313        let old_parent = &context.old_parent().node;
1314        let new_parent = &context.new_parent().node;
1315        let renamed = &context.renamed.node;
1316        current_task.override_creds(self.stack.mounter.clone(), || {
1317            let renamed_overlay = OverlayNode::from_fs_node(renamed)?;
1318            if renamed_overlay.has_lower() && renamed_overlay.main_entry().entry().node.is_dir() {
1319                // Return EXDEV for directory renames. Potentially they may be handled with the
1320                // `redirect_dir` feature, but it's not implemented here yet.
1321                // See https://docs.kernel.org/filesystems/overlayfs.html#renaming-directories
1322                return error!(EXDEV);
1323            }
1324            renamed_overlay.ensure_upper(current_task, &renamed.fs())?;
1325
1326            let old_parent_overlay = OverlayNode::from_fs_node(old_parent)?;
1327            let old_parent_upper = old_parent_overlay.ensure_upper(current_task, &renamed.fs())?;
1328
1329            let new_parent_overlay = OverlayNode::from_fs_node(new_parent)?;
1330            let new_parent_upper = new_parent_overlay.ensure_upper(current_task, &renamed.fs())?;
1331
1332            let need_whiteout = old_parent_overlay.lower_entry_exists(current_task, old_name)?;
1333
1334            DirEntry::rename(
1335                current_task,
1336                old_parent_upper.entry(),
1337                old_parent_upper.mount(),
1338                old_name,
1339                new_parent_upper.entry(),
1340                new_parent_upper.mount(),
1341                new_name,
1342                RenameFlags::REPLACE_ANY,
1343            )?;
1344
1345            // If the old node existed in lower FS, then override it in the upper FS with a
1346            // whiteout.
1347            if need_whiteout {
1348                match old_parent_upper.create_whiteout(current_task, old_name) {
1349                    Err(e) => log_warn!("overlayfs: failed to create whiteout for {old_name}: {e}"),
1350                    Ok(_) => (),
1351                }
1352            }
1353
1354            Ok(())
1355        })
1356    }
1357
1358    fn unmount(&self) {}
1359
1360    fn sub_filesystems(&self) -> Vec<Arc<FileSystem>> {
1361        vec![self.stack.lower_fs.clone(), self.stack.upper_fs.clone()]
1362    }
1363}
1364
1365/// Helper used to resolve directories passed in mount options. The directory is resolved in the
1366/// namespace of the calling process, but only `DirEntry` is returned (detached from the
1367/// namespace). The corresponding file systems may be unmounted before overlayfs that uses them.
1368fn resolve_dir_param(
1369    current_task: &CurrentTask,
1370    params: &MountParams,
1371    name: &FsStr,
1372) -> Result<ActiveEntry, Errno> {
1373    let path = params.get(&**name).ok_or_else(|| {
1374        log_error!("overlayfs: {name} was not specified");
1375        errno!(EINVAL)
1376    })?;
1377
1378    current_task
1379        .open_file(path.as_ref(), OpenFlags::RDONLY | OpenFlags::DIRECTORY)
1380        .map(|f| ActiveEntry { entry: f.name.entry.clone(), mount: f.name.mount.clone() })
1381        .map_err(|e| {
1382            log_error!("overlayfs: Failed to lookup {path}: {}", e);
1383            e
1384        })
1385}
1386
1387/// Copies file content from one file to another.
1388fn copy_file_content(
1389    current_task: &CurrentTask,
1390    from: &ActiveEntry,
1391    to: &ActiveEntry,
1392) -> Result<(), Errno> {
1393    let from_file = from.entry().open_anonymous(current_task, OpenFlags::RDONLY)?;
1394    let to_file = to.entry().open_anonymous(current_task, OpenFlags::WRONLY)?;
1395
1396    security::fs_node_permission(
1397        current_task,
1398        from_file.node().as_ref(),
1399        security::PermissionFlags::READ,
1400        (&**from_file).into(),
1401    )?;
1402    security::fs_node_permission(
1403        current_task,
1404        to_file.node().as_ref(),
1405        security::PermissionFlags::WRITE,
1406        (&**to_file).into(),
1407    )?;
1408
1409    const BUFFER_SIZE: usize = 4096;
1410
1411    let mut read_offset = 0;
1412    let mut write_offset = 0;
1413    loop {
1414        // TODO(sergeyu): Reuse buffer between iterations.
1415
1416        let mut output_buffer = VecOutputBuffer::new(BUFFER_SIZE);
1417        let bytes_read =
1418            from_file.ops().read(&from_file, current_task, read_offset, &mut output_buffer)?;
1419        if bytes_read == 0 {
1420            break;
1421        }
1422        read_offset += bytes_read;
1423
1424        let buffer: Vec<u8> = output_buffer.into();
1425        let mut input_buffer = VecInputBuffer::from(buffer);
1426        while input_buffer.available() > 0 {
1427            write_offset +=
1428                to_file.ops().write(&to_file, current_task, write_offset, &mut input_buffer)?;
1429        }
1430    }
1431
1432    to_file.ops().data_sync(&to_file, current_task)?;
1433
1434    Ok(())
1435}