@@ -324,7 +324,7 @@ pub(crate) enum Opt {
324
324
/// TODO use https://github.com/ostreedev/ostree/pull/2779 once
325
325
/// we can depend on a new enough ostree
326
326
#[ context( "Ensuring mountns" ) ]
327
- pub ( crate ) async fn ensure_self_unshared_mount_namespace ( ) -> Result < ( ) > {
327
+ pub ( crate ) fn ensure_self_unshared_mount_namespace ( ) -> Result < ( ) > {
328
328
let uid = rustix:: process:: getuid ( ) ;
329
329
if !uid. is_root ( ) {
330
330
tracing:: debug!( "Not root, assuming no need to unshare" ) ;
@@ -354,6 +354,7 @@ pub(crate) async fn ensure_self_unshared_mount_namespace() -> Result<()> {
354
354
/// TODO drain this and the above into SysrootLock
355
355
#[ context( "Acquiring sysroot" ) ]
356
356
pub ( crate ) async fn get_locked_sysroot ( ) -> Result < ostree_ext:: sysroot:: SysrootLock > {
357
+ prepare_for_write ( ) ?;
357
358
let sysroot = ostree:: Sysroot :: new_default ( ) ;
358
359
sysroot. set_mount_namespace_in_use ( ) ;
359
360
let sysroot = ostree_ext:: sysroot:: SysrootLock :: new_from_sysroot ( & sysroot) . await ?;
@@ -375,8 +376,21 @@ pub(crate) fn require_root() -> Result<()> {
375
376
}
376
377
377
378
/// 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.
378
381
#[ 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
+ }
380
394
if ostree_ext:: container_utils:: is_ostree_container ( ) ? {
381
395
anyhow:: bail!(
382
396
"Detected container (ostree base); this command requires a booted host system."
@@ -389,17 +403,17 @@ pub(crate) async fn prepare_for_write() -> Result<()> {
389
403
anyhow:: bail!( "This command requires an ostree-booted host system" ) ;
390
404
}
391
405
crate :: cli:: require_root ( ) ?;
392
- ensure_self_unshared_mount_namespace ( ) . await ?;
406
+ ensure_self_unshared_mount_namespace ( ) ?;
393
407
if crate :: lsm:: selinux_enabled ( ) ? && !crate :: lsm:: selinux_ensure_install ( ) ? {
394
408
tracing:: warn!( "Do not have install_t capabilities" ) ;
395
409
}
410
+ ENTERED . store ( true , Ordering :: SeqCst ) ;
396
411
Ok ( ( ) )
397
412
}
398
413
399
414
/// Implementation of the `bootc upgrade` CLI command.
400
415
#[ context( "Upgrading" ) ]
401
416
async fn upgrade ( opts : UpgradeOpts ) -> Result < ( ) > {
402
- prepare_for_write ( ) . await ?;
403
417
let sysroot = & get_locked_sysroot ( ) . await ?;
404
418
let repo = & sysroot. repo ( ) ;
405
419
let ( booted_deployment, _deployments, host) =
@@ -539,7 +553,6 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
539
553
return Ok ( ( ) ) ;
540
554
}
541
555
542
- prepare_for_write ( ) . await ?;
543
556
let cancellable = gio:: Cancellable :: NONE ;
544
557
545
558
let sysroot = & get_locked_sysroot ( ) . await ?;
@@ -585,15 +598,13 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
585
598
/// Implementation of the `bootc rollback` CLI command.
586
599
#[ context( "Rollback" ) ]
587
600
async fn rollback ( _opts : RollbackOpts ) -> Result < ( ) > {
588
- prepare_for_write ( ) . await ?;
589
601
let sysroot = & get_locked_sysroot ( ) . await ?;
590
602
crate :: deploy:: rollback ( sysroot) . await
591
603
}
592
604
593
605
/// Implementation of the `bootc edit` CLI command.
594
606
#[ context( "Editing spec" ) ]
595
607
async fn edit ( opts : EditOpts ) -> Result < ( ) > {
596
- prepare_for_write ( ) . await ?;
597
608
let sysroot = & get_locked_sysroot ( ) . await ?;
598
609
let ( booted_deployment, _deployments, host) =
599
610
crate :: status:: get_status_require_booted ( sysroot) ?;
0 commit comments