fs-core
Published on crates.io as rust-fs-core (the repository's name) and
imported as fs_core. Up to 0.2.24 it was published as am-fs-core.
Pure-Rust block-device framework. The shared substrate every filesystem driver and disk-image reader plugs into.
What it gives you
BlockRead— read-only random-access block device (read_at+size_bytes)BlockDevice: BlockRead— adds optionalwrite_at/flush/is_writable, andset_len/can_growfor the devices that can change their own lengthFileDevice— backed by a regular file, optional read-only; the one device here that can grow, when it is open read-write on a regular fileCallbackDevice— backed by host-process-owned callbacks (FFI from Swift / Go / C++)CachingDevice— LRU read-cache decorator over aBlockRead, and over aBlockDevicewhen the caller has one to give (newvsread_only)CountingDevice— counts the reads and the bytes a driver asks its device for, so drivers can be compared against each other and against themselves later by one instrument rather than several look-alike onesReadOnlyDevice— rejects writes whatever the inner type allowsSliceReader/OwnedSlice/OwnedRwSlice— a sub-range of a device presented as a device, for partition walkers and container readersBlockReadStreamer—std::io::Read + Seekover anyBlockRead- A unified
Errortype with aCustom(String)escape hatch so each driver can lift its own internal errors to the trait boundary cli— behind theclicargo feature: the command-line plumbing every tool in the family shares. One multi-call binary dispatching onargv[0], the<tool> (<crate>) <version>line,<repo> doctor, JSON results with--textas the opt-out, the{"error": ..., "code": N}failure on stderr and its exit statuses, andgenerate names|man|completionsfor packaging. A crate describes its tools once, as acli::Family, and callscli::main. Use it; do not copy it. It began as a directory each crate carried by hand, and the copies drifted within weeks.
The cli feature
[]
= ["dep:clap", "rust-fs-core/cli"]
[[]]
= "rust-fs-<fs>"
= "src/cli/main.rs"
= ["cli"]
Turn it on only under the crate's own cli feature, beside the
required-features on its binary. clap, clap_complete and clap_mangen are
optional here and reached only through the feature, so the default build —
and every static library built from it — gains no dependency. The crate's
own clap requirement stays, for its tools' commands; fs_core::cli::clap
re-exports the one this module is built against.
Intended consumers
Filesystem drivers — fs-ext4, fs-ntfs, future am-fs-exfat,
am-fs-hfsplus, am-fs-apfs, am-fs-fat32, am-fs-fat16,
am-fs-squashfs, am-fs-iso9660. Each only writes format-specific
code; the block plumbing comes from here.
Disk-image readers — am-img-qcow2, am-img-vhd, am-img-vhdx,
am-img-vmdk; future am-img-vdi, am-img-raw. Same pattern:
format-specific container logic, shared block I/O.
Block-layer utilities — am-partitions (GPT/MBR probe), future
am-block-luks, am-block-lvm, am-block-mdraid. Same trait,
opposite direction: consumes a BlockRead to expose slices of it.
Layout
src/
lib.rs public re-exports
error.rs Error / Result
block.rs BlockRead + BlockDevice traits
file_device.rs FileDevice (backed by std::fs::File)
callback_device.rs CallbackDevice (FFI-friendly)
caching_device.rs CachingDevice (LRU decorator)
counting_device.rs CountingDevice (counts reads + bytes asked for)
slice.rs SliceReader / OwnedSlice / OwnedRwSlice
readonly.rs ReadOnlyDevice (rejects writes whatever the inner type allows)
stream.rs BlockReadStreamer (std::io::Read + Seek over a BlockRead)
ffi.rs the C ABI — FsCoreDevice, FsCoreCallbackCfg
cli.rs the tools' shared plumbing (feature `cli`), and in cli/:
dispatch, version, doctor, output, docs, family
tests/
cache.rs CachingDevice + interop tests
.github/actions/
install-chore/ the composite action that installs `chore` in CI
The CI gate, and the chore installer
The one required check is ci-ok, and it stands for every job — see
.github-guard, which argues why at length. What holds that true is
scripts/core.sh ci-gate, run by the fmt job: the gate workflow must run on
pull_request, ci-ok must needs: every gating job and nothing that does
not exist, it must carry if: always() rather than a narrowing of it, and
.github-guard must require ci-ok alone.
Those rules were tests/ci_aggregate_gate.rs here, and in ten sibling
repositories as ten variants of the same 161–173 lines; then briefly a
crates/am-ci-guard dev-dependency (#156, #157). Both were the wrong
container. The rules test nothing this crate ships — they parse a YAML file
and compare strings — and the test job enforces an executed-test floor, so
a meta-test inflates the very count used to satisfy the gate. A ci:gate
subcommand inside chore was wrong too: it made a release of a shared tool a
prerequisite for a change here. They are scripts/ci-gate.sh now, one of this
crate's family scripts: every repository in the family runs this copy as
bash scripts/core.sh ci-gate against its own ci.yml and .github-guard,
and scripts/core.sh family-check refuses a repository that commits its own.
.github/actions/install-chore is the composite action that installs the
binary (antimatter-studios/chore#52), for the other thing every repository
was writing out by hand:
- uses: antimatter-studios/rust-fs-core/.github/actions/install-chore@<ref>
with:
version: "0.11.0"
Three repositories each carried a copy of scripts/ci-install-chore.sh, one
inlined the asset mapping four times, and a fifth hand-written copy asked for
a tarball name that did not exist and 404'd on every scheduled run. The
convention belongs to whoever publishes the releases, so it is spelled once,
checksum-verified. Pin the @ref.
Roadmap
Planned additions (not yet implemented):
Loggerhook — pluggableset_logger(callback)so consuming crates can route diagnostics to a host-provided sink without each crate hard-coding a logging dependency.IoStatshook — the write side of the counters, and a hit rate generalised beyondCachingDevice::stats. The read side has shipped:CountingDevicecounts reads and bytes-read for anyBlockRead, andCachingDevice::statsreports its own hits and misses. What is left is writes/bytes-written, and one accessor that reads the same way across every adapter rather than per type.
Git hooks
The guards are github-guard's,
installed once per clone into .git/hooks:
Nothing is committed for them: a hook inside the working tree is a hook a
branch checkout can replace, which is what moving them out of it prevented.
The one tracked file is .github-guard, which declares the checks main
requires.
Why the dependency-pinning guard looks half-idle here
pre-commit.d/rust-deps-pinned.sh is the same file every sibling
project runs, and part of it has nothing to do in this one. That is expected
rather than a misconfiguration, and it is recorded here so nobody has to work
it out twice.
The guard does two jobs. It refuses a workflow that clones a sibling
project at a floating ref instead of a tag — and this crate sits at the
bottom of the dependency graph, with no dependency at all in its default build
and only the optional clap crates behind cli, so there is no sibling to pin
and that half never fires. It also refuses a missing or drifted
Cargo.lock and runs cargo metadata --locked as a stale-lock check, and
that half applies in full, because this crate does track its lockfile.
The guard is the same in every repository deliberately. The value of a
shared guard is that one audit covers every repository, so a local edit to
trim the idle half would cost more than the idle half does. It is updated by
re-running github-guard's installer, never by editing the copy in .git/hooks.
Verifying a release
From the next release onward, every version published to crates.io is
also attached to the GitHub release for its tag, with a build-provenance
attestation signed by this repository's release workflow. It proves the
crate was built by .github/workflows/release.yml from a commit in this
repository, not uploaded from someone's machine. To check the crates.io
download of version X.Y.Z:
The workflow refuses to attest a .crate whose sha256 differs from the
checksum crates.io records for that version, so the file on the release
page and the crates.io download are the same bytes.
License
MIT.
Non-filesystem targets
The block traits, callback devices and cache also build for wasm32-unknown-unknown.
FileDevice and the fs_core_file_open C convenience function are available only
on Unix and Windows, where the native positioned-file implementation exists.
Browser embedders supply a callback device; native behavior is unchanged.