Skip to content

Commit e6d8d0b

Browse files
committed
store: Introduce BootedOstree and BootedComposefs types
Refactor the storage layer to introduce new types that better encapsulate boot context: - Add BootedStorage wrapper that detects ostree vs composefs boot - Add BootedOstree struct holding sysroot reference and deployment - Add BootedComposefs struct holding repository and cmdline - Add BootedStorageKind enum to discriminate between backends This allows passing boot context as a cohesive unit rather than separate parameters. Update the upgrade code path to use these new types: - Change get_status() to accept &BootedOstree instead of separate params - Update handle_staged_soft_reboot() similarly - Update upgrade() to use structs throughout Add comprehensive documentation to all new types and methods in store/mod.rs. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
1 parent 7ebd101 commit e6d8d0b

File tree

7 files changed

+265
-106
lines changed

7 files changed

+265
-106
lines changed

crates/lib/src/bootc_composefs/status.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,14 +247,23 @@ async fn boot_entry_from_composefs_deployment(
247247
Ok(e)
248248
}
249249

250-
#[context("Getting composefs deployment status")]
251250
pub(crate) async fn composefs_deployment_status() -> Result<Host> {
252251
let composefs_state = composefs_booted()?
253252
.ok_or_else(|| anyhow::anyhow!("Failed to find composefs parameter in kernel cmdline"))?;
254-
let composefs_digest = &composefs_state.digest;
255253

256254
let sysroot =
257255
Dir::open_ambient_dir("/sysroot", ambient_authority()).context("Opening sysroot")?;
256+
257+
composefs_deployment_status_from(&sysroot, composefs_state).await
258+
}
259+
260+
#[context("Getting composefs deployment status")]
261+
pub(crate) async fn composefs_deployment_status_from(
262+
sysroot: &Dir,
263+
cmdline: &ComposefsCmdline,
264+
) -> Result<Host> {
265+
let composefs_digest = &cmdline.digest;
266+
258267
let deployments = sysroot
259268
.read_dir(STATE_DIR_RELATIVE)
260269
.with_context(|| format!("Reading sysroot {STATE_DIR_RELATIVE}"))?;

crates/lib/src/bootc_composefs/update.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
},
1515
cli::UpgradeOpts,
1616
spec::ImageReference,
17-
store::ComposefsRepository,
17+
store::{BootedComposefs, ComposefsRepository, Storage},
1818
};
1919

2020
use cap_std_ext::cap_std::{ambient_authority, fs::Dir};
@@ -61,7 +61,11 @@ async fn is_image_pulled(
6161
}
6262

