Skip to content

Commit

Permalink
Store the empty descriptor in blobs/sha256 to avoid missing references
Browse files Browse the repository at this point in the history
Otherwise skopeo complains that it can't find the blob referenced by the
empty descriptor.

Signed-off-by: Ariel Miculas-Trif <[email protected]>
  • Loading branch information
ariel-miculas committed Sep 18, 2024
1 parent fe75024 commit 062e7cc
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 28 deletions.
46 changes: 27 additions & 19 deletions puzzlefs-lib/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::fsverity_helpers::{
};
use crate::oci::Digest;
use std::any::Any;
use std::backtrace::Backtrace;
use std::cmp::min;
use std::collections::{BTreeMap, HashMap};
use std::ffi::{OsStr, OsString};
Expand Down Expand Up @@ -399,7 +400,7 @@ pub fn build_initial_rootfs<C: Compression + Any>(
tag: &str,
) -> Result<Descriptor> {
let mut verity_data: VerityData = BTreeMap::new();
let mut image_manifest = Image::get_empty_manifest()?;
let mut image_manifest = oci.get_empty_manifest()?;
let inodes = build_delta::<C>(rootfs, oci, None, &mut verity_data, &mut image_manifest)?;

let rootfs_buf = serialize_metadata(Rootfs {
Expand Down Expand Up @@ -430,7 +431,7 @@ pub fn add_rootfs_delta<C: Compression + Any>(
base_layer: &str,
) -> Result<(Descriptor, Arc<Image>)> {
let mut verity_data: VerityData = BTreeMap::new();
let mut image_manifest = Image::get_empty_manifest()?;
let mut image_manifest = oci.get_empty_manifest()?;

let pfs = PuzzleFS::open(oci, base_layer, None)?;
let oci = Arc::clone(&pfs.oci);
Expand Down Expand Up @@ -462,11 +463,9 @@ pub fn add_rootfs_delta<C: Compression + Any>(
Ok((rootfs_descriptor, oci))
}

pub fn enable_fs_verity(oci: Image, tag: &str, manifest_root_hash: &str) -> Result<()> {
// first enable fs verity for the puzzlefs image manifest
let manifest_fd = oci.get_image_manifest_fd(tag)?;
fn enable_verity_for_file(file: &cap_std::fs::File) -> Result<()> {
if let Err(e) = fsverity_enable(
manifest_fd.as_raw_fd(),
file.as_raw_fd(),
FS_VERITY_BLOCK_SIZE_DEFAULT,
InnerHashAlgorithm::Sha256,
&[],
Expand All @@ -476,26 +475,35 @@ pub fn enable_fs_verity(oci: Image, tag: &str, manifest_root_hash: &str) -> Resu
return Err(WireFormatError::from(e));
}
}
check_fs_verity(&manifest_fd, &hex::decode(manifest_root_hash)?[..])?;
Ok(())
}

fn enable_and_check_verity_for_file(file: &cap_std::fs::File, expected: &[u8]) -> Result<()> {
enable_verity_for_file(file)?;
check_fs_verity(file, expected)
}

pub fn enable_fs_verity(oci: Image, tag: &str, manifest_root_hash: &str) -> Result<()> {
// first enable fs verity for the puzzlefs image manifest
let manifest_fd = oci.get_image_manifest_fd(tag)?;
enable_and_check_verity_for_file(&manifest_fd, &hex::decode(manifest_root_hash)?[..])?;

let pfs = PuzzleFS::open(oci, tag, None)?;
let oci = Arc::clone(&pfs.oci);
let rootfs = oci.open_rootfs_blob(tag, None)?;

let rootfs_fd = oci.get_pfs_rootfs(tag, None)?;
if let Err(e) = fsverity_enable(
rootfs_fd.as_raw_fd(),
FS_VERITY_BLOCK_SIZE_DEFAULT,
InnerHashAlgorithm::Sha256,
&[],
) {
// if fsverity is enabled, ignore the error
if e.kind() != std::io::ErrorKind::AlreadyExists {
return Err(WireFormatError::from(e));
}
}
let rootfs_verity = oci.get_pfs_rootfs_verity(tag)?;
check_fs_verity(&rootfs_fd, &rootfs_verity[..])?;

enable_and_check_verity_for_file(&rootfs_fd, &rootfs_verity[..])?;

let manifest = oci
.0
.find_manifest_with_tag(tag)?
.ok_or_else(|| WireFormatError::MissingManifest(tag.to_string(), Backtrace::capture()))?;
let config_digest = manifest.config().digest().digest();
let config_digest_path = oci.blob_path().join(config_digest);
enable_verity_for_file(&oci.0.dir.open(config_digest_path)?)?;

for (content_addressed_file, verity_hash) in rootfs.get_verity_data()? {
let file_path = oci
Expand Down
25 changes: 16 additions & 9 deletions puzzlefs-lib/src/oci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::any::Any;
use std::backtrace::Backtrace;
use std::fs;
use std::io;
use std::io::Write;
use std::io::{Read, Seek};
use std::path::{Path, PathBuf};

Expand Down Expand Up @@ -275,13 +276,8 @@ impl Image {
.ok_or_else(|| OciSpecError::Other("missing OCI index".to_string()))?)
}

pub fn get_empty_manifest() -> Result<ImageManifest> {
pub fn get_empty_manifest(&self) -> Result<ImageManifest> {
// see https://github.com/opencontainers/image-spec/blob/main/manifest.md#guidance-for-an-empty-descriptor
// TODO: write the empty blob to blobs/sha256, otherwise skopeo won't be able to copy the image
// the fs verity test will also need changes because currently it makes sure that verity is
// enabled for every blob in blobs/sha256; one thing we could do is enable verity but skip
// checking if the digests match or add the verity annotations for each descriptor; another
// way is to only check the verity data for the PuzzleFS rootfs and file chunks
let config = DescriptorBuilder::default()
.media_type(MediaType::EmptyJSON)
.size(2_u32)
Expand All @@ -290,6 +286,17 @@ impl Image {
)?)
.data("e30=")
.build()?;

if !self.0.dir.exists(
self.blob_path()
.join("44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"),
) {
let mut blob = self.0.create_blob()?;
blob.write_all("{}".as_bytes())?;
// TODO: blob.complete_verified_as(&config)? once https://github.com/containers/ocidir-rs/pull/18 is merged
blob.complete()?;
}

let image_manifest = ImageManifestBuilder::default()
.schema_version(2_u32)
.config(config)
Expand All @@ -310,8 +317,8 @@ mod tests {
#[test]
fn test_put_blob_correct_hash() -> anyhow::Result<()> {
let dir = tempdir()?;
let mut image_manifest = Image::get_empty_manifest()?;
let image: Image = Image::new(dir.path())?;
let mut image_manifest = image.get_empty_manifest()?;
let (desc, ..) = image.put_blob::<Noop>(
"meshuggah rocks".as_bytes(),
&mut image_manifest,
Expand Down Expand Up @@ -341,7 +348,7 @@ mod tests {
fn test_put_get_index() -> anyhow::Result<()> {
let dir = tempdir()?;
let image = Image::new(dir.path())?;
let mut image_manifest = Image::get_empty_manifest()?;
let mut image_manifest = image.get_empty_manifest()?;
let mut annotations = HashMap::new();
annotations.insert(ANNOTATION_REF_NAME.to_string(), "foo".to_string());
image_manifest.set_annotations(Some(annotations));
Expand All @@ -364,8 +371,8 @@ mod tests {
#[test]
fn double_put_ok() -> anyhow::Result<()> {
let dir = tempdir()?;
let mut image_manifest = Image::get_empty_manifest()?;
let image = Image::new(dir.path())?;
let mut image_manifest = image.get_empty_manifest()?;
let desc1 = image.put_blob::<DefaultCompression>(
"meshuggah rocks".as_bytes(),
&mut image_manifest,
Expand Down

0 comments on commit 062e7cc

Please sign in to comment.