Skip to content

Commit

Permalink
Merge pull request ostreedev#314 from cgwalters/drop-openat
Browse files Browse the repository at this point in the history
ocidir: Port to cap-std, drop openat
  • Loading branch information
jmarrero committed Jun 24, 2022
2 parents ae205bd + 290ebfa commit 571a064
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 39 deletions.
3 changes: 1 addition & 2 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ camino = "1.0.4"
chrono = "0.4.19"
cjson = "0.1.1"
cap-std-ext = ">= 0.24, <= 0.25"
cap-tempfile = "0.24"
flate2 = { features = ["zlib"], default_features = false, version = "1.0.20" }
fn-error-context = "0.2.0"
futures-util = "0.3.13"
Expand All @@ -28,8 +29,6 @@ io-lifetimes = "0.5"
once_cell = "1.9"
libc = "0.2.92"
oci-spec = "0.5.4"
openat = "0.1.20"
openat-ext = "0.2.0"
openssl = "0.10.33"
ostree = { features = ["v2021_5", "cap-std-apis"], version = "0.14.0" }
pin-project = "1.0"
Expand Down
7 changes: 4 additions & 3 deletions lib/src/container/encapsulate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use crate::chunking::{Chunking, ObjectMetaSized};
use crate::container::skopeo;
use crate::tar as ostree_tar;
use anyhow::{anyhow, Context, Result};
use cap_std::fs::Dir;
use cap_std_ext::cap_std;
use fn_error_context::context;
use gio::glib;
use oci_spec::image as oci_image;
Expand All @@ -15,7 +17,6 @@ use std::borrow::Cow;
use std::collections::{BTreeMap, HashMap};
use std::num::NonZeroU32;
use std::path::Path;
use std::rc::Rc;
use tracing::instrument;

/// Annotation injected into the layer to say that this is an ostree commit.
Expand Down Expand Up @@ -125,8 +126,8 @@ fn build_oci(
) -> Result<ImageReference> {
// Explicitly error if the target exists
std::fs::create_dir(ocidir_path).context("Creating OCI dir")?;
let ocidir = Rc::new(openat::Dir::open(ocidir_path)?);
let mut writer = ocidir::OciDir::create(ocidir)?;
let ocidir = Dir::open_ambient_dir(ocidir_path, cap_std::ambient_authority())?;
let mut writer = ocidir::OciDir::create(&ocidir)?;

let commit = repo.require_rev(rev)?;
let commit = commit.as_str();
Expand Down
69 changes: 41 additions & 28 deletions lib/src/container/ocidir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@

use anyhow::{anyhow, Context, Result};
use camino::Utf8Path;
use cap_std::fs::Dir;
use cap_std_ext::cap_std;
use cap_std_ext::dirext::CapStdExtDirExt;
use flate2::write::GzEncoder;
use fn_error_context::context;
use oci_image::MediaType;
use oci_spec::image::{self as oci_image, Descriptor};
use openat_ext::*;
use openssl::hash::{Hasher, MessageDigest};
use std::collections::HashMap;
use std::fs::File;
use std::io::{prelude::*, BufReader};
use std::os::unix::fs::DirBuilderExt;
use std::path::{Path, PathBuf};
use std::rc::Rc;

/// Path inside an OCI directory to the blobs
const BLOBDIR: &str = "blobs/sha256";
Expand Down Expand Up @@ -53,7 +55,7 @@ impl Layer {
/// Create an OCI blob.
pub(crate) struct BlobWriter<'a> {
pub(crate) hash: Hasher,
pub(crate) target: Option<FileWriter<'a>>,
pub(crate) target: Option<cap_tempfile::TempFile<'a>>,
size: u64,
}

Expand All @@ -65,13 +67,13 @@ pub(crate) struct RawLayerWriter<'a> {
}

pub(crate) struct OciDir {
pub(crate) dir: Rc<openat::Dir>,
pub(crate) dir: std::sync::Arc<Dir>,
}