6363
#[context("Upgrading composefs")]
64-
pub(crate) async fn upgrade_composefs(opts: UpgradeOpts) -> Result<()> {
64+
pub(crate) async fn upgrade_composefs(
65+
opts: UpgradeOpts,
66+
_storage: &Storage,
67+
_composefs: &BootedComposefs,
68+
) -> Result<()> {
6569
let host = composefs_deployment_status()
6670
.await
6771
.context("Getting composefs deployment status")?;

crates/lib/src/cli.rs

Lines changed: 68 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ use crate::bootc_composefs::{
3838
finalize::{composefs_backend_finalize, get_etc_diff},
3939
rollback::composefs_rollback,
4040
state::composefs_usr_overlay,
41-
status::composefs_booted,
4241
switch::switch_composefs,
4342
update::upgrade_composefs,
4443
};
@@ -48,7 +47,8 @@ use crate::podstorage::set_additional_image_store;
4847
use crate::progress_jsonl::{ProgressWriter, RawProgressFd};
4948
use crate::spec::Host;
5049
use crate::spec::ImageReference;
51-
use crate::store::ComposefsRepository;
50+
use crate::store::{BootedOstree, ComposefsRepository, Storage};
51+
use crate::store::{BootedStorage, BootedStorageKind};
5252
use crate::utils::sigpolicy_from_opt;
5353

5454
/// Shared progress options
@@ -712,24 +712,11 @@ pub(crate) fn ensure_self_unshared_mount_namespace() -> Result<()> {
712712
bootc_utils::reexec::reexec_with_guardenv(recurse_env, &["unshare", "-m", "--"])
713713
}
714714

715-
/// Acquire a locked sysroot.
716-
/// TODO drain this and the above into SysrootLock
717-
#[context("Acquiring sysroot")]
718-
pub(crate) async fn get_locked_sysroot() -> Result<ostree_ext::sysroot::SysrootLock> {
719-
prepare_for_write()?;
720-
let sysroot = ostree::Sysroot::new_default();
721-
sysroot.set_mount_namespace_in_use();
722-
let sysroot = ostree_ext::sysroot::SysrootLock::new_from_sysroot(&sysroot).await?;
723-
sysroot.load(gio::Cancellable::NONE)?;
724-
Ok(sysroot)
725-
}
726-
727715
/// Load global storage state, expecting that we're booted into a bootc system.
728716
#[context("Initializing storage")]
729-
pub(crate) async fn get_storage() -> Result<crate::store::Storage> {
730-
let global_run = Dir::open_ambient_dir("/run", cap_std::ambient_authority())?;
731-
let sysroot = get_locked_sysroot().await?;
732-
crate::store::Storage::new(sysroot, &global_run)
717+
pub(crate) async fn get_storage() -> Result<crate::store::BootedStorage> {
718+
prepare_for_write()?;
719+
BootedStorage::new().await
733720
}
734721

735722
#[context("Querying root privilege")]
@@ -811,15 +798,15 @@ where
811798
/// Handle soft reboot for staged deployments (used by upgrade and switch)
812799
#[context("Handling staged soft reboot")]
813800
fn handle_staged_soft_reboot(
814-
sysroot: &SysrootLock,
801+
booted_ostree: &BootedOstree<'_>,
815802
soft_reboot_mode: Option<SoftRebootMode>,
816803
host: &crate::spec::Host,
817804
) -> Result<()> {
818805
handle_soft_reboot(
819806
soft_reboot_mode,
820807
host.status.staged.as_ref(),
821808
"staged",
822-
|| soft_reboot_staged(sysroot),
809+
|| soft_reboot_staged(booted_ostree.sysroot),
823810
)
824811
}
825812

@@ -881,11 +868,14 @@ fn prepare_for_write() -> Result<()> {
881868

882869
/// Implementation of the `bootc upgrade` CLI command.
883870
#[context("Upgrading")]
884-
async fn upgrade(opts: UpgradeOpts) -> Result<()> {
885-
let sysroot = &get_storage().await?;
886-
let ostree = sysroot.get_ostree()?;
887-
let repo = &ostree.repo();
888-
let (booted_deployment, _deployments, host) = crate::status::get_status_require_booted(ostree)?;
871+
async fn upgrade(
872+
opts: UpgradeOpts,
873+
storage: &Storage,
874+
booted_ostree: &BootedOstree<'_>,
875+
) -> Result<()> {
876+
let repo = &booted_ostree.repo();
877+
878+
let host = crate::status::get_status(booted_ostree)?.1;
889879
let imgref = host.spec.image.as_ref();
890880
let prog: ProgressWriter = opts.progress.try_into()?;
891881

@@ -953,16 +943,16 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
953943
.unwrap_or_default();
954944
if staged_unchanged {
955945
println!("Staged update present, not changed.");
956-
handle_staged_soft_reboot(ostree, opts.soft_reboot, &host)?;
946+
handle_staged_soft_reboot(booted_ostree, opts.soft_reboot, &host)?;
957947
if opts.apply {
958948
crate::reboot::reboot()?;
959949
}
960950
} else if booted_unchanged {
961951
println!("No update available.")
962952
} else {
963-
let stateroot = booted_deployment.osname();
964-
let from = MergeState::from_stateroot(sysroot, &stateroot)?;
965-
crate::deploy::stage(sysroot, from, &fetched, &spec, prog.clone()).await?;
953+
let stateroot = booted_ostree.stateroot();
954+
let from = MergeState::from_stateroot(storage, &stateroot)?;
955+
crate::deploy::stage(storage, from, &fetched, &spec, prog.clone()).await?;
966956
changed = true;
967957
if let Some(prev) = booted_image.as_ref() {
968958
if let Some(fetched_manifest) = fetched.get_manifest(repo)? {
@@ -974,13 +964,13 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
974964
}
975965
}
976966
if changed {
977-
sysroot.update_mtime()?;
967+
storage.update_mtime()?;
978968

979969
if opts.soft_reboot.is_some() {
980970
// At this point we have new staged deployment and the host definition has changed.
981971
// We need the updated host status before we check if we can prepare the soft-reboot.
982-
let updated_host = crate::status::get_status(ostree, Some(&booted_deployment))?.1;
983-
handle_staged_soft_reboot(ostree, opts.soft_reboot, &updated_host)?;
972+
let updated_host = crate::status::get_status(booted_ostree)?.1;
973+
handle_staged_soft_reboot(booted_ostree, opts.soft_reboot, &updated_host)?;
984974
}
985975

986976
if opts.apply {
@@ -1030,8 +1020,8 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
10301020

10311021
let cancellable = gio::Cancellable::NONE;
10321022

1033-
let sysroot = &get_storage().await?;
1034-
let ostree = sysroot.get_ostree()?;
1023+
let storage = &get_storage().await?;
1024+
let ostree = storage.get_ostree()?;
10351025
let repo = &ostree.repo();
10361026
let (booted_deployment, _deployments, host) = crate::status::get_status_require_booted(ostree)?;
10371027

@@ -1081,16 +1071,20 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
10811071
}
10821072

10831073
let stateroot = booted_deployment.osname();
1084-
let from = MergeState::from_stateroot(sysroot, &stateroot)?;
1085-
crate::deploy::stage(sysroot, from, &fetched, &new_spec, prog.clone()).await?;
1074+
let from = MergeState::from_stateroot(storage, &stateroot)?;
1075+
crate::deploy::stage(storage, from, &fetched, &new_spec, prog.clone()).await?;
10861076

1087-
sysroot.update_mtime()?;
1077+
storage.update_mtime()?;
10881078

10891079
if opts.soft_reboot.is_some() {
10901080
// At this point we have staged the deployment and the host definition has changed.
10911081
// We need the updated host status before we check if we can prepare the soft-reboot.
1092-
let updated_host = crate::status::get_status(ostree, Some(&booted_deployment))?.1;
1093-
handle_staged_soft_reboot(ostree, opts.soft_reboot, &updated_host)?;
1082+
let booted_ostree = BootedOstree {
1083+
sysroot: ostree,
1084+
deployment: booted_deployment.clone(),
1085+
};
1086+
let updated_host = crate::status::get_status(&booted_ostree)?.1;
1087+
handle_staged_soft_reboot(&booted_ostree, opts.soft_reboot, &updated_host)?;
10941088
}
10951089

10961090
if opts.apply {
@@ -1103,9 +1097,9 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
11031097
/// Implementation of the `bootc rollback` CLI command.
11041098
#[context("Rollback")]
11051099
async fn rollback(opts: &RollbackOpts) -> Result<()> {
1106-
let sysroot = &get_storage().await?;
1107-
let ostree = sysroot.get_ostree()?;
1108-
crate::deploy::rollback(sysroot).await?;
1100+
let storage = &get_storage().await?;
1101+
let ostree = storage.get_ostree()?;
1102+
crate::deploy::rollback(storage).await?;
11091103

11101104
if opts.soft_reboot.is_some() {
11111105
// Get status of rollback deployment to check soft-reboot capability
@@ -1125,8 +1119,8 @@ async fn rollback(opts: &RollbackOpts) -> Result<()> {
11251119
/// Implementation of the `bootc edit` CLI command.
11261120
#[context("Editing spec")]
11271121
async fn edit(opts: EditOpts) -> Result<()> {
1128-
let sysroot = &get_storage().await?;
1129-
let ostree = sysroot.get_ostree()?;
1122+
let storage = &get_storage().await?;
1123+
let ostree = storage.get_ostree()?;
11301124
let repo = &ostree.repo();
11311125

11321126
let (booted_deployment, _deployments, host) = crate::status::get_status_require_booted(ostree)?;
@@ -1153,18 +1147,18 @@ async fn edit(opts: EditOpts) -> Result<()> {
11531147
// We only support two state transitions right now; switching the image,
11541148
// or flipping the bootloader ordering.
11551149
if host.spec.boot_order != new_host.spec.boot_order {
1156-
return crate::deploy::rollback(sysroot).await;
1150+
return crate::deploy::rollback(storage).await;
11571151
}
11581152

11591153
let fetched = crate::deploy::pull(repo, new_spec.image, None, opts.quiet, prog.clone()).await?;
11601154

11611155
// TODO gc old layers here
11621156

11631157
let stateroot = booted_deployment.osname();
1164-
let from = MergeState::from_stateroot(sysroot, &stateroot)?;
1165-
crate::deploy::stage(sysroot, from, &fetched, &new_spec, prog.clone()).await?;
1158+
let from = MergeState::from_stateroot(storage, &stateroot)?;
1159+
crate::deploy::stage(storage, from, &fetched, &new_spec, prog.clone()).await?;
11661160

1167-
sysroot.update_mtime()?;
1161+
storage.update_mtime()?;
11681162

11691163
Ok(())
11701164
}
@@ -1264,24 +1258,28 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
12641258
let root = &Dir::open_ambient_dir("/", cap_std::ambient_authority())?;
12651259
match opt {
12661260
Opt::Upgrade(opts) => {
1267-
if composefs_booted()?.is_some() {
1268-
upgrade_composefs(opts).await
1269-
} else {
1270-
upgrade(opts).await
1261+
let storage = &get_storage().await?;
1262+
match storage.kind()? {
1263+
BootedStorageKind::Ostree(booted_ostree) => {
1264+
upgrade(opts, storage, &booted_ostree).await
1265+
}
1266+
BootedStorageKind::Composefs(booted_cfs) => {
1267+
upgrade_composefs(opts, storage, &booted_cfs).await
1268+
}
12711269
}
12721270
}
12731271
Opt::Switch(opts) => {
1274-
if composefs_booted()?.is_some() {
1275-
switch_composefs(opts).await
1276-
} else {
1277-
switch(opts).await
1272+
let storage = &get_storage().await?;
1273+
match storage.kind()? {
1274+
BootedStorageKind::Ostree(_) => switch(opts).await,
1275+
BootedStorageKind::Composefs(_) => switch_composefs(opts).await,
12781276
}
12791277
}
12801278
Opt::Rollback(opts) => {
1281-
if composefs_booted()?.is_some() {
1282-
composefs_rollback().await?
1283-
} else {
1284-
rollback(&opts).await?
1279+
let storage = &get_storage().await?;
1280+
match storage.kind()? {
1281+
BootedStorageKind::Ostree(_) => rollback(&opts).await?,
1282+
BootedStorageKind::Composefs(_) => composefs_rollback().await?,
12851283
}
12861284

12871285
if opts.apply {
@@ -1292,10 +1290,10 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
12921290
}
12931291
Opt::Edit(opts) => edit(opts).await,
12941292
Opt::UsrOverlay => {
1295-
if composefs_booted()?.is_some() {
1296-
composefs_usr_overlay()
1297-
} else {
1298-
usroverlay().await
1293+
let storage = &get_storage().await?;
1294+
match storage.kind()? {
1295+
BootedStorageKind::Ostree(_) => usroverlay().await,
1296+
BootedStorageKind::Composefs(_) => composefs_usr_overlay(),
12991297
}
13001298
}
13011299
Opt::Container(opts) => match opts {
@@ -1391,8 +1389,8 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
13911389
crate::image::push_entrypoint(source.as_deref(), target.as_deref()).await
13921390
}
13931391
ImageOpts::PullFromDefaultStorage { image } => {
1394-
let sysroot = get_storage().await?;
1395-
sysroot
1392+
let storage = get_storage().await?;
1393+
storage
13961394
.get_ensure_imgstore()?
13971395
.pull_from_host_storage(&image)
13981396
.await
@@ -1492,8 +1490,8 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
14921490
InternalsOpts::Cfs { args } => crate::cfsctl::run_from_iter(args.iter()).await,
14931491
InternalsOpts::Reboot => crate::reboot::reboot(),
14941492
InternalsOpts::Fsck => {
1495-
let sysroot = &get_storage().await?;
1496-
crate::fsck::fsck(&sysroot, std::io::stdout().lock()).await?;
1493+
let storage = &get_storage().await?;
1494+
crate::fsck::fsck(&storage, std::io::stdout().lock()).await?;
14971495
Ok(())
14981496
}
14991497
InternalsOpts::FixupEtcFstab => crate::deploy::fixup_etc_fstab(&root),
@@ -1507,8 +1505,8 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
15071505
Ok(())
15081506
}
15091507
InternalsOpts::Cleanup => {
1510-
let sysroot = get_storage().await?;
1511-
crate::deploy::cleanup(&sysroot).await
1508+
let storage = get_storage().await?;
1509+
crate::deploy::cleanup(&storage).await
15121510
}
15131511
InternalsOpts::Relabel { as_path, path } => {
15141512
let root = &Dir::open_ambient_dir("/", cap_std::ambient_authority())?;

crates/lib/src/image.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ async fn list_images(list_type: ImageListType) -> Result<Vec<ImageOutput>> {
7272
let rootfs = cap_std::fs::Dir::open_ambient_dir("/", cap_std::ambient_authority())
7373
.context("Opening /")?;
7474

75-
let sysroot: Option<crate::store::Storage> =
75+
let sysroot: Option<crate::store::BootedStorage> =
7676
if ostree_ext::container_utils::running_in_container() {
7777
None
7878
} else {

crates/lib/src/install.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ async fn initialize_ostree_root(state: &State, root_setup: &RootSetup) -> Result
811811

812812
sysroot.load(cancellable)?;
813813
let sysroot = SysrootLock::new_from_sysroot(&sysroot).await?;
814-
let storage = Storage::new(sysroot, &temp_run)?;
814+
let storage = Storage::new_ostree(sysroot, &temp_run)?;
815815

816816
Ok((storage, has_ostree))
817817
}

0 commit comments

Comments
 (0)