Skip to content

Commit 72bca1b

Browse files
authored
Merge pull request #619 from cgwalters/refactor-sysroot-lock
cli: Make sysroot lock automatically check root + setup mountns
2 parents b5f47ff + b10a1fe commit 72bca1b

File tree

3 files changed

+19
-9
lines changed

3 files changed

+19
-9
lines changed

lib/src/cli.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ pub(crate) enum Opt {
324324
/// TODO use https://github.com/ostreedev/ostree/pull/2779 once
325325
/// we can depend on a new enough ostree
326326
#[context("Ensuring mountns")]
327-
pub(crate) async fn ensure_self_unshared_mount_namespace() -> Result<()> {
327+
pub(crate) fn ensure_self_unshared_mount_namespace() -> Result<()> {
328328
let uid = rustix::process::getuid();
329329
if !uid.is_root() {
330330
tracing::debug!("Not root, assuming no need to unshare");
@@ -354,6 +354,7 @@ pub(crate) async fn ensure_self_unshared_mount_namespace() -> Result<()> {
354354
/// TODO drain this and the above into SysrootLock
355355
#[context("Acquiring sysroot")]
356356
pub(crate) async fn get_locked_sysroot() -> Result<ostree_ext::sysroot::SysrootLock> {
357+
prepare_for_write()?;
357358
let sysroot = ostree::Sysroot::new_default();
358359
sysroot.set_mount_namespace_in_use();
359360
let sysroot = ostree_ext::sysroot::SysrootLock::new_from_sysroot(&sysroot).await?;
@@ -375,8 +376,21 @@ pub(crate) fn require_root() -> Result<()> {
375376
}
376377

377378
/// A few process changes that need to be made for writing.
379+
/// IMPORTANT: This may end up re-executing the current process,
380+
/// so anything that happens before this should be idempotent.
378381
#[context("Preparing for write")]
379-
pub(crate) async fn prepare_for_write() -> Result<()> {
382+
fn prepare_for_write() -> Result<()> {
383+
use std::sync::atomic::{AtomicBool, Ordering};
384+
385+
// This is intending to give "at most once" semantics to this
386+
// function. We should never invoke this from multiple threads
387+
// at the same time, but verifying "on main thread" is messy.
388+
// Yes, using SeqCst is likely overkill, but there is nothing perf
389+
// sensitive about this.
390+
static ENTERED: AtomicBool = AtomicBool::new(false);
391+
if ENTERED.load(Ordering::SeqCst) {
392+
return Ok(());
393+
}
380394
if ostree_ext::container_utils::is_ostree_container()? {
381395
anyhow::bail!(
382396
"Detected container (ostree base); this command requires a booted host system."
@@ -389,17 +403,17 @@ pub(crate) async fn prepare_for_write() -> Result<()> {
389403
anyhow::bail!("This command requires an ostree-booted host system");
390404
}
391405
crate::cli::require_root()?;
392-
ensure_self_unshared_mount_namespace().await?;
406+
ensure_self_unshared_mount_namespace()?;
393407
if crate::lsm::selinux_enabled()? && !crate::lsm::selinux_ensure_install()? {
394408
tracing::warn!("Do not have install_t capabilities");
395409
}
410+
ENTERED.store(true, Ordering::SeqCst);
396411
Ok(())
397412
}
398413

399414
/// Implementation of the `bootc upgrade` CLI command.
400415
#[context("Upgrading")]
401416
async fn upgrade(opts: UpgradeOpts) -> Result<()> {
402-
prepare_for_write().await?;
403417
let sysroot = &get_locked_sysroot().await?;
404418
let repo = &sysroot.repo();
405419
let (booted_deployment, _deployments, host) =
@@ -539,7 +553,6 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
539553
return Ok(());
540554
}
541555

542-
prepare_for_write().await?;
543556
let cancellable = gio::Cancellable::NONE;
544557

545558
let sysroot = &get_locked_sysroot().await?;
@@ -585,15 +598,13 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
585598
/// Implementation of the `bootc rollback` CLI command.
586599
#[context("Rollback")]
587600
async fn rollback(_opts: RollbackOpts) -> Result<()> {
588-
prepare_for_write().await?;
589601
let sysroot = &get_locked_sysroot().await?;
590602
crate::deploy::rollback(sysroot).await
591603
}
592604

593605
/// Implementation of the `bootc edit` CLI command.
594606
#[context("Editing spec")]
595607
async fn edit(opts: EditOpts) -> Result<()> {
596-
prepare_for_write().await?;
597608
let sysroot = &get_locked_sysroot().await?;
598609
let (booted_deployment, _deployments, host) =
599610
crate::status::get_status_require_booted(sysroot)?;

lib/src/install.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1138,7 +1138,7 @@ async fn prepare_install(
11381138
// Even though we require running in a container, the mounts we create should be specific
11391139
// to this process, so let's enter a private mountns to avoid leaking them.
11401140
if !external_source && std::env::var_os("BOOTC_SKIP_UNSHARE").is_none() {
1141-
super::cli::ensure_self_unshared_mount_namespace().await?;
1141+
super::cli::ensure_self_unshared_mount_namespace()?;
11421142
}
11431143

11441144
setup_sys_mount("efivarfs", EFIVARFS)?;

lib/src/status.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,6 @@ pub(crate) async fn status(opts: super::cli::StatusOpts) -> Result<()> {
305305
let host = if !Utf8Path::new("/run/ostree-booted").try_exists()? {
306306
Default::default()
307307
} else {
308-
crate::cli::prepare_for_write().await?;
309308
let sysroot = super::cli::get_locked_sysroot().await?;
310309
let booted_deployment = sysroot.booted_deployment();
311310
let (_deployments, host) = get_status(&sysroot, booted_deployment.as_ref())?;

0 commit comments

Comments
 (0)