@@ -20,8 +20,8 @@ use std::str::FromStr;
20
20
use std:: sync:: Arc ;
21
21
use std:: time:: Duration ;
22
22
23
- use anyhow:: Ok ;
24
23
use anyhow:: { anyhow, Context , Result } ;
24
+ use anyhow:: { ensure, Ok } ;
25
25
use bootc_utils:: CommandRunExt ;
26
26
use camino:: Utf8Path ;
27
27
use camino:: Utf8PathBuf ;
@@ -576,26 +576,36 @@ pub(crate) fn print_configuration() -> Result<()> {
576
576
}
577
577
578
578
#[ context( "Creating ostree deployment" ) ]
579
- async fn initialize_ostree_root ( state : & State , root_setup : & RootSetup ) -> Result < Storage > {
579
+ async fn initialize_ostree_root ( state : & State , root_setup : & RootSetup ) -> Result < ( Storage , bool ) > {
580
580
let sepolicy = state. load_policy ( ) ?;
581
581
let sepolicy = sepolicy. as_ref ( ) ;
582
582
// Load a fd for the mounted target physical root
583
583
let rootfs_dir = & root_setup. rootfs_fd ;
584
584
let rootfs = root_setup. rootfs . as_path ( ) ;
585
585
let cancellable = gio:: Cancellable :: NONE ;
586
586
587
+ let stateroot = state. stateroot ( ) ;
588
+
589
+ let has_ostree = rootfs_dir. try_exists ( "ostree/repo" ) ?;
590
+ if !has_ostree {
591
+ Task :: new_and_run (
592
+ "Initializing ostree layout" ,
593
+ "ostree" ,
594
+ [ "admin" , "init-fs" , "--modern" , rootfs. as_str ( ) ] ,
595
+ ) ?;
596
+ } else {
597
+ println ! ( "Reusing extant ostree layout" ) ;
598
+
599
+ let path = "." . into ( ) ;
600
+ let _ = crate :: utils:: open_dir_remount_rw ( rootfs_dir, path)
601
+ . context ( "remounting target as read-write" ) ?;
602
+ crate :: utils:: remove_immutability ( rootfs_dir, path) ?;
603
+ }
604
+
587
605
// Ensure that the physical root is labeled.
588
606
// Another implementation: https://github.com/coreos/coreos-assembler/blob/3cd3307904593b3a131b81567b13a4d0b6fe7c90/src/create_disk.sh#L295
589
607
crate :: lsm:: ensure_dir_labeled ( rootfs_dir, "" , Some ( "/" . into ( ) ) , 0o755 . into ( ) , sepolicy) ?;
590
608
591
- let stateroot = state. stateroot ( ) ;
592
-
593
- Task :: new_and_run (
594
- "Initializing ostree layout" ,
595
- "ostree" ,
596
- [ "admin" , "init-fs" , "--modern" , rootfs. as_str ( ) ] ,
597
- ) ?;
598
-
599
609
// And also label /boot AKA xbootldr, if it exists
600
610
let bootdir = rootfs. join ( "boot" ) ;
601
611
if bootdir. try_exists ( ) ? {
@@ -619,6 +629,11 @@ async fn initialize_ostree_root(state: &State, root_setup: &RootSetup) -> Result
619
629
let sysroot = ostree:: Sysroot :: new ( Some ( & gio:: File :: for_path ( rootfs) ) ) ;
620
630
sysroot. load ( cancellable) ?;
621
631
632
+ let stateroot_exists = rootfs_dir. try_exists ( format ! ( "ostree/deploy/{stateroot}" ) ) ?;
633
+ ensure ! (
634
+ !stateroot_exists,
635
+ "Cannot redeploy over extant stateroot {stateroot}"
636
+ ) ;
622
637
sysroot
623
638
. init_osname ( stateroot, cancellable)
624
639
. context ( "initializing stateroot" ) ?;
@@ -649,14 +664,15 @@ async fn initialize_ostree_root(state: &State, root_setup: &RootSetup) -> Result
649
664
let sysroot = ostree:: Sysroot :: new ( Some ( & gio:: File :: for_path ( rootfs) ) ) ;
650
665
sysroot. load ( cancellable) ?;
651
666
let sysroot = SysrootLock :: new_from_sysroot ( & sysroot) . await ?;
652
- Storage :: new ( sysroot, & temp_run)
667
+ Ok ( ( Storage :: new ( sysroot, & temp_run) ? , has_ostree ) )
653
668
}
654
669
655
670
#[ context( "Creating ostree deployment" ) ]
656
671
async fn install_container (
657
672
state : & State ,
658
673
root_setup : & RootSetup ,
659
674
sysroot : & ostree:: Sysroot ,
675
+ has_ostree : bool ,
660
676
) -> Result < ( ostree:: Deployment , InstallAleph ) > {
661
677
let sepolicy = state. load_policy ( ) ?;
662
678
let sepolicy = sepolicy. as_ref ( ) ;
@@ -749,6 +765,7 @@ async fn install_container(
749
765
options. kargs = Some ( kargs. as_slice ( ) ) ;
750
766
options. target_imgref = Some ( & state. target_imgref ) ;
751
767
options. proxy_cfg = proxy_cfg;
768
+ options. no_clean = has_ostree;
752
769
let imgstate = crate :: utils:: async_task_with_spinner (
753
770
"Deploying container image" ,
754
771
ostree_container:: deploy:: deploy ( & sysroot, stateroot, & src_imageref, Some ( options) ) ,
@@ -1295,10 +1312,11 @@ async fn install_with_sysroot(
1295
1312
sysroot : & Storage ,
1296
1313
boot_uuid : & str ,
1297
1314
bound_images : & [ crate :: boundimage:: ResolvedBoundImage ] ,
1315
+ has_ostree : bool ,
1298
1316
) -> Result < ( ) > {
1299
1317
// And actually set up the container in that root, returning a deployment and
1300
1318
// the aleph state (see below).
1301
- let ( _deployment, aleph) = install_container ( state, rootfs, & sysroot) . await ?;
1319
+ let ( _deployment, aleph) = install_container ( state, rootfs, & sysroot, has_ostree ) . await ?;
1302
1320
// Write the aleph data that captures the system state at the time of provisioning for aid in future debugging.
1303
1321
rootfs
1304
1322
. rootfs_fd
@@ -1359,6 +1377,12 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
1359
1377
. ok_or_else ( || anyhow ! ( "No uuid for boot/root" ) ) ?;
1360
1378
tracing:: debug!( "boot uuid={boot_uuid}" ) ;
1361
1379
1380
+ // If we're doing an alongside install, then the /dev bootupd sees needs to be the host's.
1381
+ ensure ! (
1382
+ crate :: mount:: is_same_as_host( Utf8Path :: new( "/dev" ) ) ?,
1383
+ "Missing /dev mount to host /dev"
1384
+ ) ;
1385
+
1362
1386
let bound_images = if state. config_opts . skip_bound_images {
1363
1387
Vec :: new ( )
1364
1388
} else {
@@ -1379,8 +1403,16 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
1379
1403
1380
1404
// Initialize the ostree sysroot (repo, stateroot, etc.)
1381
1405
{
1382
- let sysroot = initialize_ostree_root ( state, rootfs) . await ?;
1383
- install_with_sysroot ( state, rootfs, & sysroot, & boot_uuid, & bound_images) . await ?;
1406
+ let ( sysroot, has_ostree) = initialize_ostree_root ( state, rootfs) . await ?;
1407
+ install_with_sysroot (
1408
+ state,
1409
+ rootfs,
1410
+ & sysroot,
1411
+ & boot_uuid,
1412
+ & bound_images,
1413
+ has_ostree,
1414
+ )
1415
+ . await ?;
1384
1416
// We must drop the sysroot here in order to close any open file
1385
1417
// descriptors.
1386
1418
}
@@ -1521,7 +1553,8 @@ fn remove_all_in_dir_no_xdev(d: &Dir) -> Result<()> {
1521
1553
1522
1554
#[ context( "Removing boot directory content" ) ]
1523
1555
fn clean_boot_directories ( rootfs : & Dir ) -> Result < ( ) > {
1524
- let bootdir = rootfs. open_dir ( BOOT ) . context ( "Opening /boot" ) ?;
1556
+ let bootdir =
1557
+ crate :: utils:: open_dir_remount_rw ( rootfs, BOOT . into ( ) ) . context ( "Opening /boot" ) ?;
1525
1558
// This should not remove /boot/efi note.
1526
1559
remove_all_in_dir_no_xdev ( & bootdir) ?;
1527
1560
if ARCH_USES_EFI {
@@ -1612,12 +1645,33 @@ pub(crate) async fn install_to_filesystem(
1612
1645
if !st. is_dir ( ) {
1613
1646
anyhow:: bail!( "Not a directory: {root_path}" ) ;
1614
1647
}
1648
+
1649
+ let possible_physical_root = fsopts. root_path . join ( "sysroot" ) ;
1650
+ let possible_ostree_dir = possible_physical_root. join ( "ostree" ) ;
1651
+ let root_path = if possible_ostree_dir. exists ( ) {
1652
+ tracing:: debug!(
1653
+ "ostree detected in {possible_ostree_dir}, assuming / is a deployment root and using {possible_physical_root} instead of {root_path} as target root"
1654
+ ) ;
1655
+ & possible_physical_root
1656
+ } else {
1657
+ root_path
1658
+ } ;
1659
+
1615
1660
let rootfs_fd = Dir :: open_ambient_dir ( root_path, cap_std:: ambient_authority ( ) )
1616
1661
. with_context ( || format ! ( "Opening target root directory {root_path}" ) ) ?;
1662
+
1663
+ tracing:: debug!( "Root filesystem: {root_path}" ) ;
1664
+
1617
1665
if let Some ( false ) = ostree_ext:: mountutil:: is_mountpoint ( & rootfs_fd, "." ) ? {
1618
1666
anyhow:: bail!( "Not a mountpoint: {root_path}" ) ;
1619
1667
}
1620
1668
1669
+ let fsopts = {
1670
+ let mut fsopts = fsopts. clone ( ) ;
1671
+ fsopts. root_path = root_path. clone ( ) ;
1672
+ fsopts
1673
+ } ;
1674
+
1621
1675
// Gather global state, destructuring the provided options.
1622
1676
// IMPORTANT: We might re-execute the current process in this function (for SELinux among other things)
1623
1677
// IMPORTANT: and hence anything that is done before MUST BE IDEMPOTENT.
0 commit comments