Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ jobs:
options: "--privileged --pid=host -v /var/tmp:/var/tmp --tmpfs /tmp:rw,exec,nosuid,nodev -v /:/run/host"

steps:
- run: dnf -y install cargo clippy composefs-devel e2fsprogs just ostree rustfmt gcc-c++
- run: dnf -y install cargo clippy composefs-devel e2fsprogs fsverity-utils just ostree rustfmt gcc-c++
- name: Enable fs-verity on /
run: tune2fs -O verity $(findmnt -vno SOURCE /run/host)
- uses: actions/checkout@v7
- name: Run all checks (clippy, fmt, feature combos, tests)
run: env CFS_TEST_TMPDIR=/run/host/var/tmp just check
- name: Run mount.composefs tests
run: env TMPDIR=/run/host/var/tmp just test-mount-composefs

# Fast smoke test — catches basic breakage before spending time on
# container builds and VM boots. Runs only the unprivileged tests
Expand Down Expand Up @@ -88,6 +90,20 @@ jobs:
crates/composefs/fuzz/artifacts/
target/fuzz-logs/

# C API compatibility: builds the Rust cdylib, then runs the C
# composefs test suite (test-checksums, test-units) against it.
capi:
name: C API compatibility
needs: smoke
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: extractions/setup-just@v4
- name: Run C test suite against Rust libcomposefs
run: just test-capi

# Full integration tests: builds a bootc container image, runs all
# tests (both unprivileged and privileged). Privileged tests execute
# inside bcvk ephemeral VMs booted from the container image.
Expand Down Expand Up @@ -222,7 +238,7 @@ jobs:
# repository settings as the single required status check.
required-checks:
if: always()
needs: [nightly, fedora, smoke, fuzz, integration, examples]
needs: [nightly, fedora, smoke, fuzz, capi, integration, examples]
runs-on: ubuntu-latest
steps:
- run: exit 1
Expand All @@ -231,5 +247,6 @@ jobs:
needs.fedora.result != 'success' ||
needs.smoke.result != 'success' ||
needs.fuzz.result != 'success' ||
needs.capi.result != 'success' ||
needs.integration.result != 'success' ||
needs.examples.result != 'success'
9 changes: 9 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ test-integration *ARGS: build
cargo test -p composefs-integration-tests --test cfsctl-integration-tests -- --skip privileged_ {{ ARGS }}
fi

# Run mount.composefs shell tests (needs fsverity-utils; mount tests need root)
test-mount-composefs: build
crates/composefs-ctl/tests/test-mount-composefs.sh $(pwd)/target/debug/cfsctl

# Build the test container image for VM-based integration tests
_integration-container-build:
podman build --build-arg base_image={{base_image}} --build-arg cfsctl_features={{cfsctl_features}} -t {{_test_image}} .
Expand Down Expand Up @@ -141,6 +145,11 @@ generate-corpus:
fuzz-list:
cd crates/composefs && cargo +nightly fuzz list

# Test composefs-capi against the C composefs test suite in a container.
# Optionally pass the path to a local checkout of the C composefs repo.
test-capi *ARGS:
crates/composefs-capi/tests/test-capi-container.sh {{ARGS}}

# Clean build artifacts
clean:
cargo clean
36 changes: 36 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
PREFIX ?= /usr/local
DESTDIR ?=
LIBDIR ?= $(PREFIX)/lib
SBINDIR ?= $(PREFIX)/sbin
BINDIR ?= $(PREFIX)/bin
MANDIR ?= $(PREFIX)/share/man

.PHONY: build-capi install-capi build-ctl install-ctl install-man

build-capi:
cargo build --release -p composefs-capi

install-capi: build-capi
LIBDIR=$(DESTDIR)$(LIBDIR) crates/composefs-capi/install.sh $(DESTDIR)$(PREFIX)

build-ctl:
cargo build --release -p composefs-ctl

install-ctl: build-ctl
install -d $(DESTDIR)$(BINDIR) $(DESTDIR)$(SBINDIR)
install -m 755 target/release/cfsctl $(DESTDIR)$(BINDIR)/cfsctl
ln -sf $(BINDIR)/cfsctl $(DESTDIR)$(SBINDIR)/mount.composefs
ln -sf $(BINDIR)/cfsctl $(DESTDIR)$(BINDIR)/mkcomposefs
ln -sf $(BINDIR)/cfsctl $(DESTDIR)$(BINDIR)/composefs-info

