Skip to content

Commit 22d068a

Browse files
committed
oci: Add containers-storage integration with zero-copy import
Implement the containers-storage import path (cstor module) which can import OCI images directly from podman/buildah storage without going through skopeo, using reflinks or hardlinks to avoid data copies when the composefs repo is on the same filesystem. Assisted-by: OpenCode (Claude Opus 4) Signed-off-by: Colin Walters <walters@verbum.org>
1 parent 1f5d04e commit 22d068a

16 files changed

Lines changed: 2314 additions & 23 deletions

File tree

crates/cfsctl/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ version.workspace = true
1414
path = "src/lib.rs"
1515

1616
[features]
17-
default = ['pre-6.15', 'oci']
17+
default = ['pre-6.15', 'oci', 'containers-storage']
1818
http = ['composefs-http']
1919
oci = ['composefs-oci']
20+
containers-storage = ['composefs-oci/containers-storage', 'cstorage']
2021
rhel9 = ['composefs/rhel9']
2122
'pre-6.15' = ['composefs/pre-6.15']
2223

@@ -29,8 +30,10 @@ composefs = { workspace = true }
2930
composefs-boot = { workspace = true }
3031
composefs-oci = { workspace = true, optional = true, features = ["boot"] }
3132
composefs-http = { workspace = true, optional = true }
33+
cstorage = { path = "../cstorage", version = "0.3.0", features = ["userns-helper"], optional = true }
3234
env_logger = { version = "0.11.0", default-features = false }
3335
hex = { version = "0.4.0", default-features = false }
36+
indicatif = { version = "0.17.0", default-features = false }
3437
rustix = { version = "1.0.0", default-features = false, features = ["fs", "process"] }
3538
serde = { version = "1.0", default-features = false, features = ["derive"] }
3639
serde_json = { version = "1.0", default-features = false, features = ["std"] }

crates/cfsctl/src/lib.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -928,20 +928,15 @@ where
928928
} => {
929929
// If no explicit name provided, use the image reference as the tag
930930
let tag_name = name.as_deref().unwrap_or(image);
931-
let (result, stats) =
932-
composefs_oci::pull_image(&repo, image, Some(tag_name), None).await?;
931+
932+
let result =
933+
composefs_oci::pull(&repo, image, Some(tag_name), Default::default()).await?;
933934

934935
println!("manifest {}", result.manifest_digest);
935936
println!("config {}", result.config_digest);
936937
println!("verity {}", result.manifest_verity.to_hex());
937938
println!("tagged {tag_name}");
938-
println!(
939-
"objects {} copied, {} already present, {} bytes copied, {} bytes inlined",
940-
stats.objects_copied,
941-
stats.objects_already_present,
942-
stats.bytes_copied,
943-
stats.bytes_inlined,
944-
);
939+
println!("objects {}", result.stats);
945940

946941
if bootable {
947942
let image_verity =

crates/cfsctl/src/main.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,20 @@ use cfsctl::App;
99
use anyhow::Result;
1010
use clap::Parser;
1111

12-
#[tokio::main]
13-
async fn main() -> Result<()> {
12+
fn main() -> Result<()> {
13+
// If we were spawned as a userns helper process, handle that and exit.
14+
// This MUST be called before the tokio runtime is created.
15+
#[cfg(feature = "containers-storage")]
16+
cstorage::init_if_helper();
17+
18+
// Now we can create the tokio runtime for the main application
19+
tokio::runtime::Builder::new_multi_thread()
20+
.enable_all()
21+
.build()?
22+
.block_on(async_main())
23+
}
24+
25+
async fn async_main() -> Result<()> {
1426
env_logger::init();
1527

1628
let args = App::parse();

crates/composefs-oci/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,21 @@ rust-version.workspace = true
1111
version.workspace = true
1212

1313
[features]
14+
default = ["containers-storage"]
1415
test = ["tar", "rand", "composefs/test"]
1516
boot = ["composefs-boot"]
17+
containers-storage = ["dep:cstorage", "dep:base64", "cstorage/userns-helper"]
1618

1719
[dependencies]
1820
anyhow = { version = "1.0.87", default-features = false }
1921
fn-error-context = "0.2"
2022
async-compression = { version = "0.4.0", default-features = false, features = ["tokio", "zstd", "gzip"] }
23+
base64 = { version = "0.22", default-features = false, features = ["std"], optional = true }
2124
bytes = { version = "1", default-features = false }
2225
composefs = { workspace = true }
2326
composefs-boot = { workspace = true, optional = true }
2427
containers-image-proxy = { version = "0.9.2", default-features = false }
28+
cstorage = { path = "../cstorage", version = "0.3.0", optional = true }
2529
hex = { version = "0.4.0", default-features = false }
2630
indicatif = { version = "0.18.0", default-features = false, features = ["tokio"] }
2731
rustix = { version = "1.0.0", features = ["fs"] }
@@ -34,6 +38,7 @@ tar = { version = "0.4.38", default-features = false, optional = true }
3438
tar-core = "0.1.0"
3539
tokio = { version = "1.24.2", features = ["macros", "rt-multi-thread"] }
3640
tokio-util = { version = "0.7", default-features = false, features = ["io"] }
41+
tracing = { version = "0.1", default-features = false }
3742

3843
[dev-dependencies]
3944
cap-std = { version = "4.0.0", default-features = false }

0 commit comments

Comments
 (0)