fa10 makes files bigger. On purpose. Then it gives them back.
Zip spent decades getting good at shrinking files. fa10 skipped that lecture.
Point it at a file or a folder and it writes one larger .fa10 archive padded
with obvious filler text instead of compressed bits. Run fa10 restore and the
original tree comes back byte for byte, checked against a SHA-256 so you know
it's the real thing and not a convincing fake.
Why would you want a bigger file? Because every so often you need a 6 GB file to
test a backup, an upload limit, or a "disk almost full" alert, and
dd if=/dev/urandom just hands you 6 GB of noise you can never turn back into
anything. fa10's filler is plain ASCII (FA10-PADDING-BLOCK- over and over), so
it stands out in a hex dump, compresses back down to almost nothing, and
restores to exactly what you fed it.
So yes, it's the opposite of zip. No, we are not entirely sure why either. The tests pass.
$ fa10 --multiplier 5 project/
packed 42 entries (1.20 MiB) -> project.fa10 (6.00 MiB, 4.80 MiB padding)
$ fa10 restore project.fa10
extracted 42 entries from project.fa10 -> . (1.20 MiB), SHA-256 verified
curl -fsSL https://raw.githubusercontent.com/walangstudio/fa10/main/install.sh | shThis downloads the right prebuilt binary, verifies its SHA-256, and installs it
to /usr/local/bin (or ~/.local/bin if that is not writable). Re-run it any
time to upgrade. Options: --version v0.3.0 for a specific release,
--pre-release for the latest pre-release.
irm https://raw.githubusercontent.com/walangstudio/fa10/main/install.ps1 | iexInstalls to %LOCALAPPDATA%\Programs\fa10 and adds it to your user PATH. For
options, run the script explicitly:
& ([scriptblock]::Create((irm https://raw.githubusercontent.com/walangstudio/fa10/main/install.ps1))) -Version v0.3.0cargo install fa10 # from crates.io, once published
cargo install --git https://github.com/walangstudio/fa10 # from sourcecargo binstall fa10 also works if you have cargo-binstall (it pulls the prebuilt binary).
Prebuilt binaries are attached to each
release: Linux and macOS on
x86_64 and arm64, Windows on x86_64, shipped as .tar.gz (Unix) / .zip
(Windows) with a SHA256SUMS file. Download, verify, extract the single fa10
binary, and put it anywhere on your PATH.
# Linux / macOS
curl -fsSL https://raw.githubusercontent.com/walangstudio/fa10/main/install.sh | sh -s -- --uninstall# Windows
& ([scriptblock]::Create((irm https://raw.githubusercontent.com/walangstudio/fa10/main/install.ps1))) -UninstallOr just delete the fa10 binary from wherever it was installed. fa10 keeps no
config, cache, or registry state, so removing the binary removes everything.
inflate is the default action, so a bare path just inflates it:
fa10 <path>... pack by 2x total size (the default)
fa10 --multiplier 5 <path>... pack to 5x the total input size
fa10 --size 100MB <path>... pack to a fixed size
fa10 report.csv -> report.fa10 (extension dropped, like zip)
fa10 mydir/ pack a directory tree -> mydir.fa10
fa10 a.txt b.txt -o out.fa10 pack several files into one archive
fa10 restore <archive>... extract the tree (into the current dir)
fa10 info <archive> list entries and metadata, change nothing
Output naming: a file drops its extension, like zip, so report.csv becomes
report.fa10 (the original name is kept in the manifest and restore puts the
extension back). A directory bar has no extension to drop, so it becomes
bar.fa10; two or more loose files default to archive.fa10. Pass --output
to name it yourself. Note two inputs that differ only by extension
(a.csv, a.xml) resolve to the same default name; fa10 refuses to overwrite
rather than clobber the first. Extraction recreates the stored tree under the
current directory, or under --output <dir>, like unzip.
fa10 inflate <path> is the explicit form if you prefer to spell it out
(grow works too, as a hidden alias). The implicit inflate only kicks in when
the first argument is not a known subcommand, so a file literally named
restore needs fa10 inflate restore.
There are themed aliases if you want them:
fa10 cake <path>... same as --multiplier 2
fa10 feast <path>... same as --multiplier 5
fa10 buffet <path>... same as --multiplier 10
fa10 diet <archive> same as restore
fa10 slim <archive> same as restore
| Flag | Command | What it does |
|---|---|---|
-m, --multiplier <N> |
inflate | Output size as a multiple of the total input size. Default is 2. |
-s, --size <SIZE> |
inflate | Fixed target size, for example 100MB or 2GiB. Cannot be combined with --multiplier. |
-o, --output <PATH> |
inflate | Archive path. Defaults to the input name with its extension replaced by .fa10 (a directory keeps its name + .fa10), or archive.fa10 for 2+ inputs. |
-o, --output <DIR> |
restore | Directory to extract into. Defaults to the current directory. |
--pattern <STR> |
inflate | Padding text to repeat. Default is FA10-PADDING-BLOCK-. |
--in-place |
inflate | Replace a single input file with its archive. Requires --confirm. |
--confirm |
inflate | Allow in-place writes and output over the 10 GiB cap. |
--verify |
inflate | Re-read the archive and check every entry's SHA-256 before reporting success. |
--no-verify |
restore | Skip the SHA-256 check while extracting. |
--force |
restore | Overwrite existing files when extracting. |
--batch |
inflate | Allow packing more than 100 files. |
-q, --quiet |
any | No banner, no progress bar. |
-v, --verbose |
any | With info, also print each entry's SHA-256. |
Sizes use 1024 as the base. KB, MB, GB, and TB mean the same thing as
KiB, MiB, GiB, and TiB. A plain number is a byte count. Decimals work,
so 1.5MB is 1572864 bytes.
Offset Size Field
0 8 header magic "FA10ARC\0"
8 .. entry contents concatenated in manifest order
.. P padding repeating "FA10-PADDING-BLOCK-" (or --pattern)
.. M manifest:
magic "FA10MANI"
entry_count u32 little-endian
per entry: kind u8 (0=file, 1=empty dir)
path length u32 LE, path (UTF-8, '/'-separated)
content size u64 LE
SHA-256 of content (32 bytes)
crc32 u32 LE over the manifest bytes
EOF - 16 8 end magic "FA10AEND"
EOF - 8 8 manifest length u64 little-endian
Entries are sorted by path, so the same input tree always produces a byte-identical archive. The 16-byte trailer holds the manifest length, so restore and info reach the manifest with two seeks instead of scanning. Restore reads the manifest, then streams the contiguous content region back out entry by entry, checking each SHA-256.
The .fa10 extension is used because nothing else claims it. .fa is taken by
the FASTA format from bioinformatics.
fa10 tries not to surprise you:
- It writes to a sibling file (
name.fa10/dir.fa10) and leaves the inputs alone unless you pass both--in-placeand--confirm(single file only). - It refuses to touch system paths such as
/usr,/bin,/etc,/System,~/Library, andC:\Windows. - It checks free space first and refuses if the write would leave under 2 GiB.
- Output above 10 GiB needs
--confirm; packing more than 100 files needs--batch. - Extraction is guarded against Zip-Slip: entry paths that are absolute,
drive-qualified, or contain
..are refused, so an archive can never write outside the extraction directory. Existing files are not overwritten without--force. - Symlinks are followed at pack time (their target content is stored as a plain file), with cycle detection; the archive never contains a symlink, so extraction only ever creates regular files and directories.
- It does not use the network, write config or registry entries, set up autostart, or modify itself.
See SECURITY.md for the details.
cargo build --release
cargo test
cargo clippy --all-targets -- -D warnings
cargo fmt --checkThe test suite uses small files (a few kilobytes) so it runs in well under a second. There is nothing that writes a large file as part of the tests.
The progress bar is behind a default progress feature. Build with
cargo build --release --no-default-features to drop the indicatif
dependency for a smaller binary; operations still run, just without the bar.
The toolchain is pinned in rust-toolchain.toml; CI uses the same version.
Releases are built by .github/workflows/release.yml. Either push a tag:
git tag -a v0.3.0 -m "Release v0.3.0"
git push origin v0.3.0or run the Release workflow manually (Actions tab) with a tag input. The
workflow stamps the crate version from the tag, builds the five target
binaries, generates SHA256SUMS, and publishes a GitHub Release with
auto-generated notes.
MIT. See LICENSE.