/// Write a serializable data (JSON) as an OCI blob
#[context("Writing json blob")]
pub(crate) fn write_json_blob<S: serde::Serialize>(
ocidir: &openat::Dir,
ocidir: &Dir,
v: &S,
media_type: oci_image::MediaType,
) -> Result<oci_image::DescriptorBuilder> {
Expand Down Expand Up @@ -111,29 +113,33 @@ pub(crate) fn new_empty_manifest() -> oci_image::ImageManifestBuilder {

impl OciDir {
/// Create a new, empty OCI directory at the target path, which should be empty.
pub(crate) fn create(dir: impl Into<Rc<openat::Dir>>) -> Result<Self> {
let dir = dir.into();
dir.ensure_dir_all(BLOBDIR, 0o755)?;
dir.write_file_contents("oci-layout", 0o644, r#"{"imageLayoutVersion":"1.0.0"}"#)?;
pub(crate) fn create(dir: &Dir) -> Result<Self> {
let mut db = cap_std::fs::DirBuilder::new();
db.recursive(true).mode(0o755);
dir.ensure_dir_with(BLOBDIR, &db)?;
dir.atomic_write("oci-layout", r#"{"imageLayoutVersion":"1.0.0"}"#)?;
Self::open(dir)
}

/// Clone an OCI directory, using reflinks for blobs.
pub(crate) fn clone_to(&self, destdir: &openat::Dir, p: impl AsRef<Path>) -> Result<Self> {
pub(crate) fn clone_to(&self, destdir: &Dir, p: impl AsRef<Path>) -> Result<Self> {
let p = p.as_ref();
destdir.ensure_dir(p, 0o755)?;
let cloned = Self::create(destdir.sub_dir(p)?)?;
for blob in self.dir.list_dir(BLOBDIR)? {
destdir.create_dir(p)?;
let cloned = Self::create(&destdir.open_dir(p)?)?;
for blob in self.dir.read_dir(BLOBDIR)? {
let blob = blob?;
let path = Path::new(BLOBDIR).join(blob.file_name());
self.dir.copy_file_at(&path, destdir, &path)?;
let mut src = self.dir.open(&path).map(BufReader::new)?;
self.dir
.atomic_replace_with(&path, |w| std::io::copy(&mut src, w))?;
}
Ok(cloned)
}

/// Open an existing OCI directory.
pub(crate) fn open(dir: impl Into<Rc<openat::Dir>>) -> Result<Self> {
Ok(Self { dir: dir.into() })
pub(crate) fn open(dir: &Dir) -> Result<Self> {
let dir = std::sync::Arc::new(dir.try_clone()?);
Ok(Self { dir })
}

/// Create a writer for a new blob (expected to be a tar stream)
Expand Down Expand Up @@ -211,7 +217,10 @@ impl OciDir {

pub(crate) fn read_blob(&self, desc: &oci_spec::image::Descriptor) -> Result<File> {
let path = Self::parse_descriptor_to_path(desc)?;
self.dir.open_file(&path).map_err(Into::into)
self.dir
.open(&path)
.map_err(Into::into)
.map(|f| f.into_std())
}

/// Read a JSON blob.
Expand Down Expand Up @@ -250,7 +259,7 @@ impl OciDir {
.build()
.unwrap();
self.dir
.write_file_with("index.json", 0o644, |w| -> Result<()> {
.atomic_replace_with("index.json", |w| -> Result<()> {
cjson::to_writer(w, &index_data).map_err(|e| anyhow::anyhow!("{:?}", e))?;
Ok(())
})?;
Expand All @@ -268,7 +277,7 @@ impl OciDir {
) -> Result<(oci_image::ImageManifest, Descriptor)> {
let f = self
.dir
.open_file("index.json")
.open("index.json")
.context("Failed to open index.json")?;
let idx: oci_image::ImageIndex = serde_json::from_reader(BufReader::new(f))?;
let desc = match idx.manifests().as_slice() {
Expand All @@ -282,11 +291,11 @@ impl OciDir {

impl<'a> BlobWriter<'a> {
#[context("Creating blob writer")]
fn new(ocidir: &'a openat::Dir) -> Result<Self> {
fn new(ocidir: &'a Dir) -> Result<Self> {
Ok(Self {
hash: Hasher::new(MessageDigest::sha256())?,
// FIXME add ability to choose filename after completion
target: Some(ocidir.new_file_writer(0o644)?),
target: Some(cap_tempfile::TempFile::new(ocidir)?),
size: 0,
})
}
Expand All @@ -295,8 +304,9 @@ impl<'a> BlobWriter<'a> {
/// Finish writing this blob object.
pub(crate) fn complete(mut self) -> Result<Blob> {
let sha256 = hex::encode(self.hash.finish()?);
let target = &format!("{}/{}", BLOBDIR, sha256);
self.target.take().unwrap().complete(target)?;
let destname = &format!("{}/{}", BLOBDIR, sha256);
let target = self.target.take().unwrap();
target.replace(destname)?;
Ok(Blob {
sha256,
size: self.size,
Expand All @@ -307,7 +317,11 @@ impl<'a> BlobWriter<'a> {
impl<'a> std::io::Write for BlobWriter<'a> {
fn write(&mut self, srcbuf: &[u8]) -> std::io::Result<usize> {
self.hash.update(srcbuf)?;
self.target.as_mut().unwrap().writer.write_all(srcbuf)?;
self.target
.as_mut()
.unwrap()
.as_file_mut()
.write_all(srcbuf)?;
self.size += srcbuf.len() as u64;
Ok(srcbuf.len())
}
Expand All @@ -319,7 +333,7 @@ impl<'a> std::io::Write for BlobWriter<'a> {

impl<'a> RawLayerWriter<'a> {
/// Create a writer for a gzip compressed layer blob.
fn new(ocidir: &'a openat::Dir, c: Option<flate2::Compression>) -> Result<Self> {
fn new(ocidir: &'a Dir, c: Option<flate2::Compression>) -> Result<Self> {
let bw = BlobWriter::new(ocidir)?;
Ok(Self {
bw,
Expand Down Expand Up @@ -400,9 +414,8 @@ mod tests {

#[test]
fn test_build() -> Result<()> {
let td = tempfile::tempdir()?;
let td = openat::Dir::open(td.path())?;
let w = OciDir::create(td)?;
let td = cap_tempfile::tempdir(cap_std::ambient_authority())?;
let w = OciDir::create(&td)?;
let mut layerw = w.create_raw_layer(None)?;
layerw.write_all(b"pretend this is a tarball")?;
let root_layer = layerw.complete()?;
Expand Down
8 changes: 5 additions & 3 deletions lib/src/container/update_detachedmeta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ use crate::container::{ocidir, skopeo};
use crate::container::{store as container_store, Transport};
use anyhow::{anyhow, Context, Result};
use camino::Utf8Path;
use cap_std::fs::Dir;
use cap_std_ext::cap_std;
use std::io::{BufReader, BufWriter};
use std::rc::Rc;

/// Given an OSTree container image reference, update the detached metadata (e.g. GPG signature)
/// while preserving all other container image metadata.
Expand Down Expand Up @@ -37,8 +38,9 @@ pub async fn update_detached_metadata(
// Fork a thread to do the heavy lifting of filtering the tar stream, rewriting the manifest/config.
crate::tokio_util::spawn_blocking_cancellable_flatten(move |cancellable| {
// Open the temporary OCI directory.
let tempsrc = Rc::new(openat::Dir::open(tempsrc_ref_path).context("Opening src")?);
let tempsrc = ocidir::OciDir::open(tempsrc)?;
let tempsrc = Dir::open_ambient_dir(tempsrc_ref_path, cap_std::ambient_authority())
.context("Opening src")?;
let tempsrc = ocidir::OciDir::open(&tempsrc)?;

// Load the manifest, platform, and config
let (mut manifest, manifest_descriptor) = tempsrc
Expand Down
7 changes: 4 additions & 3 deletions lib/src/integrationtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use std::path::Path;
use crate::container::ocidir;
use anyhow::Result;
use camino::Utf8Path;
use cap_std::fs::Dir;
use cap_std_ext::cap_std;
use fn_error_context::context;
use gio::prelude::*;
use oci_spec::image as oci_image;
Expand All @@ -27,10 +29,9 @@ pub(crate) fn detectenv() -> &'static str {
/// Should only be enabled for testing.
#[context("Generating derived oci")]
pub fn generate_derived_oci(src: impl AsRef<Utf8Path>, dir: impl AsRef<Utf8Path>) -> Result<()> {
use std::rc::Rc;
let src = src.as_ref();
let src = Rc::new(openat::Dir::open(src.as_std_path())?);
let src = ocidir::OciDir::open(src)?;
let src = Dir::open_ambient_dir(src, cap_std::ambient_authority())?;
let src = ocidir::OciDir::open(&src)?;
let dir = dir.as_ref();
let mut manifest = src.read_manifest()?;
let mut config: oci_spec::image::ImageConfiguration = src.read_json_blob(manifest.config())?;
Expand Down

0 comments on commit 571a064

Please sign in to comment.