Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
8dfedee
Merge pull request #1 from zombocoder/ci/fix-github-action
zombocoder Sep 5, 2025
6ec7be9
Update GitHub Actions to use latest page configuration actions
zombocoder Sep 5, 2025
521d10c
Add permissions for release asset uploads in GitHub Actions workflow
zombocoder Sep 5, 2025
776f987
Add compression support to BFC library
zombocoder Sep 5, 2025
f78985d
Merge pull request #2 from zombocoder/feature/compression-support
zombocoder Sep 5, 2025
ac5979c
Add unit tests for encryption functionality and integration tests
zombocoder Sep 6, 2025
c55d73c
Fix encryption password function calls in end-to-end tests
zombocoder Sep 6, 2025
b17eeef
Refactor decryption calls for improved readability in bfc_reader.c
zombocoder Sep 6, 2025
63bfac0
Fix file path references in CI tests for compression and encryption f…
zombocoder Sep 6, 2025
fe2893c
Fix file path in CI test for encryption with compression and enhance …
zombocoder Sep 6, 2025
f976cec
Update file paths in end-to-end encryption tests to use relative paths
zombocoder Sep 6, 2025
4c63404
Update file paths in encryption tests to use unique temporary filenames
zombocoder Sep 6, 2025
4e9afb8
Make temporary filenames unique in end-to-end encryption tests using …
zombocoder Sep 6, 2025
9785fe3
Add encryption status display in container info summary
zombocoder Sep 6, 2025
3e8025e
Update encryption test to use a larger test file for compression vali…
zombocoder Sep 6, 2025
6908af5
Fix formatting inconsistencies in encryption test files and update co…
zombocoder Sep 6, 2025
4371fb6
Silence warnings on cleanup errors in run_demo function
zombocoder Sep 6, 2025
658f447
Add tests for encryption detection and error handling in bfc_encrypt
zombocoder Sep 6, 2025
022d0f7
Add build_*/ to .gitignore to exclude additional build directories
zombocoder Sep 6, 2025
f091536
Add comprehensive tests for compression and encryption edge cases
zombocoder Sep 6, 2025
04953a9
Add tests for ZSTD streaming context and enhance compression recommen…
zombocoder Sep 6, 2025
512788e
Refactor whitespace in compression test cases for improved readability
zombocoder Sep 6, 2025
67f5ecd
Update coverage threshold to reflect current achievable coverage
zombocoder Sep 6, 2025
1cf4918
Update coverage threshold to 75 and add parameter validation tests fo…
zombocoder Sep 6, 2025
4d40ceb
Add comprehensive tests for encryption functions and edge cases
zombocoder Sep 6, 2025
2423e07
Merge pull request #3 from zombocoder/feature/encryption-support
zombocoder Sep 6, 2025
c51b9f8
Enhance CLI test functionality and improve error messages in verifica…
zombocoder Sep 6, 2025
bef75fa
Add Buy Me a Coffee funding option
zombocoder Sep 6, 2025
74e4c82
Reorder directory change and container opening in extract command for…
zombocoder Sep 7, 2025
52fe28c
Merge pull request #4 from zombocoder/bugfix/fix-extract-flag
zombocoder Sep 7, 2025
ee14fb8
Add OCI Image Specs support
themoriarti Sep 12, 2025
08670ce
Add support for symlink handling in container operations
zombocoder Sep 13, 2025
48241e3
Add symlink support with comprehensive tests and CI integration
zombocoder Sep 13, 2025
ae3105d
Enable GNU extensions in cmd_create.c for enhanced compatibility
zombocoder Sep 13, 2025
7a8c314
Merge pull request #6 from zombocoder/feature/support-symlink
zombocoder Sep 13, 2025
8271c4e
feat: implement OCI image specs support with dynamic arch/OS detection
themoriarti Sep 15, 2025
2f18001
Merge branch 'zombocoder:main' into oci-image-specs-support
themoriarti Sep 15, 2025
b57475b
style: apply clang-format to all source files
themoriarti Sep 15, 2025
717ec88
Fix OCI support
themoriarti Sep 19, 2025
047993f
Add FreeBSD build support
zombocoder Oct 6, 2025
717a40b
Merge pull request #7 from zombocoder/feature/freebsd-support
zombocoder Oct 6, 2025
319faf2
Merge branch 'zombocoder:main' into oci-image-specs-support
themoriarti Nov 2, 2025
49ed931
Apply OCI patch and add comprehensive tests
themoriarti Nov 2, 2025
2edf2ec
Remove PR_DESCRIPTION.md
themoriarti Nov 2, 2025
4f495ad
Include stdio for OCI tests
themoriarti Nov 11, 2025
208594d
Silence skip message in OCI tests
themoriarti Nov 11, 2025
5e8658f
Apply formatting after fmt-fix
themoriarti Nov 11, 2025
726003b
Merge 'main' into oci-image-specs-resolve (PR #5)
sashml Jun 14, 2026
142c2ea
fix(oci): finish write path + build/spec correctness for OCI support
sashml Jun 14, 2026
f9ff81b
feat(oci): implement OCI read-back path via libcjson
sashml Jun 14, 2026
fd442fc
docs(oci): add CALM + Mermaid C4 model for the OCI integration
sashml Jun 14, 2026
a3324f1
docs(oci): document libcjson build dependency for BFC_WITH_OCI
sashml Jun 15, 2026
791ca7e
fix(examples): remove duplicate encrypt_example target from merge
sashml Jun 15, 2026
0d56115
fix(merge): drop duplicated helpers + use real OCI types in tests
sashml Jun 15, 2026
1600e04
fix(oci): address review — linking, round-trip, ownership, merge left…
sashml Aug 26, 2026
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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ jobs:
if: runner.os == 'macOS'
run: |
brew install clang-format zstd libsodium ninja || true
# cmake is already available on macOS runners

- name: Install dependencies (Windows)
if: runner.os == 'Windows'
Expand All @@ -66,6 +67,8 @@ jobs:
fi
cmake -B build \
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DCMAKE_C_COMPILER=${{ matrix.cc }} \
-DCMAKE_CXX_COMPILER=${{ matrix.cxx }} \
-DBFC_WITH_ZSTD=ON \
-DBFC_WITH_SODIUM=ON \
$TOOLCHAIN_ARG
Expand Down
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ set(CMAKE_C_EXTENSIONS OFF)
option(BFC_WITH_FUSE "Build with FUSE support for mounting" OFF)
option(BFC_WITH_ZSTD "Build with Zstd compression support" OFF)
option(BFC_WITH_SODIUM "Build with libsodium encryption support" OFF)
option(BFC_WITH_OCI "Build with OCI Image Specs support" OFF)
option(BFC_COVERAGE "Enable coverage reporting" OFF)
option(BFC_BUILD_BENCHMARKS "Build benchmarks" ON)
option(BFC_BUILD_EXAMPLES "Build examples" ON)
Expand Down Expand Up @@ -70,6 +71,15 @@ if(BFC_WITH_SODIUM)
message(STATUS "libsodium encryption support enabled")
endif()

if(BFC_WITH_OCI)
find_package(PkgConfig REQUIRED)
# IMPORTED_TARGET carries include dirs, libraries AND link directories in one
# go, so every consumer (lib, cli, tests, benchmarks, examples) links cleanly
# even where libcjson lives off the default linker path (Homebrew, /usr/local).
pkg_check_modules(CJSON REQUIRED IMPORTED_TARGET libcjson)
message(STATUS "OCI image-specs support enabled")
endif()

# Include directories
include_directories(include)

Expand Down
242 changes: 242 additions & 0 deletions OCI_SUPPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
# BFC OCI Image Specs Support

This document describes the OCI (Open Container Initiative) Image Specs support added to BFC (Binary File Container).

## Overview

BFC now supports storing and managing OCI container images in its efficient single-file format. This allows BFC to be used as a storage backend for OCI-compliant container registries and image management systems.

## Features

- **OCI Manifest Support**: Store and manage OCI image manifests
- **OCI Config Support**: Store and manage OCI image configurations
- **OCI Layer Support**: Store and manage OCI image layers
- **OCI Index Support**: Store and manage OCI image indexes
- **Validation**: Validate OCI manifests and configs
- **Extraction**: Extract BFC containers to OCI format

## Building

OCI support is opt-in and **off by default**. Enable it with `-DBFC_WITH_OCI=ON`.
It requires **libcjson** (used to read manifests/configs back), detected via
pkg-config like the other optional dependencies:

```bash
# Debian/Ubuntu
sudo apt-get install libcjson-dev
# FreeBSD
sudo pkg install libcjson
# Windows (vcpkg)
vcpkg install cjson:x64-windows

cmake -B build -DBFC_WITH_OCI=ON
cmake --build build
```

### Memory ownership

The **caller owns the struct; the library owns the fields.** The `bfc_free_oci_*`
helpers release the fields and zero the struct — they never `free()` the struct
itself, so they are safe on stack locals:

```c
bfc_oci_manifest_t m = {0};
bfc_get_oci_manifest(bfc, &m); /* fills m, allocates its fields */
bfc_free_oci_manifest(&m); /* frees the fields only */
```

If you allocated the struct yourself, free it yourself after the helper returns.
`bfc_list_oci_layers()` is the exception that allocates a block: it returns one
contiguous array, released with `bfc_free_oci_layers(layers, count)`.

### Platform support

OCI support is **POSIX-only** for now: the implementation uses `fmemopen`,
two-argument `mkdir`, `<unistd.h>` and `<libgen.h>`, none of which exist under
MSVC. Linux, macOS and FreeBSD are supported; `-DBFC_WITH_OCI=ON` is not
expected to build on Windows/MSVC yet.

The write path serializes manifests/indexes to JSON; the read path
(`bfc_get_oci_manifest`/`bfc_get_oci_config`/`bfc_list_oci_layers`) parses them
back with libcjson.

## API Reference

### OCI Manifest Functions

```c
// Create BFC container from OCI image manifest
int bfc_create_from_oci_manifest(bfc_t* bfc, const bfc_oci_manifest_t* manifest, const char* config_json);

// Get OCI manifest from BFC container
int bfc_get_oci_manifest(bfc_t* bfc, bfc_oci_manifest_t* manifest);

// Validate OCI manifest
int bfc_validate_oci_manifest(const bfc_oci_manifest_t* manifest);
```

### OCI Layer Functions

```c
// Add OCI layer to BFC container
int bfc_add_oci_layer(bfc_t* bfc, const bfc_oci_layer_t* layer, FILE* layer_data);

// List OCI layers in BFC container
int bfc_list_oci_layers(bfc_t* bfc, bfc_oci_layer_t** layers, size_t* layer_count);
```

### OCI Index Functions

```c
// Create BFC container from OCI image index
int bfc_create_from_oci_index(bfc_t* bfc, const bfc_oci_index_t* index);
```

### Utility Functions

```c
// Extract BFC container to OCI format
int bfc_extract_to_oci(bfc_t* bfc, const char* output_dir);

// Free OCI structures
void bfc_free_oci_manifest(bfc_oci_manifest_t* manifest);
void bfc_free_oci_config(bfc_oci_config_t* config);
void bfc_free_oci_layer(bfc_oci_layer_t* layer);
void bfc_free_oci_index(bfc_oci_index_t* index);
void bfc_free_oci_layers(bfc_oci_layer_t* layers, size_t layer_count);
```

## Data Structures

### OCI Manifest

```c
typedef struct {
char* schema_version; // OCI schema version — always "2" per the image-spec
char* media_type; // Media type (e.g., "application/vnd.oci.image.manifest.v1+json")
char* config_digest; // SHA256 digest of config
size_t config_size; // Size of config in bytes
char** layer_digests; // Array of layer digests
size_t layer_count; // Number of layers
char* annotations; // JSON annotations
} bfc_oci_manifest_t;
```

### OCI Config

```c
typedef struct {
char* architecture; // Target architecture (e.g., "amd64")
char* os; // Target OS (e.g., "linux")
char* created; // Creation timestamp
char* author; // Image author
char* config; // Container configuration
char* rootfs; // Root filesystem configuration
char* history; // Image history
} bfc_oci_config_t;
```

### OCI Layer

```c
typedef struct {
char* digest; // Layer digest (e.g., "sha256:abc123...")
char* media_type; // Layer media type
size_t size; // Layer size in bytes
char** urls; // Optional URLs for layer
size_t url_count; // Number of URLs
char* annotations; // Layer annotations
} bfc_oci_layer_t;
```

## Usage Example

```c
#include "bfc_oci.h"

int main() {
// Create BFC container
bfc_t* bfc = NULL;
bfc_create("image.bfc", 4096, 0, &bfc);

// Create OCI manifest
bfc_oci_manifest_t* manifest = calloc(1, sizeof(bfc_oci_manifest_t));
manifest->schema_version = strdup("2");
manifest->media_type = strdup("application/vnd.oci.image.manifest.v1+json");
manifest->config_digest = strdup("sha256:abc123...");
manifest->config_size = 1024;
manifest->layer_count = 1;
manifest->layer_digests = calloc(1, sizeof(char*));
manifest->layer_digests[0] = strdup("sha256:def456...");

// Add manifest to BFC
bfc_create_from_oci_manifest(bfc, manifest, "{\"architecture\":\"amd64\"}");

// Add layer
bfc_oci_layer_t* layer = calloc(1, sizeof(bfc_oci_layer_t));
layer->digest = strdup("sha256:def456...");
layer->media_type = strdup("application/vnd.oci.image.layer.v1.tar+gzip");
layer->size = 1024 * 1024;

FILE* layer_data = fopen("layer.tar.gz", "rb");
bfc_add_oci_layer(bfc, layer, layer_data);
fclose(layer_data);

// Finish container
bfc_finish(bfc);
bfc_close(bfc);

// Cleanup: the helpers release the FIELDS; these structs were malloc'd
// by us, so we release them ourselves.
bfc_free_oci_manifest(manifest);
free(manifest);
bfc_free_oci_layer(layer);
free(layer);

return 0;
}
```

## Building with OCI Support

OCI support is built through CMake (see **Building** above) — the module lives at
`src/lib/bfc_oci.c` and is compiled only when `BFC_WITH_OCI=ON`:

```bash
cmake -B build -DBFC_WITH_OCI=ON && cmake --build build
```

## Integration with Container Runtimes

BFC with OCI support can be integrated with:

- **Docker**: Use BFC as a storage backend for Docker images
- **Podman**: Use BFC as a storage backend for Podman images
- **containerd**: Use BFC as a storage backend for containerd
- **CRI-O**: Use BFC as a storage backend for CRI-O
- **Custom Runtimes**: Use BFC as a storage backend for custom container runtimes

## Benefits

1. **Efficiency**: Single file storage for entire OCI images
2. **Compression**: Built-in zstd compression support
3. **Encryption**: Built-in ChaCha20-Poly1305 encryption support
4. **Integrity**: Built-in CRC32c checksums
5. **Portability**: Easy to copy and transfer OCI images
6. **ZFS Integration**: Works well with ZFS snapshots and clones

## Future Enhancements

- **Registry Integration**: Direct integration with OCI registries
- **Layer Deduplication**: Automatic deduplication of identical layers
- **Compression Optimization**: Automatic compression level selection
- **Encryption Key Management**: Advanced encryption key management
- **Metadata Indexing**: Fast metadata search and indexing

## Contributing

Contributions to OCI support are welcome! Please see the main BFC repository for contribution guidelines.

## License

This OCI support code is licensed under the Apache License 2.0, same as the main BFC project.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ cmake --build build
**Optional dependencies:**
- ZSTD library for compression support
- libsodium for encryption support
- libcjson for OCI image-specs support (`-DBFC_WITH_OCI=ON`)
- pkg-config (or pkgconf on FreeBSD/Linux) for dependency detection on Unix
- vcpkg for dependency management on Windows

Expand Down Expand Up @@ -128,6 +129,7 @@ cmake --build build
cmake -B build -DBFC_WITH_ZSTD=ON # Compression only
cmake -B build -DBFC_WITH_SODIUM=ON # Encryption only
cmake -B build -DBFC_WITH_FUSE=ON # FUSE filesystem support
cmake -B build -DBFC_WITH_OCI=ON # OCI image-specs (requires libcjson)

# Enable code coverage
cmake -B build -DCMAKE_BUILD_TYPE=Debug -DBFC_COVERAGE=ON
Expand Down
45 changes: 45 additions & 0 deletions docs/analysis/bfc-oci-deep-analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Deep Analysis: bfc OCI module (ownership + round-trip)

**Coverage**: Degraded{missing: CodebaseMemory corroboration}
**Pillars**: graphify ✓ (21 facts, anchored) · codebase-memory ✓ indexed (1659 nodes / 2781 edges) but the `code-intel` adapter returned 0 facts for it — structure gathered by hand via MCP `query_graph`, per the documented fallback.

## Structure — WHO + HOW (codebase-memory, by hand)

- **Consumers of the OCI API**: `tests/unit/test_oci.c` only, plus internal self-calls inside `src/lib/bfc_oci.c`. No CLI, no other library callers.
→ **The public ownership contract can still be chosen freely; there are no downstream consumers to break.**
- `bfc_free_oci_layer` / `bfc_free_oci_manifest` are called both internally (`bfc_oci.c`) and from the test suite.

## Rationale — WHY (graphify)

- All OCI symbols land in **one community (3)** — a cohesive, well-bounded module; no god-node, no cross-cutting entanglement. The feature is structurally clean; the defects are local to its contract.

## Conclusion (Belnap synthesis)

| Claim | Verdict | Provenance | Anchor |
|---|---|---|---|
| `bfc_list_oci_layers` allocates ONE contiguous `calloc(n, sizeof(bfc_oci_layer_t))` array of **structs** | **True** | Both (graphify anchor + read source) | `src/lib/bfc_oci.c:470` |
| `bfc_free_oci_layers` iterates that memory as an array of **pointers** (`bfc_free_oci_layer(layers[i])`) | **True** | Both | `src/lib/bfc_oci.c:631` |
| ⇒ The two are **incompatible**: pairing them reinterprets `digest`/`media_type` pointer bytes as struct pointers and frees them | **True** | Synthesized | `:470` ↔ `:631` |
| `bfc_free_oci_manifest` ends in `free(manifest)` while `bfc_get_oci_manifest` fills a **caller-owned** struct (tests pass a stack local) | **True** | Both | `:557` ↔ `:392` |
| Writer stores layers at `blobs/sha256/%s`; extractor lists prefix `"layers/"` | **True** | Both | `:236` vs `:326` |
| ⇒ Extraction can never match a layer; returns `BFC_OK` with 0 files (silent) | **True** | Synthesized | `:326` |
| `blobs/sha256/%s` with a spec digest (`sha256:ab…`) yields `blobs/sha256/sha256:ab…` — algorithm twice | **True** | Read source | `:236` |
| OCI module is one cohesive community; boundary is sound | **True** | Graphify | `:1` |

No **Conflicted** claims — the pillars did not disagree anywhere.

## Decision: the ownership rule

**The caller owns the struct; the library owns the fields.**

- Getters (`bfc_get_oci_manifest`, `bfc_get_oci_config`) keep their `T*` out-param and fill a caller-provided struct — the natural C idiom and no public signature change.
- `bfc_free_oci_manifest` / `_config` / `_layer` release **fields only** and zero the struct; they no longer `free()` the struct itself.
- `bfc_list_oci_layers` keeps returning one contiguous array; `bfc_free_oci_layers` changes `bfc_oci_layer_t**` → `bfc_oci_layer_t*` and frees each element's fields, then the array once.
- `bfc_free_oci_index` owns its `manifests[i]` pointers, so it frees fields **and** each pointer.

Chosen over "getters allocate and return `**`" because it preserves the existing getter signatures and matches how the tests already declare structs (`bfc_oci_manifest_t m = {0};`).

## Gaps

- codebase-memory contributed no facts through the `code-intel` adapter despite a fresh index — adapter query path is worth a look (structure here was obtained via MCP directly).
- Windows/MSVC path is unanalyzed: the module uses `open_memstream`/`fmemopen`/2-arg `mkdir`/`<unistd.h>`/`<libgen.h>`, none available under MSVC.
Loading
Loading