1#![recursion_limit = "512"]
6
7use fuchsia_rcu::RcuReadScope;
8use once_cell::sync::OnceCell;
9use rand::RngExt as _;
10use starnix_core::fs::tmpfs::TmpFs;
11use starnix_core::mm::memory::MemoryObject;
12use starnix_core::security::{self, PermissionFlags};
13use starnix_core::task::CurrentTask;
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_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
40const OPAQUE_DIR_XATTR: &str = "trusted.overlay.opaque";
43const OPAQUE_DIR_XATTR_VALUE: &[u8] = b"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#[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 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,
134 XattrOp::Set,
135 )
136 }
137
138 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 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 fn is_whiteout(&self) -> bool {
173 let info = self.entry().node.info();
174 info.mode.is_chr() && info.rdev == DeviceId::NONE
175 }
176
177 fn is_whiteout_child(
181 &self,
182 current_task: &CurrentTask,
183 info: &DirEntryInfo,
184 ) -> Result<bool, Errno> {
185 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 upper: OnceCell<ActiveEntry>,
210 lower: Option<ActiveEntry>,
211
212 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 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 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 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 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 fn has_lower(&self) -> bool {
336 self.lower.is_some()
337 }
338
339 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 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 !existing.is_whiteout() {
375 return error!(EEXIST);
376 }
377 }
378
379 Err(e) if e.code == ENOENT => {
380 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 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 dir.set_opaque_xattr(current_task)?;
429 let _ = self.upper_is_opaque.set(());
430
431 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 let mut access = access;
492 if access.contains(PermissionFlags::WRITE) && !lower_node.info().mode.is_special() {
493 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 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 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 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 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 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 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 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 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 sync(&self, _node: &FsNode, current_task: &CurrentTask) -> Result<(), Errno> {
871 self.node.as_mounter(current_task, || match self.node.upper.get() {
872 Some(upper) => upper.entry().node.ops().sync(&upper.entry().node, current_task),
873 None => Ok(()),
874 })
875 }
876
877 fn get_xattr(
878 &self,
879 _node: &FsNode,
880 current_task: &CurrentTask,
881 name: &FsStr,
882 max_size: usize,
883 ) -> Result<ValueOrSize<Box<[u8]>>, Errno> {
884 let entry = self
885 .node
886 .upper
887 .get()
888 .or(self.node.lower.as_ref())
889 .expect("expect either lower or upper node");
890 self.node.as_mounter(current_task, || {
891 entry.entry().node.get_xattr(current_task, &entry.mount, name, max_size)
892 })
893 }
894
895 fn set_xattr(
896 &self,
897 node: &FsNode,
898 current_task: &CurrentTask,
899 name: &FsStr,
900 value: &[u8],
901 op: XattrOp,
902 ) -> Result<(), Errno> {
903 self.node.as_mounter(current_task, || {
904 let upper = self.node.ensure_upper(current_task, &node.fs())?;
905 upper.entry().node.set_xattr(current_task, &upper.mount, name, value, op)
906 })
907 }
908
909 fn remove_xattr(
910 &self,
911 node: &FsNode,
912 current_task: &CurrentTask,
913 name: &FsStr,
914 ) -> Result<(), Errno> {
915 self.node.as_mounter(current_task, || {
916 let upper = self.node.ensure_upper(current_task, &node.fs())?;
917 upper.entry().node.remove_xattr(current_task, &upper.mount, name)
918 })
919 }
920
921 fn list_xattrs(
922 &self,
923 _node: &FsNode,
924 current_task: &CurrentTask,
925 max_size: usize,
926 ) -> Result<ValueOrSize<Vec<FsString>>, Errno> {
927 self.node.as_mounter(current_task, || {
928 let entry = self
929 .node
930 .upper
931 .get()
932 .or(self.node.lower.as_ref())
933 .expect("expect either lower or upper node");
934 entry.entry().node.list_xattrs(current_task, max_size)
935 })
936 }
937}
938struct OverlayDirectory {
939 node: Arc<OverlayNode>,
940 dir_entries: LockDepRwLock<DirEntries, OverlayFsDirEntriesLock>,
941}
942
943impl OverlayDirectory {
944 fn refresh_dir_entries(&self, current_task: &CurrentTask) -> Result<(), Errno> {
945 let mut entries = DirEntries::new();
946
947 let upper_is_opaque = self.node.upper_is_opaque.get().is_some();
948 let merge_with_lower = self.node.lower.is_some() && !upper_is_opaque;
949
950 let mut upper_set = BTreeSet::new();
953 if let Some(dir) = self.node.upper.get() {
954 for item in dir.read_dir_entries(current_task)?.drain(..) {
955 if merge_with_lower {
957 upper_set.insert(item.name.clone());
958 }
959 if !dir.is_whiteout_child(current_task, &item)? {
960 entries.push(item);
961 }
962 }
963 }
964
965 if merge_with_lower {
966 if let Some(dir) = &self.node.lower {
967 for item in dir.read_dir_entries(current_task)?.drain(..) {
968 if !upper_set.contains(&item.name)
969 && !dir.is_whiteout_child(current_task, &item)?
970 {
971 entries.push(item);
972 }
973 }
974 }
975 }
976
977 *self.dir_entries.write() = entries;
978
979 Ok(())
980 }
981}
982
983impl FileOps for OverlayDirectory {
984 fileops_impl_directory!();
985
986 fn seek(
987 &self,
988 _file: &FileObject,
989 current_task: &CurrentTask,
990 current_offset: off_t,
991 target: SeekTarget,
992 ) -> Result<off_t, Errno> {
993 self.node
994 .as_mounter(current_task, || default_seek(current_offset, target, || error!(EINVAL)))
995 }
996
997 fn readdir(
998 &self,
999 file: &FileObject,
1000 current_task: &CurrentTask,
1001 sink: &mut dyn DirentSink,
1002 ) -> Result<(), Errno> {
1003 self.node.as_mounter(current_task, || {
1004 if sink.offset() == 0 {
1005 self.refresh_dir_entries(current_task)?;
1006 }
1007
1008 emit_dotdot(file, sink)?;
1009
1010 for item in self.dir_entries.read().iter().skip(sink.offset() as usize - 2) {
1011 sink.add(item.inode_num, sink.offset() + 1, item.entry_type, item.name.as_ref())?;
1012 }
1013
1014 Ok(())
1015 })
1016 }
1017}
1018
1019enum OverlayFileState {
1020 Lower(FileHandle),
1021 Upper(FileHandle),
1022}
1023
1024impl OverlayFileState {
1025 fn file(&self) -> &FileHandle {
1026 match self {
1027 Self::Lower(f) | Self::Upper(f) => f,
1028 }
1029 }
1030}
1031
1032struct OverlayFile {
1033 node: Arc<OverlayNode>,
1034 flags: OpenFlags,
1035 state: LockDepRwLock<OverlayFileState, OverlayFsStateLock>,
1036}
1037
1038impl FileOps for OverlayFile {
1039 fileops_impl_seekable!();
1040
1041 fn read(
1042 &self,
1043 _file: &FileObject,
1044 current_task: &CurrentTask,
1045 offset: usize,
1046 data: &mut dyn OutputBuffer,
1047 ) -> Result<usize, Errno> {
1048 self.node.as_mounter(current_task, || {
1049 let mut state = self.state.read();
1050
1051 if let Some(upper) = self.node.upper.get() {
1054 if matches!(*state, OverlayFileState::Lower(_)) {
1055 std::mem::drop(state);
1056
1057 {
1058 let mut write_state = self.state.write();
1059
1060 *write_state = OverlayFileState::Upper(
1063 upper.entry().open_anonymous(current_task, self.flags)?,
1064 );
1065 }
1066 state = self.state.read();
1067 }
1068 }
1069
1070 let file = state.file();
1072 security::file_permission(current_task, &file, security::PermissionFlags::READ)?;
1073 file.ops().read(file, current_task, offset, data)
1074 })
1075 }
1076
1077 fn write(
1078 &self,
1079 _file: &FileObject,
1080 current_task: &CurrentTask,
1081 offset: usize,
1082 data: &mut dyn InputBuffer,
1083 ) -> Result<usize, Errno> {
1084 self.node.as_mounter(current_task, || {
1085 let state = self.state.read();
1086 let file = match &*state {
1087 OverlayFileState::Upper(f) => f.clone(),
1088
1089 OverlayFileState::Lower(_) => panic!("write() called for a lower FS file."),
1092 };
1093 std::mem::drop(state);
1094 security::file_permission(current_task, &file, security::PermissionFlags::WRITE)?;
1095 file.ops().write(&file, current_task, offset, data)
1096 })
1097 }
1098
1099 fn sync(&self, _file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
1100 self.node.as_mounter(current_task, || {
1101 let state = self.state.read();
1102 let file = state.file();
1103 file.ops().sync(file, current_task)
1104 })
1105 }
1106
1107 fn flush(&self, _file: &FileObject, current_task: &CurrentTask) {
1108 self.node.as_mounter(current_task, || {
1109 let state = self.state.read();
1110 let file = state.file();
1111 file.ops().flush(file, current_task)
1112 })
1113 }
1114
1115 fn get_memory(
1116 &self,
1117 _file: &FileObject,
1118 current_task: &CurrentTask,
1119 length: Option<usize>,
1120 prot: starnix_core::mm::ProtectionFlags,
1121 ) -> Result<Arc<MemoryObject>, Errno> {
1122 self.node.as_mounter(current_task, || {
1123 let state = self.state.read();
1124 let file = state.file();
1125 file.ops().get_memory(file, current_task, length, prot)
1129 })
1130 }
1131}
1132
1133pub fn new_overlay_fs(
1134 current_task: &CurrentTask,
1135 options: FileSystemOptions,
1136) -> Result<FileSystemHandle, Errno> {
1137 OverlayStack::new_fs(current_task, options)
1138}
1139
1140pub struct OverlayStack {
1141 #[allow(unused)]
1144 lower_fs: FileSystemHandle,
1145 upper_fs: FileSystemHandle,
1146
1147 work: ActiveEntry,
1148
1149 mounter: Arc<Credentials>,
1151}
1152
1153impl OverlayStack {
1154 fn new_fs(
1155 current_task: &CurrentTask,
1156 options: FileSystemOptions,
1157 ) -> Result<FileSystemHandle, Errno> {
1158 match options.params.get("redirect_dir".as_bytes()) {
1159 None => (),
1160 Some(o) if o == "off" => (),
1161 Some(_) => {
1162 track_stub!(TODO("https://fxbug.dev/322874205"), "overlayfs redirect_dir");
1163 return error!(ENOTSUP);
1164 }
1165 }
1166
1167 let lower = resolve_dir_param(current_task, &options.params, "lowerdir".into())?;
1168 let upper = resolve_dir_param(current_task, &options.params, "upperdir".into())?;
1169 let work = resolve_dir_param(current_task, &options.params, "workdir".into())?;
1170
1171 let lower_fs = lower.entry().node.fs();
1172 let upper_fs = upper.entry().node.fs();
1173
1174 if upper_fs.fs_lockdep_type() == FsLockDepType::Recursive {
1175 return error!(EINVAL);
1177 }
1178
1179 if !Arc::ptr_eq(&upper_fs, &work.entry().node.fs()) {
1180 log_error!("overlayfs: upperdir and workdir must be on the same FS");
1181 return error!(EINVAL);
1182 }
1183
1184 let kernel = current_task.kernel();
1185 let mounter = current_task.current_creds().clone();
1186 let stack = Arc::new(OverlayStack { lower_fs, upper_fs, work, mounter });
1187 let root_node = OverlayNode::new(stack.clone(), Some(lower), Some(upper), None);
1188 let root_info = root_node.main_entry().entry().node.info().clone();
1189 let fs = FileSystem::new(kernel, CacheMode::Uncached, OverlayFs { stack }, options)?;
1190 let root_ino = fs.allocate_ino();
1191 fs.create_root_with_info(root_ino, OverlayNodeOps { node: root_node }, root_info);
1192 Ok(fs)
1193 }
1194
1195 pub fn wrap_fs_in_writable_layer(
1197 current_task: &CurrentTask,
1198 rootfs: FileSystemHandle,
1199 ) -> Result<FileSystemHandle, Errno> {
1200 let kernel = current_task.kernel();
1201 let lower = ActiveEntry { entry: rootfs.root().clone(), mount: MountInfo::detached() };
1202 let lower_info = lower.entry().node.fetch_and_refresh_info(current_task)?.clone();
1203
1204 let invisible_tmp = TmpFs::new_fs(kernel);
1206 let create_dir =
1207 |name: &FsStr, mode: FileMode, cred: FsCred| -> Result<ActiveEntry, Errno> {
1208 let create_dir_node = |dir: &FsNodeHandle, mount: &MountInfo, name: &FsStr| {
1209 dir.create_node(current_task, mount, name, mode, DeviceId::NONE, cred)
1210 };
1211 let mount = MountInfo::detached();
1212 let root = invisible_tmp.root();
1213 let entry = root.create_entry(current_task, &mount, name, create_dir_node)?;
1214 Ok(ActiveEntry { entry, mount })
1215 };
1216 let upper = create_dir("upper".into(), lower_info.mode, lower_info.cred())?;
1217 let work = create_dir("work".into(), mode!(IFDIR, 0o700), FsCred::root())?;
1218
1219 let lower_fs = rootfs;
1220 let upper_fs = invisible_tmp;
1221
1222 let mounter = Credentials::root();
1223 let stack = Arc::new(OverlayStack { lower_fs, upper_fs, work, mounter });
1224 let root_node = OverlayNode::new(stack.clone(), Some(lower), Some(upper), None);
1225 let root_info = root_node.main_entry().entry().node.info().clone();
1226 let fs = FileSystem::new(
1227 kernel,
1228 CacheMode::Uncached,
1229 OverlayFs { stack },
1230 FileSystemOptions::default(),
1231 )?;
1232 let root_ino = fs.allocate_ino();
1233 fs.create_root_with_info(root_ino, OverlayNodeOps { node: root_node }, root_info);
1234 Ok(fs)
1235 }
1236
1237 fn create_upper_entry<FCreate, FInit>(
1245 &self,
1246 current_task: &CurrentTask,
1247 target_dir: &ActiveEntry,
1248 name: &FsStr,
1249 try_create: FCreate,
1250 do_init: FInit,
1251 ) -> Result<ActiveEntry, Errno>
1252 where
1253 FCreate: Fn(&ActiveEntry, &FsStr) -> Result<ActiveEntry, Errno>,
1254 FInit: FnOnce(&ActiveEntry) -> Result<(), Errno>,
1255 {
1256 let mut rng = rand::rng();
1257 let (temp_name, entry) = loop {
1258 let x: u64 = rng.random();
1259 let temp_name = FsString::from(format!("tmp{:x}", x));
1260 match try_create(&self.work, temp_name.as_ref()) {
1261 Err(err) if err.code == EEXIST => continue,
1262 Err(err) => return Err(err),
1263 Ok(entry) => break (temp_name, entry),
1264 }
1265 };
1266
1267 do_init(&entry)
1268 .and_then(|()| {
1269 DirEntry::rename(
1270 current_task,
1271 self.work.entry(),
1272 self.work.mount(),
1273 temp_name.as_ref(),
1274 target_dir.entry(),
1275 target_dir.mount(),
1276 name,
1277 RenameFlags::REPLACE_ANY,
1278 )
1279 })
1280 .map_err(|e| {
1281 self.work
1283 .entry()
1284 .unlink(
1285 current_task,
1286 self.work.mount(),
1287 temp_name.as_ref(),
1288 UnlinkKind::NonDirectory,
1289 DirectoryMode::AllowAny,
1290 )
1291 .unwrap_or_else(|e| {
1292 log_error!("Failed to cleanup work dir after an error: {}", e)
1293 });
1294 e
1295 })?;
1296
1297 Ok(entry)
1298 }
1299}
1300
1301struct OverlayFs {
1302 stack: Arc<OverlayStack>,
1303}
1304
1305impl FileSystemOps for OverlayFs {
1306 fn fs_lockdep_type(&self) -> FsLockDepType {
1307 FsLockDepType::Recursive
1308 }
1309
1310 fn statfs(&self, _fs: &FileSystem, current_task: &CurrentTask) -> Result<statfs, Errno> {
1311 current_task
1312 .override_creds(self.stack.mounter.clone(), || self.stack.upper_fs.statfs(current_task))
1313 }
1314
1315 fn sync(&self, _fs: &FileSystem, current_task: &CurrentTask) -> Result<(), Errno> {
1316 current_task
1317 .override_creds(self.stack.mounter.clone(), || self.stack.upper_fs.sync(current_task))
1318 }
1319
1320 fn name(&self) -> &'static FsStr {
1321 "overlay".into()
1322 }
1323
1324 fn rename(
1325 &self,
1326 _fs: &FileSystem,
1327 current_task: &CurrentTask,
1328 context: &mut RenameContext<'_>,
1329 old_name: &FsStr,
1330 new_name: &FsStr,
1331 ) -> Result<(), Errno> {
1332 let old_parent = &context.old_parent().node;
1333 let new_parent = &context.new_parent().node;
1334 let renamed = &context.renamed.node;
1335 current_task.override_creds(self.stack.mounter.clone(), || {
1336 let renamed_overlay = OverlayNode::from_fs_node(renamed)?;
1337 if renamed_overlay.has_lower() && renamed_overlay.main_entry().entry().node.is_dir() {
1338 return error!(EXDEV);
1342 }
1343 renamed_overlay.ensure_upper(current_task, &renamed.fs())?;
1344
1345 let old_parent_overlay = OverlayNode::from_fs_node(old_parent)?;
1346 let old_parent_upper = old_parent_overlay.ensure_upper(current_task, &renamed.fs())?;
1347
1348 let new_parent_overlay = OverlayNode::from_fs_node(new_parent)?;
1349 let new_parent_upper = new_parent_overlay.ensure_upper(current_task, &renamed.fs())?;
1350
1351 let need_whiteout = old_parent_overlay.lower_entry_exists(current_task, old_name)?;
1352
1353 DirEntry::rename(
1354 current_task,
1355 old_parent_upper.entry(),
1356 old_parent_upper.mount(),
1357 old_name,
1358 new_parent_upper.entry(),
1359 new_parent_upper.mount(),
1360 new_name,
1361 RenameFlags::REPLACE_ANY,
1362 )?;
1363
1364 if need_whiteout {
1367 match old_parent_upper.create_whiteout(current_task, old_name) {
1368 Err(e) => log_warn!("overlayfs: failed to create whiteout for {old_name}: {e}"),
1369 Ok(_) => (),
1370 }
1371 }
1372
1373 Ok(())
1374 })
1375 }
1376
1377 fn unmount(&self) {}
1378
1379 fn sub_filesystems(&self) -> Vec<Arc<FileSystem>> {
1380 vec![self.stack.lower_fs.clone(), self.stack.upper_fs.clone()]
1381 }
1382}
1383
1384fn resolve_dir_param(
1388 current_task: &CurrentTask,
1389 params: &MountParams,
1390 name: &FsStr,
1391) -> Result<ActiveEntry, Errno> {
1392 let path = params.get(&**name).ok_or_else(|| {
1393 log_error!("overlayfs: {name} was not specified");
1394 errno!(EINVAL)
1395 })?;
1396
1397 current_task
1398 .open_file(path.as_ref(), OpenFlags::RDONLY | OpenFlags::DIRECTORY)
1399 .map(|f| ActiveEntry { entry: f.name.entry.clone(), mount: f.name.mount.clone() })
1400 .map_err(|e| {
1401 log_error!("overlayfs: Failed to lookup {path}: {}", e);
1402 e
1403 })
1404}
1405
1406fn copy_file_content(
1408 current_task: &CurrentTask,
1409 from: &ActiveEntry,
1410 to: &ActiveEntry,
1411) -> Result<(), Errno> {
1412 let from_file = from.entry().open_anonymous(current_task, OpenFlags::RDONLY)?;
1413 let to_file = to.entry().open_anonymous(current_task, OpenFlags::WRONLY)?;
1414
1415 security::fs_node_permission(
1416 current_task,
1417 from_file.node().as_ref(),
1418 security::PermissionFlags::READ,
1419 (&**from_file).into(),
1420 )?;
1421 security::fs_node_permission(
1422 current_task,
1423 to_file.node().as_ref(),
1424 security::PermissionFlags::WRITE,
1425 (&**to_file).into(),
1426 )?;
1427
1428 const BUFFER_SIZE: usize = 4096;
1429
1430 let mut read_offset = 0;
1431 let mut write_offset = 0;
1432 loop {
1433 let mut output_buffer = VecOutputBuffer::new(BUFFER_SIZE);
1436 let bytes_read =
1437 from_file.ops().read(&from_file, current_task, read_offset, &mut output_buffer)?;
1438 if bytes_read == 0 {
1439 break;
1440 }
1441 read_offset += bytes_read;
1442
1443 let buffer: Vec<u8> = output_buffer.into();
1444 let mut input_buffer = VecInputBuffer::from(buffer);
1445 while input_buffer.available() > 0 {
1446 write_offset +=
1447 to_file.ops().write(&to_file, current_task, write_offset, &mut input_buffer)?;
1448 }
1449 }
1450
1451 to_file.ops().data_sync(&to_file, current_task)?;
1452
1453 Ok(())
1454}
1455
1456#[cfg(test)]
1457mod test {
1458 use super::*;
1459 use fidl_fuchsia_io as fio;
1460 use fxfs_testing::{Hooks, TestFixture, TestFixtureOptions};
1461 use starnix_core::fs::fuchsia::RemoteFs;
1462 use starnix_core::testing::spawn_kernel_and_run;
1463 use starnix_core::vfs::{FdFlags, LookupContext, Namespace, NamespaceNode, WhatToMount};
1464 use starnix_uapi::mount_flags::MountpointFlags;
1465 use std::sync::atomic::{AtomicUsize, Ordering};
1466 use storage_device::DeviceHolder;
1467 use storage_device::fake_device::FakeDevice;
1468
1469 async fn new_fxfs() -> (TestFixture, zx::Channel, Arc<AtomicUsize>, Hooks<'static>) {
1471 let commits = Arc::new(AtomicUsize::new(0));
1472 let commits_clone = commits.clone();
1473 let (mut hooks, fs_hooks) = Hooks::new();
1474 hooks.set_pre_commit(move |_transaction| {
1475 commits_clone.fetch_add(1, Ordering::SeqCst);
1476 Ok(())
1477 });
1478 let fixture = TestFixture::open(
1479 DeviceHolder::new(FakeDevice::new(16384, 512)),
1480 TestFixtureOptions { hooks: Some(fs_hooks), ..Default::default() },
1481 )
1482 .await;
1483 let (server, client) = zx::Channel::create();
1484 fixture.root().clone(server.into()).expect("clone failed");
1485 (fixture, client, commits, hooks)
1486 }
1487
1488 fn new_overlay(current_task: &CurrentTask, remote: zx::Channel) -> FileSystemHandle {
1490 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
1491 let remote = RemoteFs::new_fs(current_task.kernel(), remote, Default::default(), rights)
1492 .expect("new_fs failed");
1493 remote.root().create_dir_for_testing(current_task, "upper".into()).expect("mkdir failed");
1494 remote.root().create_dir_for_testing(current_task, "work".into()).expect("mkdir failed");
1495 let root = current_task.fs().root();
1496 root.entry.create_dir_for_testing(current_task, "lower".into()).expect("mkdir failed");
1497 root.entry.create_dir_for_testing(current_task, "remote".into()).expect("mkdir failed");
1498 root.lookup_child(current_task, &mut LookupContext::default(), "remote".into())
1499 .expect("lookup failed")
1500 .mount(WhatToMount::Fs(remote), MountpointFlags::empty())
1501 .expect("mount failed");
1502 let params = "lowerdir=/lower,upperdir=/remote/upper,workdir=/remote/work";
1503 let params = MountParams::parse(params.into()).expect("parse failed");
1504 new_overlay_fs(current_task, FileSystemOptions { params, ..Default::default() })
1505 .expect("new_overlay_fs failed")
1506 }
1507
1508 fn write_new_file(current_task: &CurrentTask, dir: &NamespaceNode) -> FileHandle {
1510 let file = dir
1511 .create_node(current_task, "file".into(), mode!(IFREG, 0o666), DeviceId::NONE)
1512 .expect("create_node failed")
1513 .open(current_task, OpenFlags::RDWR)
1514 .expect("open failed");
1515 file.write(current_task, &mut VecInputBuffer::new(b"hello")).expect("write failed");
1516 file
1517 }
1518
1519 #[::fuchsia::test]
1520 async fn test_close_flushes_upper_file() {
1521 let (fixture, remote, commits, _hooks) = new_fxfs().await;
1522 spawn_kernel_and_run(async move |current_task| {
1523 let root = Namespace::new(new_overlay(current_task, remote)).root();
1524 let file = write_new_file(current_task, &root);
1525 let fd = current_task.add_file(file, FdFlags::empty()).expect("add_file failed");
1526
1527 let commits_before = commits.load(Ordering::SeqCst);
1528 current_task.files().close(fd).expect("close failed");
1529 current_task.trigger_delayed_releaser();
1530 assert!(commits.load(Ordering::SeqCst) > commits_before);
1531 })
1532 .await;
1533 fixture.close().await;
1534 }
1535
1536 #[::fuchsia::test]
1538 async fn test_dir_fsync_syncs_upper_dir() {
1539 let (fixture, remote, commits, _hooks) = new_fxfs().await;
1540 spawn_kernel_and_run(async move |current_task| {
1541 let root = Namespace::new(new_overlay(current_task, remote)).root();
1542 let _file = write_new_file(current_task, &root);
1543 let dir = root.open(current_task, OpenFlags::RDONLY).expect("open failed");
1544
1545 let commits_before = commits.load(Ordering::SeqCst);
1546 dir.sync(current_task).expect("fsync failed");
1547 assert!(commits.load(Ordering::SeqCst) > commits_before);
1548 })
1549 .await;
1550 fixture.close().await;
1551 }
1552
1553 #[::fuchsia::test]
1554 async fn test_syncfs_syncs_upper_fs() {
1555 let (fixture, remote, commits, _hooks) = new_fxfs().await;
1556 spawn_kernel_and_run(async move |current_task| {
1557 let overlay = new_overlay(current_task, remote);
1558 let _file = write_new_file(current_task, &Namespace::new(overlay.clone()).root());
1559
1560 let commits_before = commits.load(Ordering::SeqCst);
1561 overlay.sync(current_task).expect("syncfs failed");
1562 assert!(commits.load(Ordering::SeqCst) > commits_before);
1563 })
1564 .await;
1565 fixture.close().await;
1566 }
1567
1568 #[::fuchsia::test]
1569 async fn test_wrap_fs_in_writable_layer_preserves_root_mode() {
1570 spawn_kernel_and_run(async move |current_task| {
1571 let kernel = current_task.kernel();
1572 let lower = TmpFs::new_fs_with_options(
1573 kernel,
1574 FileSystemOptions {
1575 params: MountParams::parse(b"mode=0755,uid=42,gid=84".into())
1576 .expect("parse params"),
1577 ..Default::default()
1578 },
1579 )
1580 .expect("new_fs_with_options");
1581 let overlay = OverlayStack::wrap_fs_in_writable_layer(current_task, lower)
1582 .expect("wrap_fs_in_writable_layer");
1583
1584 let info = overlay.root().node.info();
1585 assert_eq!(info.mode, mode!(IFDIR, 0o755));
1586 assert_eq!(info.uid, 42);
1587 assert_eq!(info.gid, 84);
1588 drop(info);
1589
1590 let refreshed = overlay
1591 .root()
1592 .node
1593 .fetch_and_refresh_info(current_task)
1594 .expect("fetch_and_refresh_info");
1595 assert_eq!(refreshed.mode, mode!(IFDIR, 0o755));
1596 assert_eq!(refreshed.uid, 42);
1597 assert_eq!(refreshed.gid, 84);
1598 })
1599 .await;
1600 }
1601}