Only the Local backend refused a key identifier containing a path
separator. On Vault KV2 a create with the name bad/name succeeded and
produced a nested KV2 path that the listing then reported as a
directory rather than a key, and an identifier containing .. addressed
a record outside the configured key prefix once the HTTP client
normalised the URL; Vault Transit built its transit key name and its
metadata path from the same unchecked identifier.
Lift the Local backend's containment rule into a shared segment check
(empty, /, backslash, NUL, . and ..) and apply it at the single point
where each backend turns the identifier into a path or a Transit key
name, so create, describe, encrypt, delete and the metadata writes all
refuse with InvalidKey before any request reaches Vault. The admin API
already maps that to 400. The AWS backend is untouched: it addresses
keys by ARN and alias, both of which contain /.
Refs rustfs/backlog#2474 (KMS-213 CreateNegatives on vault-kv2).
Co-authored-by: Hauser <housemecn@gmail.com>
* fix(replication): accept a bodiless 405 as a replicated delete marker during resync
A resync verifies each delete marker with `HEAD ?versionId=<marker>` on the
target. S3 targets (RustFS, MinIO, AWS) answer that with 405 and no body,
and the SDK only synthesizes an error code for 404, so the error arrived
with `code() == None`. `is_retryable_delete_replication_head_error` then
treated it as an ambiguous failure: every delete marker counted as a failed
object with `target service error`, and a site resync over any bucket that
holds a delete marker reported the whole bucket as failed
(rustfs/backlog#2479, SITE-105 on rc.6 and nightly).
Use the raw HTTP status the way the 404 path already does: a 405 without a
code confirms the marker propagated. Ambiguous statuses still fail.
- Unit tests drive `verify_resync_head_result` against a scripted target
answering 405 (accepted) and 503 (still failed).
- e2e `test_site_replication_resync_replicates_delete_marker` joins two
sites, converges a live object and a delete marker, and requires the site
resync to complete with zero failed objects; the repl-nightly selection
digest is refreshed for the new case.
* fix(replication): verify marker-version purges by absence during resync
A delete-marker resync entry with a pending or failed version purge asks
the target to remove the marker, so the bodiless 405 that proves a
created marker propagated proves the purge did not happen. Accept the
405-as-success mapping only for marker creation (empty
version_purge_status); a purge counts as replicated only when the target
answers not found, and any other HEAD outcome stays failed. Unit tests
drive both purge statuses against the 405 fixture and the 404 fixture.
* fix(io-metrics): drop the rustfs-common edge by injecting the S3 telemetry observer
dc700d366 (#7775) made the io-metrics leaf crate depend on rustfs-common
to publish typed telemetry trace events from S3HttpRequestGuard, which
breaks the backlog#1834 leaf-crate rule (io-metrics may only depend on
rustfs-s3-ops).
S3HttpRequestGuard now takes an optional fn-pointer completion observer
(operation, latency, 2xx?) and knows nothing about the trace bus. The
server builds guards through rustfs::server::s3_http_request_guard, which
attaches the observer only while a telemetry subscriber exists and owns
the S3Operation -> TelemetryTraceOperation mapping.
* chore(guard): admit #7785 s3s ratchet growth (+1 file, +4 lines)
#7785 landed the gateway key inventory admin handler after the
baselines were verified, leaving check_s3s_footprint.sh red on main
and every branch cut from it (measured 210 files / 1592 lines vs
209/1588 baselines).
The handler follows the house admin convention whose Operation::call
signature is s3s-typed (S3Request/S3Result), so it cannot route
through a non-s3s seam until the s3gate admin migration replaces the
admin router (rustfs/backlog#1677 F1); no local refactor can shed the
file-level import. Record the measured growth with rationale:
files 209 -> 210, s3_error! lines 1588 -> 1592.
* fix
* fix: resolve clippy gate failures in connect and CLI surfaces
- Box the two large CommandResult performance variants and
LicenseRenewalOutcome::Installed (clippy::large_enum_variant)
- Return RelayError instead of () from RelayTransport::deliver and the
test destination (clippy::result_unit_err)
- Drop an unused mut on the protected relay file handle (unused_mut)
- Pass the digest by value to base64 encoding and drop a redundant
result_json clone (clippy::needless_borrows_for_generic_args,
clippy::redundant_clone)
* fix(connect): resolve the remaining site-replication clippy errors
- Group the endpoint credential triple into SiteReplicationCredentials
so SiteReplicationEndpoint::new takes 6 arguments
(clippy::too_many_arguments)
- Take the build-feature validator by &str (clippy::ptr_arg); the
validator closure stays because &String needs the deref coercion
* style: apply rustfmt to the site-replication credential grouping
* fix(connect): keep the CPU profile fixture off the ecstore import scan
#7811 used "rustfs_ecstore::disk::read_object" as the raw-symbol fixture
of the profile redaction test, which the architecture guard counts as a
direct rustfs_ecstore reference outside the compatibility boundaries
(its scan deliberately includes inline test modules). The accumulator
treats symbols opaquely, so use the internal rustfs::storage:: path that
keeps the fixture realistic without matching the import scanner.
* fix
* fix
* fix
* fix
---------
Co-authored-by: Hauser <housemecn@gmail.com>
* fix(connect): restore the site replication producer build
The producer shadowed its object_url helper with a local of the same
name, and its test placed the consent nonce on the request, so neither
the rustfs library nor its tests compiled on main.
* feat(server): add RUSTFS_S3_STACK with a gateway GetBucketLocation path
RUSTFS_S3_STACK=legacy (default) keeps the s3s service as the whole S3
entry. RUSTFS_S3_STACK=gateway routes GetBucketLocation through the
RustFS Gateway pipeline pinned at rustfs/gateway@90b83a20 and falls back
to the same s3s service for every other request, decided before the
body is read. Refs rustfs/backlog#1752.
GET /rustfs/admin/v3/gateway-key-inventory (admin:InspectData) lists
every stored object key the RustFS S3 gateway would refuse on every
operation, so an operator can copy those objects to a safe key before
switching stacks. The rules mirror the gateway key floor; . and ..
segments never appear because ecstore refuses them on every write.
Refs rustfs/gateway#754
Replace s3s::dto::{RestoreStatus, Timestamp} in filemeta with a
filemeta-owned RestoreStatus (same field names, OffsetDateTime expiry).
RestoreStatusOps and parse_restore_obj_status keep their signatures and
persisted rendering; the ecstore restore finalize / lifecycle restore
writers and rustfs RestoreObject now build the filemeta type.
- crates/filemeta: drop s3s; tokio "time" becomes a dev-dependency
(the metacache tests got it through s3s feature unification).
- metadata_keys: drop the s3s half of the historical-source cross-check;
PINNED, the pre-A3a fixture and per-character mutation still pin keys.
- new test re-renders the pre-A3a fixture restore value byte-for-byte.
- s3s footprint baselines 210 -> 209 files, ecstore 37 -> 36.
Refs rustfs/backlog#1735
#7769 left rustfs/src/connect/diagnostics/perf_site_replication.rs failing
to compile: a local binding named object_url shadowed the object_url()
helper in the same scope (E0618), and a test put the consent nonce on the
request instead of LocalSiteReplicationConsent (E0063/E0560).
Rename the binding to source_url and move the test nonce into the consent.
Migrate ecstore and rustfs-lifecycle consumers of the persisted xl.meta
meta_user keys (object lock, restore, replication status, storage class,
SSE read) from s3s::header / rustfs_utils header constants to
rustfs_filemeta::metadata_keys. HTTP header production is unchanged and
every lookup keeps its previous exact / case-insensitive mode.
- SSE key: persisted only as lowercase; the mixed-case spelling is
outbound replication user metadata. Keep the case-insensitive read and
pin it against the pre-A3a fixture.
- warm tier: strip promoted keys case-insensitively so the persisted
X-Amz-Replication-Status is no longer forwarded to the tier.
- fix stale HashReader::add_checksum_from_s3s call in ecstore tests.
Refs rustfs/backlog#1735