Skip to content

fix(s3): fix file deletion on versioned S3 buckets#673

Merged
camathieu merged 1 commit intomasterfrom
fix/s3-delete-versioned-buckets
Mar 7, 2026
Merged

fix(s3): fix file deletion on versioned S3 buckets#673
camathieu merged 1 commit intomasterfrom
fix/s3-delete-versioned-buckets

Conversation

@camathieu
Copy link
Copy Markdown
Member

What

Fix S3 RemoveFile to properly delete objects from versioned S3-compatible
buckets (e.g. Backblaze B2 with "Keep all versions" enabled).

Why

Two bugs in the previous implementation:

  1. Dead legacy fallback: DeleteObject returns success even for non-existent
    keys, so the fallback from {uploadID}.{fileID} to legacy {fileID} naming
    never triggered.

  2. Versioned buckets: DeleteObject without a VersionID only creates a
    delete marker — actual data persists indefinitely, silently accumulating
    storage costs.

Changes

  • server/data/s3/s3.go — Rewrite RemoveFile with a new removeObject
    helper that uses StatObjectRemoveObject(VersionID) flow. Forwards SSE
    options for SSE-C compatibility.
  • server/data/s3/s3_integration_test.go — 5 new integration tests (new
    format, legacy fallback, idempotent, not found, prefix)
  • server/data/gcs/gcs_integration_test.go — 3 new integration tests
  • server/data/swift/swift_integration_test.go — 3 new integration tests
  • docs/backends/data.md — Add versioning warning note
  • testing/ARCHITECTURE.md — Document per-backend integration tests

Testing

  • make lint
  • make test ✅ (integration tests skip correctly without PLIKD_CONFIG)
  • make docs
  • make vuln
  • S3 integration tests against Backblaze B2 with versioning ✅
  • Server-level TestClean against B2 ✅
  • Manual upload → plikd file delete --all → bucket empty ✅

The S3 RemoveFile implementation had two bugs:

1. Dead legacy-fallback code: S3's DeleteObject API returns success
   even for non-existent objects, so the fallback from new to legacy
   object naming never triggered.

2. Versioned bucket deletion: On S3-compatible stores with versioning
   enabled (e.g. Backblaze B2), DeleteObject without a VersionID only
   creates a delete marker — the actual data persists indefinitely.

Fix: introduce a removeObject helper that calls StatObject first to
check existence (enabling the legacy fallback) and captures the
VersionID, which is then passed to RemoveObject for permanent deletion.
SSE options are forwarded to StatObject for SSE-C compatibility.

Add per-backend storage-level integration tests for S3, GCS, and Swift
that verify objects are truly removed after RemoveFile. These tests
use t.Skip when PLIKD_CONFIG is not set, so they are transparent to
make test but can be run on demand against real backends.
@camathieu camathieu merged commit 51acb9c into master Mar 7, 2026
15 of 16 checks passed
@camathieu camathieu deleted the fix/s3-delete-versioned-buckets branch March 7, 2026 11:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant