-
MyFSIO v0.5.4 Beta Pre-Release
released this
2026-06-08 07:57:32 +00:00 | 20 commits to main since this releaseServer-side encryption — SSE-C for multipart uploads
- Per-part SSE-C for multipart uploads, with the customer key never stored. Previously the only SSE-C multipart path stored the base64 customer key in the upload manifest; that is gone. Initiate now generates a random per-object data key (ODK), wraps it under the customer key, and persists only the wrapped ODK + the customer-key MD5 in the manifest (gated behind an
__mpu_sse_c__marker, so SSE-S3 / SSE-KMS / single-PUT SSE-C / unencrypted paths are byte-for-byte unchanged). - Each
UploadPartvalidates the supplied key against the stored MD5, unwraps the ODK, and encrypts the part to a self-describing block carrying a fresh per-part random salt (nonce = HKDF(ODK, part_number ‖ salt)), so re-uploading a part can never reuse an AES-GCM nonce. GET/HEAD,Range, andpartNumberreads decrypt per-part by re-deriving the nonce from the stored salt and mapping plaintext offsets via per-part plaintext sizes. Missing key →400 InvalidRequest; wrong key →403 AccessDenied(AEAD failure never surfaces as 500).UploadPartCopyinto an SSE-C multipart upload is rejected with501 NotImplemented(useUploadPart), and the multipart minimum-part-size check runs against plaintext size before publish so a failed completion can't destroy a pre-existing object or leave a stray delete marker.
S3 API correctness & conformance
- Every error response now flows through a single error path that auto-generates a request id and emits
x-amz-request-id/x-amz-id-2headers alongside the XML body. - Per-object
<Owner>inListObjects/ListObjectsV2/ListObjectVersions: owner is parsed from the object's stored__acl__andDisplayNameis resolved via a per-request IAM map. Legacy objects with no ACL fall back to a canonicalmyfsioowner (not the requester); V1 now always emitsOwner+StorageClassper spec. - Added
PutBucketOwnershipControlsandPutPublicAccessBlockas no-op stubs (GET/PUT/DELETE) so boto3 ≥ 1.32CreateBucketfixtures stop erroring before the test body runs. - Object keys that collide with an existing directory path are now storable: shadowed keys are written to a
<dir>/.__myfsio_keydata__marker, with crash-safe conversion of blocking files via the system temp dir. Listing, HEAD/GET/DELETE, and the integrity scanner all resolve through the marker. - Leading-slash object keys are normalized on the object data path. A key sent with a leading slash (e.g.
//prefix/xon the wire) was rejected with400 InvalidObjectKeyon HEAD/GET/PUT/DELETE even though the SigV4 layer authorized it under the clean key — so clients keying not-found detection off404/NoSuchKeysaw a hard error instead of a missing object. The five object handlers now strip leading slashes to match the auth layer's key derivation, and the list-prefix handling from v0.5.3 was reworked to share the same normalization (internal double-slashes and..traversal handling unchanged). 416 Range Not Satisfiablenow returns aContent-Rangeheader;UploadPartvalidates the part number is within1..=10000.- S3 Select: sets
memory_limit/max_memoryand fixes a UTF-8 chunk-boundary panic.
Replication
- Resumable, partial-failure-tolerant multipart replication. Failed attempts now persist the
upload_idplus a source-identity fingerprint (size, etag, part size); a retry validates the fingerprint, callsListPartsto discover already-uploaded parts, and resumes the same MPU instead of restarting from byte zero (part ETags preserved verbatim). Stale ids —NoSuchUpload, changed source content/tuning, or permanentCompleteMultipartUploaderrors — are aborted server-side and cleared so no retry ever reuses a dead id. - Per-part failures within a pass are collected and retried in a second in-MPU pass; a permanent error or task panic immediately aborts sibling parts and the MPU, so a missing/panicked part can never slip into
CompleteMultipartUpload. - Progress-aware stall detection: each part body streams through a progress reader paired with a
tokio::select!watchdog.REPLICATION_PART_STALL_TIMEOUT_SECONDS(default 300s) now bounds both "no first byte" and "mid-transfer stall" cases, replacing the coarse Smithy read-timeout for stuck-upload detection on HDD/WAN paths. - Failure-store writes now hold the cache lock across the read-modify-write and persist atomically (temp + rename); an RAII in-flight guard prevents a panicking replicate task from leaking in-flight counters or wedging a bucket's batch run;
NoSuchBucketis classified via the structured SDK error code.
Integrity & garbage collection
- HDD-friendly scanning.
INTEGRITY_HEAL_CONCURRENCYnow defaults to 1 (serial heal — parallel hashing thrashes a single spindle), and a newINTEGRITY_SCAN_PACING_MS(default0) applies a per-object pacing delay every ~100 objects across all scan phases to relieve disk-head pressure. - SSE-encrypted objects are skipped in the corruption scan. Comparing a plaintext ETag against ciphertext produced a false positive that, combined with auto-heal, was quarantining and poisoning every encrypted object.
- Corruption mismatches are now re-verified under the object write lock before quarantine, with a grace-period guard so versions mid-archive aren't quarantined; the in-progress guard is hardened against the never-polled-task window and
objects_scannedis de-duplicated. - Stuck "scan in progress" banner fixed. Integrity/GC run state leaked when an HTTP request was cancelled mid-run, leaving the banner stuck forever. Runs now execute on a detached task with an RAII guard that always clears the running/started-at state, regardless of client cancellation.
Security & auth
- Bucket-policy matching is now gated by request method. A read grant (
s3:GetBucketCors/Lifecycle/Replication/ …) no longer authorizes the correspondingPUT/DELETE. Allow-only semantics, IAM, and wildcards are unchanged. - SSE-C customer-key MD5 is persisted and constant-time verified on GET/HEAD.
DeleteObjectshonorsx-amz-bypass-governance-retention.- Buffered upload paths (checksummed
PUT,POSTform) are now size-bounded to prevent OOM; tag key/value are XML-escaped inGetBucketTagging/GetObjectTagging; the replication-UI failure endpoints validate the bucket name (path-traversal / cross-bucket tampering). - The streaming-SigV4 acceptance warning is demoted to once per process instead of one line per upload.
Web UI
- The
/ui/bucketspage gains an access filter (All / Private / Public / Custom) alongside the existing name search, driven by each bucket's resolved policy type; filter state is applied on load. (Bucket access is now modeled as a typed enum server-side.)
Downloads
- Per-part SSE-C for multipart uploads, with the customer key never stored. Previously the only SSE-C multipart path stored the base64 customer key in the upload manifest; that is gone. Initiate now generates a random per-object data key (ODK), wraps it under the customer key, and persists only the wrapped ODK + the customer-key MD5 in the manifest (gated behind an