@@ -69,6 +69,8 @@ const BOOT: &str = "boot";
6969const RUN_BOOTC : & str = "/run/bootc" ;
7070/// The default path for the host rootfs
7171const ALONGSIDE_ROOT_MOUNT : & str = "/target" ;
72+ /// Global flag to signal the booted system was provisioned via an alongside bootc install
73+ const DESTRUCTIVE_CLEANUP : & str = "bootc-destructive-cleanup" ;
7274/// This is an ext4 special directory we need to ignore.
7375const LOST_AND_FOUND : & str = "lost+found" ;
7476/// The filename of the composefs EROFS superblock; TODO move this into ostree
@@ -335,6 +337,11 @@ pub(crate) struct InstallToExistingRootOpts {
335337 #[ clap( long) ]
336338 pub ( crate ) acknowledge_destructive : bool ,
337339
340+ /// Add the bootc-destructive-cleanup systemd service to delete files from
341+ /// the previous install on first boot
342+ #[ clap( long) ]
343+ pub ( crate ) cleanup : bool ,
344+
338345 /// Path to the mounted root; this is now not necessary to provide.
339346 /// Historically it was necessary to ensure the host rootfs was mounted at here
340347 /// via e.g. `-v /:/target`.
@@ -1460,7 +1467,7 @@ impl BoundImages {
14601467 }
14611468}
14621469
1463- async fn install_to_filesystem_impl ( state : & State , rootfs : & mut RootSetup ) -> Result < ( ) > {
1470+ async fn install_to_filesystem_impl ( state : & State , rootfs : & mut RootSetup ) -> Result < Storage > {
14641471 if matches ! ( state. selinux_state, SELinuxFinalState :: ForceTargetDisabled ) {
14651472 rootfs. kargs . push ( "selinux=0" . to_string ( ) ) ;
14661473 }
@@ -1489,7 +1496,8 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
14891496 let bound_images = BoundImages :: from_state ( state) . await ?;
14901497
14911498 // Initialize the ostree sysroot (repo, stateroot, etc.)
1492- {
1499+
1500+ let sysroot = {
14931501 let ( sysroot, has_ostree, imgstore) = initialize_ostree_root ( state, rootfs) . await ?;
14941502
14951503 install_with_sysroot (
@@ -1504,7 +1512,9 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
15041512 . await ?;
15051513 // We must drop the sysroot here in order to close any open file
15061514 // descriptors.
1507- }
1515+ //
1516+ sysroot
1517+ } ;
15081518
15091519 // Run this on every install as the penultimate step
15101520 install_finalize ( & rootfs. physical_root_path ) . await ?;
@@ -1517,7 +1527,7 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
15171527 }
15181528 }
15191529
1520- Ok ( ( ) )
1530+ Ok ( sysroot )
15211531}
15221532
15231533fn installation_complete ( ) {
@@ -1570,7 +1580,9 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
15701580 ( rootfs, loopback_dev)
15711581 } ;
15721582
1573- install_to_filesystem_impl ( & state, & mut rootfs) . await ?;
1583+ {
1584+ let _sysroot = install_to_filesystem_impl ( & state, & mut rootfs) . await ?;
1585+ }
15741586
15751587 // Drop all data about the root except the bits we need to ensure any file descriptors etc. are closed.
15761588 let ( root_path, luksdev) = rootfs. into_storage ( ) ;
@@ -1740,11 +1752,17 @@ fn warn_on_host_root(rootfs_fd: &Dir) -> Result<()> {
17401752 Ok ( ( ) )
17411753}
17421754
1755+ pub enum Cleanup {
1756+ Skip ,
1757+ TriggerOnNextBoot ,
1758+ }
1759+
17431760/// Implementation of the `bootc install to-filsystem` CLI command.
17441761#[ context( "Installing to filesystem" ) ]
17451762pub ( crate ) async fn install_to_filesystem (
17461763 opts : InstallToFilesystemOpts ,
17471764 targeting_host_root : bool ,
1765+ cleanup : Cleanup ,
17481766) -> Result < ( ) > {
17491767 // Gather global state, destructuring the provided options.
17501768 // IMPORTANT: We might re-execute the current process in this function (for SELinux among other things)
@@ -1950,7 +1968,15 @@ pub(crate) async fn install_to_filesystem(
19501968 skip_finalize,
19511969 } ;
19521970
1953- install_to_filesystem_impl ( & state, & mut rootfs) . await ?;
1971+ {
1972+ let sysroot = install_to_filesystem_impl ( & state, & mut rootfs) . await ?;
1973+
1974+ if matches ! ( cleanup, Cleanup :: TriggerOnNextBoot ) {
1975+ let sysroot_dir = crate :: utils:: sysroot_dir ( & sysroot. sysroot ) ?;
1976+ tracing:: debug!( "Writing {DESTRUCTIVE_CLEANUP}" ) ;
1977+ sysroot_dir. atomic_write ( format ! ( "etc/{}" , DESTRUCTIVE_CLEANUP ) , b"" ) ?;
1978+ }
1979+ }
19541980
19551981 // Drop all data about the root except the path to ensure any file descriptors etc. are closed.
19561982 drop ( rootfs) ;
@@ -1961,6 +1987,11 @@ pub(crate) async fn install_to_filesystem(
19611987}
19621988
19631989pub ( crate ) async fn install_to_existing_root ( opts : InstallToExistingRootOpts ) -> Result < ( ) > {
1990+ let cleanup = match opts. cleanup {
1991+ true => Cleanup :: TriggerOnNextBoot ,
1992+ false => Cleanup :: Skip ,
1993+ } ;
1994+
19641995 let opts = InstallToFilesystemOpts {
19651996 filesystem_opts : InstallTargetFilesystemOpts {
19661997 root_path : opts. root_path ,
@@ -1975,7 +2006,7 @@ pub(crate) async fn install_to_existing_root(opts: InstallToExistingRootOpts) ->
19752006 config_opts : opts. config_opts ,
19762007 } ;
19772008
1978- install_to_filesystem ( opts, true ) . await
2009+ install_to_filesystem ( opts, true , cleanup ) . await
19792010}
19802011
19812012/// Implementation of `bootc install finalize`.
0 commit comments