@@ -20,8 +20,8 @@ use std::str::FromStr;
2020use std:: sync:: Arc ;
2121use std:: time:: Duration ;
2222
23- use anyhow:: Ok ;
2423use anyhow:: { anyhow, Context , Result } ;
24+ use anyhow:: { ensure, Ok } ;
2525use bootc_utils:: CommandRunExt ;
2626use camino:: Utf8Path ;
2727use camino:: Utf8PathBuf ;
@@ -564,26 +564,34 @@ pub(crate) fn print_configuration() -> Result<()> {
564564}
565565
566566#[ context( "Creating ostree deployment" ) ]
567- async fn initialize_ostree_root ( state : & State , root_setup : & RootSetup ) -> Result < Storage > {
567+ async fn initialize_ostree_root ( state : & State , root_setup : & RootSetup ) -> Result < ( Storage , bool ) > {
568568 let sepolicy = state. load_policy ( ) ?;
569569 let sepolicy = sepolicy. as_ref ( ) ;
570570 // Load a fd for the mounted target physical root
571571 let rootfs_dir = & root_setup. rootfs_fd ;
572572 let rootfs = root_setup. rootfs . as_path ( ) ;
573573 let cancellable = gio:: Cancellable :: NONE ;
574574
575+ let stateroot = state. stateroot ( ) ;
576+
577+ let has_ostree = rootfs_dir. try_exists ( "ostree/repo" ) ?;
578+ if !has_ostree {
579+ Task :: new_and_run (
580+ "Initializing ostree layout" ,
581+ "ostree" ,
582+ [ "admin" , "init-fs" , "--modern" , rootfs. as_str ( ) ] ,
583+ ) ?;
584+ } else {
585+ println ! ( "Reusing extant ostree layout" ) ;
586+ let path = "." . into ( ) ;
587+ let _ = crate :: utils:: open_dir_remount_rw ( rootfs_dir, path)
588+ . context ( "remounting sysroot as read-write" ) ?;
589+ }
590+
575591 // Ensure that the physical root is labeled.
576592 // Another implementation: https://github.com/coreos/coreos-assembler/blob/3cd3307904593b3a131b81567b13a4d0b6fe7c90/src/create_disk.sh#L295
577593 crate :: lsm:: ensure_dir_labeled ( rootfs_dir, "" , Some ( "/" . into ( ) ) , 0o755 . into ( ) , sepolicy) ?;
578594
579- let stateroot = state. stateroot ( ) ;
580-
581- Task :: new_and_run (
582- "Initializing ostree layout" ,
583- "ostree" ,
584- [ "admin" , "init-fs" , "--modern" , rootfs. as_str ( ) ] ,
585- ) ?;
586-
587595 // And also label /boot AKA xbootldr, if it exists
588596 let bootdir = rootfs. join ( "boot" ) ;
589597 if bootdir. try_exists ( ) ? {
@@ -607,9 +615,14 @@ async fn initialize_ostree_root(state: &State, root_setup: &RootSetup) -> Result
607615 let sysroot = ostree:: Sysroot :: new ( Some ( & gio:: File :: for_path ( rootfs) ) ) ;
608616 sysroot. load ( cancellable) ?;
609617
610- sysroot
611- . init_osname ( stateroot, cancellable)
612- . context ( "initializing stateroot" ) ?;
618+ let stateroot_exists = rootfs_dir. try_exists ( format ! ( "ostree/deploy/{stateroot}" ) ) ?;
619+ if stateroot_exists {
620+ anyhow:: bail!( "Cannot redeploy over extant stateroot {stateroot}" ) ;
621+ } else {
622+ sysroot
623+ . init_osname ( stateroot, cancellable)
624+ . context ( "initializing stateroot" ) ?;
625+ }
613626
614627 let sysroot_dir = Dir :: reopen_dir ( & crate :: utils:: sysroot_fd ( & sysroot) ) ?;
615628
@@ -637,14 +650,15 @@ async fn initialize_ostree_root(state: &State, root_setup: &RootSetup) -> Result
637650 let sysroot = ostree:: Sysroot :: new ( Some ( & gio:: File :: for_path ( rootfs) ) ) ;
638651 sysroot. load ( cancellable) ?;
639652 let sysroot = SysrootLock :: new_from_sysroot ( & sysroot) . await ?;
640- Storage :: new ( sysroot, & temp_run)
653+ Ok ( ( Storage :: new ( sysroot, & temp_run) ? , has_ostree ) )
641654}
642655
643656#[ context( "Creating ostree deployment" ) ]
644657async fn install_container (
645658 state : & State ,
646659 root_setup : & RootSetup ,
647660 sysroot : & ostree:: Sysroot ,
661+ has_ostree : bool ,
648662) -> Result < ( ostree:: Deployment , InstallAleph ) > {
649663 let sepolicy = state. load_policy ( ) ?;
650664 let sepolicy = sepolicy. as_ref ( ) ;
@@ -730,6 +744,7 @@ async fn install_container(
730744 options. kargs = Some ( kargs. as_slice ( ) ) ;
731745 options. target_imgref = Some ( & state. target_imgref ) ;
732746 options. proxy_cfg = proxy_cfg;
747+ options. no_clean = has_ostree;
733748 let imgstate = crate :: utils:: async_task_with_spinner (
734749 "Deploying container image" ,
735750 ostree_container:: deploy:: deploy ( & sysroot, stateroot, & src_imageref, Some ( options) ) ,
@@ -1272,10 +1287,11 @@ async fn install_with_sysroot(
12721287 sysroot : & Storage ,
12731288 boot_uuid : & str ,
12741289 bound_images : & [ crate :: boundimage:: ResolvedBoundImage ] ,
1290+ has_ostree : bool ,
12751291) -> Result < ( ) > {
12761292 // And actually set up the container in that root, returning a deployment and
12771293 // the aleph state (see below).
1278- let ( _deployment, aleph) = install_container ( state, rootfs, & sysroot) . await ?;
1294+ let ( _deployment, aleph) = install_container ( state, rootfs, & sysroot, has_ostree ) . await ?;
12791295 // Write the aleph data that captures the system state at the time of provisioning for aid in future debugging.
12801296 rootfs
12811297 . rootfs_fd
@@ -1336,6 +1352,12 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
13361352 . ok_or_else ( || anyhow ! ( "No uuid for boot/root" ) ) ?;
13371353 tracing:: debug!( "boot uuid={boot_uuid}" ) ;
13381354
1355+ // If we're doing an alongside install, then the /dev bootupd sees needs to be the host's.
1356+ ensure ! (
1357+ crate :: mount:: is_same_as_host( Utf8Path :: new( "/dev" ) ) ?,
1358+ "Missing /dev mount to host /dev"
1359+ ) ;
1360+
13391361 let bound_images = if state. config_opts . skip_bound_images {
13401362 Vec :: new ( )
13411363 } else {
@@ -1356,8 +1378,16 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
13561378
13571379 // Initialize the ostree sysroot (repo, stateroot, etc.)
13581380 {
1359- let sysroot = initialize_ostree_root ( state, rootfs) . await ?;
1360- install_with_sysroot ( state, rootfs, & sysroot, & boot_uuid, & bound_images) . await ?;
1381+ let ( sysroot, has_ostree) = initialize_ostree_root ( state, rootfs) . await ?;
1382+ install_with_sysroot (
1383+ state,
1384+ rootfs,
1385+ & sysroot,
1386+ & boot_uuid,
1387+ & bound_images,
1388+ has_ostree,
1389+ )
1390+ . await ?;
13611391 // We must drop the sysroot here in order to close any open file
13621392 // descriptors.
13631393 }
@@ -1498,7 +1528,8 @@ fn remove_all_in_dir_no_xdev(d: &Dir) -> Result<()> {
14981528
14991529#[ context( "Removing boot directory content" ) ]
15001530fn clean_boot_directories ( rootfs : & Dir ) -> Result < ( ) > {
1501- let bootdir = rootfs. open_dir ( BOOT ) . context ( "Opening /boot" ) ?;
1531+ let bootdir =
1532+ crate :: utils:: open_dir_remount_rw ( rootfs, BOOT . into ( ) ) . context ( "Opening /boot" ) ?;
15021533 // This should not remove /boot/efi note.
15031534 remove_all_in_dir_no_xdev ( & bootdir) ?;
15041535 if ARCH_USES_EFI {
@@ -1589,12 +1620,35 @@ pub(crate) async fn install_to_filesystem(
15891620 if !st. is_dir ( ) {
15901621 anyhow:: bail!( "Not a directory: {root_path}" ) ;
15911622 }
1623+
1624+ let inspect = crate :: mount:: inspect_filesystem ( & fsopts. root_path ) ?;
1625+
1626+ let alternative_root = fsopts. root_path . join ( "sysroot" ) ;
1627+ let root_path = match inspect. source . as_str ( ) {
1628+ // Our target filesystem is an overlay, the true root is in `/sysroot`
1629+ "overlay" => {
1630+ tracing:: debug!(
1631+ "Overlay filesystem detected, using {alternative_root} instead of {root_path} as target root"
1632+ ) ;
1633+ & alternative_root
1634+ }
1635+ _ => root_path,
1636+ } ;
15921637 let rootfs_fd = Dir :: open_ambient_dir ( root_path, cap_std:: ambient_authority ( ) )
15931638 . with_context ( || format ! ( "Opening target root directory {root_path}" ) ) ?;
1639+
1640+ tracing:: debug!( "Root filesystem: {root_path}" ) ;
1641+
15941642 if let Some ( false ) = ostree_ext:: mountutil:: is_mountpoint ( & rootfs_fd, "." ) ? {
15951643 anyhow:: bail!( "Not a mountpoint: {root_path}" ) ;
15961644 }
15971645
1646+ let fsopts = {
1647+ let mut fsopts = fsopts. clone ( ) ;
1648+ fsopts. root_path = root_path. clone ( ) ;
1649+ fsopts
1650+ } ;
1651+
15981652 // Gather global state, destructuring the provided options.
15991653 // IMPORTANT: We might re-execute the current process in this function (for SELinux among other things)
16001654 // IMPORTANT: and hence anything that is done before MUST BE IDEMPOTENT.
0 commit comments