Skip to content

Add support for creating ostree commits#343

Merged
alexlarsson merged 5 commits into
mainfrom
ostree-commit
Jul 2, 2026
Merged

Add support for creating ostree commits#343
alexlarsson merged 5 commits into
mainfrom
ostree-commit

Conversation

@alexlarsson

@alexlarsson alexlarsson commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

This lets us create a local ostree commit in the composefs repo based on an Filesystem (which could come from an image, which in turn could come from an oci image).

This lets us integrate composefs and ostree easily, as mentioned in #141

Replace the lossy `Vec<(String, String)>` metadata representation
with `Vec<(String, MetadataValue)>` where MetadataValue is a typed
enum covering the common GVariant types used in ostree commit
metadata (s, b, u, t, as, ay) with a raw `Owned<Variant>` fallback
for unrecognized types.

This preserves type information needed for serialization (to compute
correct checksums when generating commits), while the Display impl
reproduces the same output as before for inspect/display purposes.

Signed-off-by: Alexander Larsson <alexl@redhat.com>
Add serialize() methods to OstreeDirMeta, OstreeDirTree, and
OstreeCommit, using gv!().serialize_to_vec() — the same pattern
already used by OstreeFileHeader.

Add SerializeTo<Variant> for MetadataValue so OstreeCommit can
serialize its a{sv} metadata dict with correct variant type tags,
using VariantWrap for typed values and raw Variant passthrough for
the Other fallback.

Signed-off-by: Alexander Larsson <alexl@redhat.com>

@Johan-Liebert1 Johan-Liebert1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic looks okay at a first glance. Some comments

Comment thread crates/composefs-ostree/src/commit.rs Outdated
Comment thread crates/composefs-ostree/src/commit.rs Outdated
) -> Result<Sha256Digest> {
let leaf = fs.leaf(leaf_id);

let (symlink_target, content_owned, external_obj) = match &leaf.content {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about making symlink_target Option<String>?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will eventually end up in OstreeFileHeader.symlink_target, which is kind of a direct mapping of an ostree GVariant where the format for nothing is "", because gvariant doesn't have optional types. We could introduce one in the rust mapping, but I'm not sure its worth that.

Comment thread crates/composefs-ostree/src/commit.rs
Comment thread crates/composefs-ostree/src/commit.rs Outdated
Comment thread crates/composefs-ostree/src/commit.rs Outdated
gid: leaf.stat.st_gid,
mode: (leaf.stat.st_mode & !S_IFMT) | file_type_bits,
symlink_target,
xattrs: stat_xattrs_to_vec(&leaf.stat),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not strictly related, as it works correctly if your Filesystem has the correct xattrs (i.e. matching on-disk format for selinux labels), which they would if you created one from an actual filesystem. However, given that existing code like selabel() gets this wrong I added some canonicalization to avoid running into this again.

Add CommitWriter::from_filesystem() which walks a FileSystem tree
bottom-up, producing ostree dirtree/dirmeta/file objects with correct
checksums.  Hardlinks are handled via a LeafId cache so shared leaves
produce the same ostree checksum.

The public commit_filesystem() function wraps this with commit
metadata (subject, timestamp, ostree.ref-binding), serializes the
splitstream, and generates the linked EROFS image.

This enables creating ostree commits from any composefs FileSystem,
regardless of its origin (OCI image, local directory, etc.).

Signed-off-by: Alexander Larsson <alexl@redhat.com>
Validates that commiting an image created by composefs-ostree return
the identical ostree commit id.

Signed-off-by: Alexander Larsson <alexl@redhat.com>
Creates an ostree commit from an existing composefs image in the
repository.  The image is specified by its object ID or refs/ name.

Example workflow to create an ostree commit from an OCI image:

  IMAGE_ID=$(cfsctl oci compute-id myimage:latest)
  cfsctl ostree commit $IMAGE_ID --reference fedora/40/x86_64

Signed-off-by: Alexander Larsson <alexl@redhat.com>
@alexlarsson alexlarsson added this pull request to the merge queue Jul 2, 2026
Merged via the queue into main with commit d7e6ea9 Jul 2, 2026
20 checks passed
RegularFile::External(obj_id, _) | RegularFile::ExternalNoVerity(obj_id, _),
) => Some(repo.read_object(obj_id)?),
LeafContent::Regular(RegularFile::Sparse(size)) => {
Some(vec![0u8; *size as usize])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't happen - honestly I'd rather just error out on sparse files in this case because again they're just a leftover bogosity from the composefs-c project I think.

But, if we're going ot handle them we should not potentially heap allocate gigabytes of zeroes...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// Use [`ostree::CommitMetadata::default()`] for a minimal commit, the
/// builder methods to set fields, or [`ostree::OstreeCommit::commit_metadata()`]
/// to round-trip an existing commit.
pub fn commit_filesystem<ObjectID: FsVerityHashValue>(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The challenge I see here is that now composefs holds the GC root for the ostree commit...and actually since we aren't writing to an ostree repo at all, I am not sure how we'd sanely do in-place upgrades.

IOW a goal of the bootc unified storage is that (for compatibility) every aspect of ostree continues to work unchanged (including e.g. rpm-ostree package layering), we just share storage. ostree handles GC on its side, etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you worried about a gc in the ostree repo removing a commit, and but the ostree commit (as well as the oci i guess) is not GC:ed in the composefs repo?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants