@@ -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 ;
@@ -574,26 +574,34 @@ pub(crate) fn print_configuration() -> Result<()> {
574
574
}
575
575
576
576
#[ context( "Creating ostree deployment" ) ]
577
- async fn initialize_ostree_root ( state : & State , root_setup : & RootSetup ) -> Result < Storage > {
577
+ async fn initialize_ostree_root ( state : & State , root_setup : & RootSetup ) -> Result < ( Storage , bool ) > {
578
578
let sepolicy = state. load_policy ( ) ?;
579
579
let sepolicy = sepolicy. as_ref ( ) ;
580
580
// Load a fd for the mounted target physical root
581
581
let rootfs_dir = & root_setup. rootfs_fd ;
582
582
let rootfs = root_setup. rootfs . as_path ( ) ;
583
583
let cancellable = gio:: Cancellable :: NONE ;
584
584
585
+ let stateroot = state. stateroot ( ) ;
586
+
587
+ let has_ostree = rootfs_dir. try_exists ( "ostree/repo" ) ?;
588
+ if !has_ostree {
589
+ Task :: new_and_run (
590
+ "Initializing ostree layout" ,
591
+ "ostree" ,
592
+ [ "admin" , "init-fs" , "--modern" , rootfs. as_str ( ) ] ,
593
+ ) ?;
594
+ } else {
595
+ println ! ( "Reusing extant ostree layout" ) ;
596
+ let path = "." . into ( ) ;
597
+ let _ = crate :: utils:: open_dir_remount_rw ( rootfs_dir, path)
598
+ . context ( "remounting sysroot as read-write" ) ?;
599
+ }
600
+
585
601
// Ensure that the physical root is labeled.
586
602
// Another implementation: https://github.com/coreos/coreos-assembler/blob/3cd3307904593b3a131b81567b13a4d0b6fe7c90/src/create_disk.sh#L295
587
603
crate :: lsm:: ensure_dir_labeled ( rootfs_dir, "" , Some ( "/" . into ( ) ) , 0o755 . into ( ) , sepolicy) ?;
588
604
589
- let stateroot = state. stateroot ( ) ;
590
-
591
- Task :: new_and_run (
592
- "Initializing ostree layout" ,
593
- "ostree" ,
594
- [ "admin" , "init-fs" , "--modern" , rootfs. as_str ( ) ] ,
595
- ) ?;
596
-
597
605
// And also label /boot AKA xbootldr, if it exists
598
606
let bootdir = rootfs. join ( "boot" ) ;
599
607
if bootdir. try_exists ( ) ? {
@@ -617,6 +625,11 @@ async fn initialize_ostree_root(state: &State, root_setup: &RootSetup) -> Result
617
625
let sysroot = ostree:: Sysroot :: new ( Some ( & gio:: File :: for_path ( rootfs) ) ) ;
618
626
sysroot. load ( cancellable) ?;
619
627
628
+ let stateroot_exists = rootfs_dir. try_exists ( format ! ( "ostree/deploy/{stateroot}" ) ) ?;
629
+ ensure ! (
630
+ !stateroot_exists,
631
+ "Cannot redeploy over extant stateroot {stateroot}"
632
+ ) ;
620
633
sysroot
621
634
. init_osname ( stateroot, cancellable)
622
635
. context ( "initializing stateroot" ) ?;
@@ -647,14 +660,15 @@ async fn initialize_ostree_root(state: &State, root_setup: &RootSetup) -> Result
647
660
let sysroot = ostree:: Sysroot :: new ( Some ( & gio:: File :: for_path ( rootfs) ) ) ;
648
661
sysroot. load ( cancellable) ?;
649
662
let sysroot = SysrootLock :: new_from_sysroot ( & sysroot) . await ?;
650
- Storage :: new ( sysroot, & temp_run)
663
+ Ok ( ( Storage :: new ( sysroot, & temp_run) ? , has_ostree ) )
651
664
}
652
665
653
666
#[ context( "Creating ostree deployment" ) ]
654
667
async fn install_container (
655
668
state : & State ,
656
669
root_setup : & RootSetup ,
657
670
sysroot : & ostree:: Sysroot ,
671
+ has_ostree : bool ,
658
672
) -> Result < ( ostree:: Deployment , InstallAleph ) > {
659
673
let sepolicy = state. load_policy ( ) ?;
660
674
let sepolicy = sepolicy. as_ref ( ) ;
@@ -740,6 +754,7 @@ async fn install_container(
740
754
options. kargs = Some ( kargs. as_slice ( ) ) ;
741
755
options. target_imgref = Some ( & state. target_imgref ) ;
742
756
options. proxy_cfg = proxy_cfg;
757
+ options. no_clean = has_ostree;
743
758
let imgstate = crate :: utils:: async_task_with_spinner (
744
759
"Deploying container image" ,
745
760
ostree_container:: deploy:: deploy ( & sysroot, stateroot, & src_imageref, Some ( options) ) ,
@@ -1282,10 +1297,11 @@ async fn install_with_sysroot(
1282
1297
sysroot : & Storage ,
1283
1298
boot_uuid : & str ,
1284
1299
bound_images : & [ crate :: boundimage:: ResolvedBoundImage ] ,
1300
+ has_ostree : bool ,
1285
1301
) -> Result < ( ) > {
1286
1302
// And actually set up the container in that root, returning a deployment and
1287
1303
// the aleph state (see below).
1288
- let ( _deployment, aleph) = install_container ( state, rootfs, & sysroot) . await ?;
1304
+ let ( _deployment, aleph) = install_container ( state, rootfs, & sysroot, has_ostree ) . await ?;
1289
1305
// Write the aleph data that captures the system state at the time of provisioning for aid in future debugging.
1290
1306
rootfs
1291
1307
. rootfs_fd
@@ -1346,6 +1362,12 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
1346
1362
. ok_or_else ( || anyhow ! ( "No uuid for boot/root" ) ) ?;
1347
1363
tracing:: debug!( "boot uuid={boot_uuid}" ) ;
1348
1364
1365
+ // If we're doing an alongside install, then the /dev bootupd sees needs to be the host's.
1366
+ ensure ! (
1367
+ crate :: mount:: is_same_as_host( Utf8Path :: new( "/dev" ) ) ?,
1368
+ "Missing /dev mount to host /dev"
1369
+ ) ;
1370
+
1349
1371
let bound_images = if state. config_opts . skip_bound_images {
1350
1372
Vec :: new ( )
1351
1373
} else {
@@ -1366,8 +1388,16 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
1366
1388
1367
1389
// Initialize the ostree sysroot (repo, stateroot, etc.)
1368
1390
{
1369
- let sysroot = initialize_ostree_root ( state, rootfs) . await ?;
1370
- install_with_sysroot ( state, rootfs, & sysroot, & boot_uuid, & bound_images) . await ?;
1391
+ let ( sysroot, has_ostree) = initialize_ostree_root ( state, rootfs) . await ?;
1392
+ install_with_sysroot (
1393
+ state,
1394
+ rootfs,
1395
+ & sysroot,
1396
+ & boot_uuid,
1397
+ & bound_images,
1398
+ has_ostree,
1399
+ )
1400
+ . await ?;
1371
1401
// We must drop the sysroot here in order to close any open file
1372
1402
// descriptors.
1373
1403
}
@@ -1508,7 +1538,8 @@ fn remove_all_in_dir_no_xdev(d: &Dir) -> Result<()> {
1508
1538
1509
1539
#[ context( "Removing boot directory content" ) ]
1510
1540
fn clean_boot_directories ( rootfs : & Dir ) -> Result < ( ) > {
1511
- let bootdir = rootfs. open_dir ( BOOT ) . context ( "Opening /boot" ) ?;
1541
+ let bootdir =
1542
+ crate :: utils:: open_dir_remount_rw ( rootfs, BOOT . into ( ) ) . context ( "Opening /boot" ) ?;
1512
1543
// This should not remove /boot/efi note.
1513
1544
remove_all_in_dir_no_xdev ( & bootdir) ?;
1514
1545
if ARCH_USES_EFI {
@@ -1599,12 +1630,35 @@ pub(crate) async fn install_to_filesystem(
1599
1630
if !st. is_dir ( ) {
1600
1631
anyhow:: bail!( "Not a directory: {root_path}" ) ;
1601
1632
}
1633
+
1634
+ let inspect = crate :: mount:: inspect_filesystem ( & fsopts. root_path ) ?;
1635
+
1636
+ let alternative_root = fsopts. root_path . join ( "sysroot" ) ;
1637
+ let root_path = match inspect. fstype . as_str ( ) {
1638
+ // Our target filesystem is an overlay, the true root is in `/sysroot`
1639
+ "overlay" => {
1640
+ tracing:: debug!(
1641
+ "Overlay filesystem detected, using {alternative_root} instead of {root_path} as target root"
1642
+ ) ;
1643
+ & alternative_root
1644
+ }
1645
+ _ => root_path,
1646
+ } ;
1602
1647
let rootfs_fd = Dir :: open_ambient_dir ( root_path, cap_std:: ambient_authority ( ) )
1603
1648
. with_context ( || format ! ( "Opening target root directory {root_path}" ) ) ?;
1649
+
1650
+ tracing:: debug!( "Root filesystem: {root_path}" ) ;
1651
+
1604
1652
if let Some ( false ) = ostree_ext:: mountutil:: is_mountpoint ( & rootfs_fd, "." ) ? {
1605
1653
anyhow:: bail!( "Not a mountpoint: {root_path}" ) ;
1606
1654
}
1607
1655
1656
+ let fsopts = {
1657
+ let mut fsopts = fsopts. clone ( ) ;
1658
+ fsopts. root_path = root_path. clone ( ) ;
1659
+ fsopts
1660
+ } ;
1661
+
1608
1662
// Gather global state, destructuring the provided options.
1609
1663
// IMPORTANT: We might re-execute the current process in this function (for SELinux among other things)
1610
1664
// IMPORTANT: and hence anything that is done before MUST BE IDEMPOTENT.
0 commit comments