@@ -28,6 +28,7 @@ use camino::Utf8Path;
2828use camino:: Utf8PathBuf ;
2929use cap_std:: fs:: { Dir , MetadataExt } ;
3030use cap_std_ext:: cap_std;
31+ use cap_std_ext:: cap_std:: fs:: FileType ;
3132use cap_std_ext:: cap_std:: fs_utf8:: DirEntry as DirEntryUtf8 ;
3233use cap_std_ext:: cap_tempfile:: TempDir ;
3334use cap_std_ext:: cmdext:: CapStdExtCommandExt ;
@@ -56,7 +57,7 @@ use crate::progress_jsonl::ProgressWriter;
5657use crate :: spec:: ImageReference ;
5758use crate :: store:: Storage ;
5859use crate :: task:: Task ;
59- use crate :: utils:: sigpolicy_from_opts;
60+ use crate :: utils:: { open_dir_noxdev , sigpolicy_from_opts} ;
6061
6162/// The toplevel boot directory
6263const BOOT : & str = "boot" ;
@@ -1558,14 +1559,22 @@ fn require_empty_rootdir(rootfs_fd: &Dir) -> Result<()> {
15581559}
15591560
15601561/// Remove all entries in a directory, but do not traverse across distinct devices.
1562+ /// If mount_err is true, then an error is returned if a mount point is found;
1563+ /// otherwise it is silently ignored.
15611564#[ context( "Removing entries (noxdev)" ) ]
1562- fn remove_all_in_dir_no_xdev ( d : & Dir ) -> Result < ( ) > {
1563- let parent_dev = d. dir_metadata ( ) ?. dev ( ) ;
1565+ fn remove_all_in_dir_no_xdev ( d : & Dir , mount_err : bool ) -> Result < ( ) > {
15641566 for entry in d. entries ( ) ? {
15651567 let entry = entry?;
1566- let entry_dev = entry. metadata ( ) ?. dev ( ) ;
1567- if entry_dev == parent_dev {
1568- d. remove_all_optional ( entry. file_name ( ) ) ?;
1568+ let name = entry. file_name ( ) ;
1569+ let etype = entry. file_type ( ) ?;
1570+ if etype == FileType :: dir ( ) {
1571+ if let Some ( subdir) = open_dir_noxdev ( d, & name) ? {
1572+ remove_all_in_dir_no_xdev ( & subdir, mount_err) ?;
1573+ } else if mount_err {
1574+ anyhow:: bail!( "Found unexpected mount point {name:?}" ) ;
1575+ }
1576+ } else {
1577+ d. remove_file_optional ( & name) ?;
15691578 }
15701579 }
15711580 anyhow:: Ok ( ( ) )
@@ -1576,13 +1585,15 @@ fn clean_boot_directories(rootfs: &Dir) -> Result<()> {
15761585 let bootdir =
15771586 crate :: utils:: open_dir_remount_rw ( rootfs, BOOT . into ( ) ) . context ( "Opening /boot" ) ?;
15781587 // This should not remove /boot/efi note.
1579- remove_all_in_dir_no_xdev ( & bootdir) ?;
1588+ remove_all_in_dir_no_xdev ( & bootdir, false ) ?;
1589+ // TODO: Discover the ESP the same way bootupd does it; we should also
1590+ // support not wiping the ESP.
15801591 if ARCH_USES_EFI {
15811592 if let Some ( efidir) = bootdir
15821593 . open_dir_optional ( crate :: bootloader:: EFI_DIR )
15831594 . context ( "Opening /boot/efi" ) ?
15841595 {
1585- remove_all_in_dir_no_xdev ( & efidir) ?;
1596+ remove_all_in_dir_no_xdev ( & efidir, false ) ?;
15861597 }
15871598 }
15881599 Ok ( ( ) )
@@ -1730,14 +1741,8 @@ pub(crate) async fn install_to_filesystem(
17301741 Some ( ReplaceMode :: Wipe ) => {
17311742 let rootfs_fd = rootfs_fd. try_clone ( ) ?;
17321743 println ! ( "Wiping contents of root" ) ;
1733- tokio:: task:: spawn_blocking ( move || {
1734- for e in rootfs_fd. entries ( ) ? {
1735- let e = e?;
1736- rootfs_fd. remove_all_optional ( e. file_name ( ) ) ?;
1737- }
1738- anyhow:: Ok ( ( ) )
1739- } )
1740- . await ??;
1744+ tokio:: task:: spawn_blocking ( move || remove_all_in_dir_no_xdev ( & rootfs_fd, true ) )
1745+ . await ??;
17411746 }
17421747 Some ( ReplaceMode :: Alongside ) => clean_boot_directories ( & rootfs_fd) ?,
17431748 None => require_empty_rootdir ( & rootfs_fd) ?,
0 commit comments