@@ -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,11 @@ 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 (
1471+ state : & State ,
1472+ rootfs : & mut RootSetup ,
1473+ cleanup : Cleanup ,
1474+ ) -> Result < ( ) > {
14641475 if matches ! ( state. selinux_state, SELinuxFinalState :: ForceTargetDisabled ) {
14651476 rootfs. kargs . push ( "selinux=0" . to_string ( ) ) ;
14661477 }
@@ -1489,6 +1500,7 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
14891500 let bound_images = BoundImages :: from_state ( state) . await ?;
14901501
14911502 // Initialize the ostree sysroot (repo, stateroot, etc.)
1503+
14921504 {
14931505 let ( sysroot, has_ostree, imgstore) = initialize_ostree_root ( state, rootfs) . await ?;
14941506
@@ -1502,9 +1514,16 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
15021514 & imgstore,
15031515 )
15041516 . await ?;
1517+
1518+ if matches ! ( cleanup, Cleanup :: TriggerOnNextBoot ) {
1519+ let sysroot_dir = crate :: utils:: sysroot_dir ( & sysroot) ?;
1520+ tracing:: debug!( "Writing {DESTRUCTIVE_CLEANUP}" ) ;
1521+ sysroot_dir. atomic_write ( format ! ( "etc/{}" , DESTRUCTIVE_CLEANUP ) , b"" ) ?;
1522+ }
1523+
15051524 // We must drop the sysroot here in order to close any open file
15061525 // descriptors.
1507- }
1526+ } ;
15081527
15091528 // Run this on every install as the penultimate step
15101529 install_finalize ( & rootfs. physical_root_path ) . await ?;
@@ -1570,7 +1589,7 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
15701589 ( rootfs, loopback_dev)
15711590 } ;
15721591
1573- install_to_filesystem_impl ( & state, & mut rootfs) . await ?;
1592+ install_to_filesystem_impl ( & state, & mut rootfs, Cleanup :: Skip ) . await ?;
15741593
15751594 // Drop all data about the root except the bits we need to ensure any file descriptors etc. are closed.
15761595 let ( root_path, luksdev) = rootfs. into_storage ( ) ;
@@ -1740,11 +1759,17 @@ fn warn_on_host_root(rootfs_fd: &Dir) -> Result<()> {
17401759 Ok ( ( ) )
17411760}
17421761
1762+ pub enum Cleanup {
1763+ Skip ,
1764+ TriggerOnNextBoot ,
1765+ }
1766+
17431767/// Implementation of the `bootc install to-filsystem` CLI command.
17441768#[ context( "Installing to filesystem" ) ]
17451769pub ( crate ) async fn install_to_filesystem (
17461770 opts : InstallToFilesystemOpts ,
17471771 targeting_host_root : bool ,
1772+ cleanup : Cleanup ,
17481773) -> Result < ( ) > {
17491774 // Gather global state, destructuring the provided options.
17501775 // IMPORTANT: We might re-execute the current process in this function (for SELinux among other things)
@@ -1950,7 +1975,7 @@ pub(crate) async fn install_to_filesystem(
19501975 skip_finalize,
19511976 } ;
19521977
1953- install_to_filesystem_impl ( & state, & mut rootfs) . await ?;
1978+ install_to_filesystem_impl ( & state, & mut rootfs, cleanup ) . await ?;
19541979
19551980 // Drop all data about the root except the path to ensure any file descriptors etc. are closed.
19561981 drop ( rootfs) ;
@@ -1961,6 +1986,11 @@ pub(crate) async fn install_to_filesystem(
19611986}
19621987
19631988pub ( crate ) async fn install_to_existing_root ( opts : InstallToExistingRootOpts ) -> Result < ( ) > {
1989+ let cleanup = match opts. cleanup {
1990+ true => Cleanup :: TriggerOnNextBoot ,
1991+ false => Cleanup :: Skip ,
1992+ } ;
1993+
19641994 let opts = InstallToFilesystemOpts {
19651995 filesystem_opts : InstallTargetFilesystemOpts {
19661996 root_path : opts. root_path ,
@@ -1975,7 +2005,7 @@ pub(crate) async fn install_to_existing_root(opts: InstallToExistingRootOpts) ->
19752005 config_opts : opts. config_opts ,
19762006 } ;
19772007
1978- install_to_filesystem ( opts, true ) . await
2008+ install_to_filesystem ( opts, true , cleanup ) . await
19792009}
19802010
19812011/// Implementation of `bootc install finalize`.
0 commit comments