@@ -31,6 +31,7 @@ use ostree_ext::prelude::Cast;
31
31
use serde:: { Deserialize , Serialize } ;
32
32
33
33
use self :: baseline:: InstallBlockDeviceOpts ;
34
+ use crate :: containerenv:: ContainerExecutionInfo ;
34
35
use crate :: lsm:: lsm_label;
35
36
use crate :: task:: Task ;
36
37
use crate :: utils:: run_in_host_mountns;
@@ -158,17 +159,28 @@ pub(crate) struct InstallToFilesystemOpts {
158
159
pub ( crate ) config_opts : InstallConfigOpts ,
159
160
}
160
161
161
- // Shared read-only global state
162
- pub ( crate ) struct State {
162
+ /// Global state captured from the container.
163
+ #[ derive( Debug , Clone ) ]
164
+ pub ( crate ) struct SourceInfo {
163
165
/// Image reference we'll pull from (today always containers-storage: type)
164
- source_imageref : ostree_container:: ImageReference ,
166
+ pub ( crate ) imageref : ostree_container:: ImageReference ,
165
167
/// The digest to use for pulls
166
- source_digest : String ,
168
+ pub ( crate ) digest : String ,
169
+ /// The embedded base OSTree commit checksum
170
+ #[ allow( dead_code) ]
171
+ pub ( crate ) commit : String ,
172
+ /// Whether or not SELinux appears to be enabled in the source commit
173
+ pub ( crate ) selinux : bool ,
174
+ }
175
+
176
+ // Shared read-only global state
177
+ pub ( crate ) struct State {
178
+ pub ( crate ) source : SourceInfo ,
167
179
/// Force SELinux off in target system
168
- override_disable_selinux : bool ,
169
- config_opts : InstallConfigOpts ,
170
- target_opts : InstallTargetOpts ,
171
- install_config : config:: InstallConfiguration ,
180
+ pub ( crate ) override_disable_selinux : bool ,
181
+ pub ( crate ) config_opts : InstallConfigOpts ,
182
+ pub ( crate ) target_opts : InstallTargetOpts ,
183
+ pub ( crate ) install_config : config:: InstallConfiguration ,
172
184
}
173
185
174
186
/// Path to initially deployed version information
@@ -261,6 +273,45 @@ impl FromStr for MountSpec {
261
273
}
262
274
}
263
275
276
+ impl SourceInfo {
277
+ // Inspect container information and convert it to an ostree image reference
278
+ // that pulls from containers-storage.
279
+ #[ context( "Gathering source info from container env" ) ]
280
+ pub ( crate ) fn from_container ( container_info : & ContainerExecutionInfo ) -> Result < Self > {
281
+ if !container_info. engine . starts_with ( "podman" ) {
282
+ anyhow:: bail!( "Currently this command only supports being executed via podman" ) ;
283
+ }
284
+ if container_info. imageid . is_empty ( ) {
285
+ anyhow:: bail!( "Invalid empty imageid" ) ;
286
+ }
287
+ let imageref = ostree_container:: ImageReference {
288
+ transport : ostree_container:: Transport :: ContainerStorage ,
289
+ name : container_info. image . clone ( ) ,
290
+ } ;
291
+ let digest = crate :: podman:: imageid_to_digest ( & container_info. imageid ) ?;
292
+ let cancellable = ostree:: gio:: Cancellable :: NONE ;
293
+ let commit = Task :: new ( "Reading ostree commit" , "ostree" )
294
+ . args ( [ "--repo=/ostree/repo" , "rev-parse" , "--single" ] )
295
+ . quiet ( )
296
+ . read ( ) ?;
297
+ let root = cap_std:: fs:: Dir :: open_ambient_dir ( "/" , cap_std:: ambient_authority ( ) ) ?;
298
+ let repo = ostree:: Repo :: open_at_dir ( & root, "ostree/repo" ) ?;
299
+ let root = repo
300
+ . read_commit ( commit. trim ( ) , cancellable)
301
+ . context ( "Reading commit" ) ?
302
+ . 0 ;
303
+ let root = root. downcast_ref :: < ostree:: RepoFile > ( ) . unwrap ( ) ;
304
+ let xattrs = root. xattrs ( cancellable) ?;
305
+ let selinux = crate :: lsm:: xattrs_have_selinux ( & xattrs) ;
306
+ Ok ( Self {
307
+ imageref,
308
+ digest,
309
+ commit,
310
+ selinux,
311
+ } )
312
+ }
313
+ }
314
+
264
315
mod config {
265
316
use super :: * ;
266
317
@@ -379,7 +430,7 @@ async fn initialize_ostree_root_from_self(
379
430
} else {
380
431
ostree_container:: OstreeImageReference {
381
432
sigverify : target_sigverify,
382
- imgref : state. source_imageref . clone ( ) ,
433
+ imgref : state. source . imageref . clone ( ) ,
383
434
}
384
435
} ;
385
436
@@ -420,15 +471,15 @@ async fn initialize_ostree_root_from_self(
420
471
let src_imageref = if skopeo_supports_containers_storage ( ) ? {
421
472
// We always use exactly the digest of the running image to ensure predictability.
422
473
let spec =
423
- crate :: utils:: digested_pullspec ( & state. source_imageref . name , & state. source_digest ) ;
474
+ crate :: utils:: digested_pullspec ( & state. source . imageref . name , & state. source . digest ) ;
424
475
ostree_container:: ImageReference {
425
476
transport : ostree_container:: Transport :: ContainerStorage ,
426
477
name : spec,
427
478
}
428
479
} else {
429
480
let td = tempfile:: tempdir_in ( "/var/tmp" ) ?;
430
481
let path: & Utf8Path = td. path ( ) . try_into ( ) . unwrap ( ) ;
431
- let r = copy_to_oci ( & state. source_imageref , path) ?;
482
+ let r = copy_to_oci ( & state. source . imageref , path) ?;
432
483
temporary_dir = Some ( td) ;
433
484
r
434
485
} ;
@@ -562,39 +613,12 @@ impl RootSetup {
562
613
}
563
614
}
564
615
565
- pub ( crate ) struct SourceData {
566
- /// The embedded base OSTree commit checksum
567
- #[ allow( dead_code) ]
568
- pub ( crate ) commit : String ,
569
- /// Whether or not SELinux appears to be enabled in the source commit
570
- pub ( crate ) selinux : bool ,
571
- }
572
-
573
- #[ context( "Gathering source data" ) ]
574
- fn gather_source_data ( ) -> Result < SourceData > {
575
- let cancellable = ostree:: gio:: Cancellable :: NONE ;
576
- let commit = Task :: new ( "Reading ostree commit" , "ostree" )
577
- . args ( [ "--repo=/ostree/repo" , "rev-parse" , "--single" ] )
578
- . quiet ( )
579
- . read ( ) ?;
580
- let root = cap_std:: fs:: Dir :: open_ambient_dir ( "/" , cap_std:: ambient_authority ( ) ) ?;
581
- let repo = ostree:: Repo :: open_at_dir ( & root, "ostree/repo" ) ?;
582
- let root = repo
583
- . read_commit ( commit. trim ( ) , cancellable)
584
- . context ( "Reading commit" ) ?
585
- . 0 ;
586
- let root = root. downcast_ref :: < ostree:: RepoFile > ( ) . unwrap ( ) ;
587
- let xattrs = root. xattrs ( cancellable) ?;
588
- let selinux = crate :: lsm:: xattrs_have_selinux ( & xattrs) ;
589
- Ok ( SourceData { commit, selinux } )
590
- }
591
-
592
616
/// If we detect that the target ostree commit has SELinux labels,
593
617
/// and we aren't passed an override to disable it, then ensure
594
618
/// the running process is labeled with install_t so it can
595
619
/// write arbitrary labels.
596
620
pub ( crate ) fn reexecute_self_for_selinux_if_needed (
597
- srcdata : & SourceData ,
621
+ srcdata : & SourceInfo ,
598
622
override_disable_selinux : bool ,
599
623
) -> Result < bool > {
600
624
let mut ret_did_override = false ;
@@ -646,13 +670,7 @@ pub(crate) fn finalize_filesystem(fs: &Utf8Path) -> Result<()> {
646
670
Ok ( ( ) )
647
671
}
648
672
649
- /// Preparation for an install; validates and prepares some (thereafter immutable) global state.
650
- async fn prepare_install (
651
- config_opts : InstallConfigOpts ,
652
- target_opts : InstallTargetOpts ,
653
- ) -> Result < Arc < State > > {
654
- // We need full root privileges, i.e. --privileged in podman
655
- crate :: cli:: require_root ( ) ?;
673
+ fn require_systemd_pid1 ( ) -> Result < ( ) > {
656
674
// We require --pid=host
657
675
let pid = std:: fs:: read_link ( "/proc/1/exe" ) . context ( "reading /proc/1/exe" ) ?;
658
676
let pid = pid
@@ -661,21 +679,21 @@ async fn prepare_install(
661
679
if !pid. contains ( "systemd" ) {
662
680
anyhow:: bail!( "This command must be run with --pid=host" )
663
681
}
682
+ Ok ( ( ) )
683
+ }
684
+
685
+ /// Preparation for an install; validates and prepares some (thereafter immutable) global state.
686
+ async fn prepare_install (
687
+ config_opts : InstallConfigOpts ,
688
+ target_opts : InstallTargetOpts ,
689
+ ) -> Result < Arc < State > > {
690
+ // We need full root privileges, i.e. --privileged in podman
691
+ crate :: cli:: require_root ( ) ?;
692
+ require_systemd_pid1 ( ) ?;
664
693
665
694
// This command currently *must* be run inside a privileged container.
666
695
let container_info = crate :: containerenv:: get_container_execution_info ( ) ?;
667
- if !container_info. engine . starts_with ( "podman" ) {
668
- anyhow:: bail!( "Currently this command only supports being executed via podman" ) ;
669
- }
670
- if container_info. imageid . is_empty ( ) {
671
- anyhow:: bail!( "Invalid empty imageid" ) ;
672
- }
673
- let source_imageref = ostree_container:: ImageReference {
674
- transport : ostree_container:: Transport :: ContainerStorage ,
675
- name : container_info. image . clone ( ) ,
676
- } ;
677
- // Find the exact digested image we are running
678
- let source_digest = crate :: podman:: imageid_to_digest ( & container_info. imageid ) ?;
696
+ let source = SourceInfo :: from_container ( & container_info) ?;
679
697
680
698
// Even though we require running in a container, the mounts we create should be specific
681
699
// to this process, so let's enter a private mountns to avoid leaking them.
@@ -693,9 +711,8 @@ async fn prepare_install(
693
711
}
694
712
695
713
// Now, deal with SELinux state.
696
- let srcdata = gather_source_data ( ) ?;
697
714
let override_disable_selinux =
698
- reexecute_self_for_selinux_if_needed ( & srcdata , config_opts. disable_selinux ) ?;
715
+ reexecute_self_for_selinux_if_needed ( & source , config_opts. disable_selinux ) ?;
699
716
700
717
let install_config = config:: load_config ( ) ?;
701
718
@@ -706,8 +723,7 @@ async fn prepare_install(
706
723
bind_mount_from_host ( "/var/tmp" , "/var/tmp" ) ?;
707
724
let state = Arc :: new ( State {
708
725
override_disable_selinux,
709
- source_imageref,
710
- source_digest,
726
+ source,
711
727
config_opts,
712
728
target_opts,
713
729
install_config,
0 commit comments