install-man:
install -d $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 $(DESTDIR)$(MANDIR)/man8
for md in man/*.md; do \
base=$$(basename "$$md" .md); \
case "$$base" in \
mount.composefs) section=8 ;; \
composefs-dump) section=5 ;; \
*) section=1 ;; \
esac; \
pandoc -s -t man "$$md" -o $(DESTDIR)$(MANDIR)/man$$section/$$base.$$section; \
done
5 changes: 4 additions & 1 deletion crates/composefs-boot/src/selabel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ pub fn open_file<H: FsVerityHashValue>(
match dir.get_file_opt(filename.as_ref())? {
Some(file) => match file {
RegularFile::Inline(data) => Ok(Some(Box::new(Cursor::new(data.clone())))),
RegularFile::External(id, ..) => Ok(Some(Box::new(File::from(repo.open_object(id)?)))),
RegularFile::External(id, ..) | RegularFile::ExternalNoVerity(id, ..) => {
Ok(Some(Box::new(File::from(repo.open_object(id)?))))
}
RegularFile::Sparse(..) => Ok(None),
},
None => Ok(None),
}
Expand Down
25 changes: 25 additions & 0 deletions crates/composefs-capi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "composefs-capi"
version = "1.4.0"
edition.workspace = true
license.workspace = true
publish = false
description = "C-compatible shared library (libcomposefs) backed by Rust composefs implementation"

[lib]
crate-type = ["cdylib", "staticlib"]

[dependencies]
composefs = { workspace = true }
libc = "0.2"
anyhow = "1"
rustix = { version = "1", features = ["fs", "mm", "process", "mount"] }
zerocopy = "0.8"

[build-dependencies]
cc = "1"

[lints.rust]
unsafe_code = "allow"
missing_docs = "allow"
missing_debug_implementations = "allow"
9 changes: 9 additions & 0 deletions crates/composefs-capi/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fn main() {
println!("cargo:rustc-cdylib-link-arg=-Wl,-soname,libcomposefs.so.1");

cc::Build::new()
.file("tests/test_lcfs.c")
.include("include/libcomposefs")
.warnings(false)
.compile("test_lcfs_c");
}
10 changes: 10 additions & 0 deletions crates/composefs-capi/composefs.pc.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
prefix=@PREFIX@
exec_prefix=${prefix}
libdir=@LIBDIR@
includedir=@INCLUDEDIR@

Name: Composefs
Description: library for generating and using composefs images
Version: @VERSION@
Libs: -L${libdir} -lcomposefs
Cflags: -I${includedir}
26 changes: 26 additions & 0 deletions crates/composefs-capi/include/libcomposefs/lcfs-erofs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* lcfs
Copyright (C) 2023 Alexander Larsson <alexl@redhat.com>

SPDX-License-Identifier: GPL-2.0-or-later OR Apache-2.0
*/
#ifndef _LCFS_EROFS_H
#define _LCFS_EROFS_H

#include <stdint.h>

#define LCFS_EROFS_VERSION 1
#define LCFS_EROFS_MAGIC 0xd078629aU

typedef enum {
LCFS_EROFS_FLAGS_HAS_ACL = (1 << 0),
} lcfs_erofs_flag_t;

struct lcfs_erofs_header_s {
uint32_t magic;
uint32_t version;
uint32_t flags;
uint32_t composefs_version;
uint32_t unused[4];
} __attribute__((__packed__));

#endif
52 changes: 52 additions & 0 deletions crates/composefs-capi/include/libcomposefs/lcfs-mount.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* lcfs
Copyright (C) 2023 Alexander Larsson <alexl@redhat.com>

SPDX-License-Identifier: GPL-2.0-or-later OR Apache-2.0
*/
#ifndef _LCFS_MOUNT_H
#define _LCFS_MOUNT_H

#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

#ifndef LCFS_EXTERN
#define LCFS_EXTERN extern
#endif

#define ENOVERITY ENOTTY
#define EWRONGVERITY EILSEQ
#define ENOSIGNATURE EBADMSG

enum lcfs_mount_flags_t {
LCFS_MOUNT_FLAGS_NONE = 0,
LCFS_MOUNT_FLAGS_REQUIRE_VERITY = (1 << 0),
LCFS_MOUNT_FLAGS_READONLY = (1 << 1),
LCFS_MOUNT_FLAGS_IDMAP = (1 << 3),
LCFS_MOUNT_FLAGS_TRY_VERITY = (1 << 4),

LCFS_MOUNT_FLAGS_MASK = (1 << 5) - 1,
};

struct lcfs_mount_options_s {
const char **objdirs;
size_t n_objdirs;
const char *workdir;
const char *upperdir;
const char *expected_fsverity_digest;
uint32_t flags;
int idmap_fd; /* userns fd */
const char *image_mountdir; /* Temporary location to mount images if needed */

uint32_t reserved[4];
void *reserved2[4];
};

LCFS_EXTERN int lcfs_mount_image(const char *path, const char *mountpoint,
struct lcfs_mount_options_s *options);
LCFS_EXTERN int lcfs_mount_fd(int fd, const char *mountpoint,
struct lcfs_mount_options_s *options);

#endif
Loading