v0.10.0 — WITHDRAWN, do not use
⚠️ This release is WITHDRAWN — do not use it
A deep audit of v0.10.0 found defects that prevent the shipped default configuration from mounting
and that silently lose or corrupt user data. v0.10.1 is in progress. Until it ships, no tagged
version of ObjectFS should be used for data you care about.
The three that matter most
C1 — the default configuration cannot mount.
internal/config/config.go defaults compression.algorithm to gzip, but
internal/compression/codec.go implements only none, zstd, and lz4. Every layer that reads
config treats gzip as valid — only the codec factory disagrees — so objectfs s3://bucket /mnt
exits with Failed to start adapter. examples/config.yaml ships the same broken value.
H7 — offset writes truncate the object.
The write-buffer flush callback in internal/adapter/adapter.go is handed (key, data, offset) and
calls backend.PutObject(ctx, key, data), discarding the offset. PutObject is a whole-object
replace, so:
$ dd if=/dev/zero of=f bs=1M count=1 # 1 MiB file
$ printf X | dd of=f bs=1 seek=1048575 conv=notrunc
$ ls -l f
-rw-r--r-- 1 byte # the other 1,048,575 are gone
Non-contiguous writes — SQLite, mmap writeback, tar, HDF5 — return EIO instead. Flush errors are
recorded to a stats counter and never returned, so close(2) reports success after a failed upload.
C4 — read amplification on every object when compression is enabled.
internal/storage/s3/backend.go decides whole-object-versus-ranged fetch from the compression
configuration rather than from the object being read. A ranged read of any object — including
objects never compressed, objects below min_size, and objects written by other tools — downloads
the whole object, and parallel reads are disabled bucket-wide. Measured against real S3 in
us-west-2 with a fixed 4 KiB read:
| object size | compression off | compression on | penalty |
|---|---|---|---|
| 16 MiB | 123 ms | 1.92 s | 15.6× |
| 64 MiB | 117 ms | 5.03 s | 43.0× |
| 256 MiB | 227 ms | 49.2 s | 216.5× |
A 4 KiB read of a 10 GiB object transfers 10 GiB.
Also withdrawn for
- Silent corruption when the codec configuration changes.
Decompressin
internal/compression/s3_integration.goreturns the payload unchanged when the stored
Content-Encodingdoesn't match the configured codec — so an object written with zstd and read
after switching to lz4 emits the raw compressed frame with exit status 0. Theobjectfs-sha256
metadata this very release added is written and never read, so nothing catches it. - The read cache cannot hit and is never invalidated. The cache key includes the requested
length, so theLookupmetadata cache never hits (one S3 HEAD per path component perstat,
forever), short reads at EOF are uncacheable, and the 16 MB chunked cache population added in this
release is unreachable. There are nocache.Deletecalls anywhere ininternal/fuse, so a read
after a write on the same descriptor returns pre-write bytes for up to the 5-minute TTL. - A reachable panic that unmounts the filesystem.
GetObjectwith a negativesizeslices
data[offset:offset+size]with neither bounds arm firing —slice bounds out of range [100:99].
This kills the mount process and takes every open file descriptor with it. - This release's headline feature is inactive in production.
buildS3Configmaps 6 of roughly 30
s3.Configfields and does not mapParallelReadThreshold;NewBackenddoes not backfill it. The
parallel range GET path is gated onthreshold > 0, so it never runs on a real mount.PoolSize
is likewise unmapped, leaving a zero-capacity semaphore that blocks forever in
GetObjects/PutObjects. rmandrmdirreported success without deleting. Fixed after this tag (#163): the operations
now fail loudly withEROFSrather than silently lying.- Windows is not supported. The
cgofusebuild tag has never compiled. Any Windows claim in the
v0.10.0 documentation is wrong.
Why the test suite didn't catch this
Every defect above is a seam defect: a value correctly produced at one layer and silently dropped
at the boundary to the next. The suite's 32,680 lines across 90 files mock the neighbouring layer in
each case, so these are invisible to it by construction. v0.10.1 adds a differential-testing oracle —
identical operation sequences run against ObjectFS and against the local OS filesystem, asserted
byte-for-byte — plus fuzz targets over the write path, the range/slice domain, config loading, and
compression round-trips. Those tests are being written before the fixes, so each fix lands with a
failing-then-passing test.
Track progress: issues ·
milestones