@@ -28,6 +28,7 @@ use camino::Utf8Path;
28
28
use camino:: Utf8PathBuf ;
29
29
use cap_std:: fs:: { Dir , MetadataExt } ;
30
30
use cap_std_ext:: cap_std;
31
+ use cap_std_ext:: cap_std:: fs:: FileType ;
31
32
use cap_std_ext:: cap_std:: fs_utf8:: DirEntry as DirEntryUtf8 ;
32
33
use cap_std_ext:: cap_tempfile:: TempDir ;
33
34
use cap_std_ext:: cmdext:: CapStdExtCommandExt ;
@@ -56,7 +57,7 @@ use crate::progress_jsonl::ProgressWriter;
56
57
use crate :: spec:: ImageReference ;
57
58
use crate :: store:: Storage ;
58
59
use crate :: task:: Task ;
59
- use crate :: utils:: sigpolicy_from_opts;
60
+ use crate :: utils:: { open_dir_noxdev , sigpolicy_from_opts} ;
60
61
61
62
/// The toplevel boot directory
62
63
const BOOT : & str = "boot" ;
@@ -1558,14 +1559,22 @@ fn require_empty_rootdir(rootfs_fd: &Dir) -> Result<()> {
1558
1559
}
1559
1560
1560
1561
/// 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.
1561
1564
#[ 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 < ( ) > {
1564
1566
for entry in d. entries ( ) ? {
1565
1567
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) ?;
1569
1578
}
1570
1579
}
1571
1580
anyhow:: Ok ( ( ) )
@@ -1576,13 +1585,15 @@ fn clean_boot_directories(rootfs: &Dir) -> Result<()> {
1576
1585
let bootdir =
1577
1586
crate :: utils:: open_dir_remount_rw ( rootfs, BOOT . into ( ) ) . context ( "Opening /boot" ) ?;
1578
1587
// 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.
1580
1591
if ARCH_USES_EFI {
1581
1592
if let Some ( efidir) = bootdir
1582
1593
. open_dir_optional ( crate :: bootloader:: EFI_DIR )
1583
1594
. context ( "Opening /boot/efi" ) ?
1584
1595
{
1585
- remove_all_in_dir_no_xdev ( & efidir) ?;
1596
+ remove_all_in_dir_no_xdev ( & efidir, false ) ?;
1586
1597
}
1587
1598
}
1588
1599
Ok ( ( ) )
@@ -1730,14 +1741,8 @@ pub(crate) async fn install_to_filesystem(
1730
1741
Some ( ReplaceMode :: Wipe ) => {
1731
1742
let rootfs_fd = rootfs_fd. try_clone ( ) ?;
1732
1743
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 ??;
1741
1746
}
1742
1747
Some ( ReplaceMode :: Alongside ) => clean_boot_directories ( & rootfs_fd) ?,
1743
1748
None => require_empty_rootdir ( & rootfs_fd) ?,
0 commit comments