Commit Graph

487 Commits

Author SHA1 Message Date
Jakob van Santen
22f04312a7 fix: cleanup tempfiles when error prevents calling link
When a client provides an invalid or incomplete body for a single-part
upload, the handler returns before the link stage.  Factor tempfile
removal into cleanup() to catch all cases.
2026-03-20 08:25:44 -07:00
Jakob van Santen
56cb36d45a feat: add option to disable copy-file-range for multipart uploads
The standard io.Copy() will attempt to use copy_file_range when available.
However, this can cause problems with certain filesystems. Add an option
that will prevent io.Copy() from being able to use copy_file_range to force
a standard data copy between file descriptors when needed for completing
multipart uploads.
2026-03-20 08:23:01 -07:00
Ben McClelland
ae411fa3c1 Merge pull request #1974 from jvansanten/no-truncate
Always write complete files in sidecar meta provider
2026-03-19 16:01:50 -07:00
Ben McClelland
88e6396950 Merge pull request #1889 from versity/ben/fast-walk
feat: implement quicker backend/posix walk algorithm
2026-03-19 16:00:54 -07:00
tonyipm
33917ad6f3 feat: implement quicker backend/posix walk algorithm
Rewrite the posix Walk implementation to avoid the extra ReadDir per
directory that was noted as a TODO in the old code.  The new algorithm
holds all traversal state in a walkState struct and uses processDir to
interleave sibling entries in correct S3 lexicographic order without a
second syscall.

Key changes:
prefix optimisation: jump directly into the deepest matching directory
rather than scanning from the root on every call

marker short-circuit: skip entire subtrees that are lexically before
the marker, making paginated listing faster

Co-authored-by: Ben McClelland <ben.mcclelland@versity.com>
2026-03-18 17:39:16 -07:00
Ben McClelland
02f925a84b Merge pull request #1978 from 57-Wolve/57-Wolve-patch-s3-glacier-response
Update scoutfs glacier stage constants for ongoing requests
2026-03-18 10:25:06 -07:00
c32ddfff1a Update stage constants for ongoing requests
Fix issue with incompatible S3 response for offline and staging status.

Resolves issue with Restic Glacier support.
2026-03-18 09:46:55 -05:00
Jakob van Santen
d8f927519a Do not expose directories that can't be buckets 2026-03-18 15:33:10 +01:00
Jakob van Santen
0559807783 Always write complete files in sidecar meta provider
Some filesystems like dCache don't allow file truncation
2026-03-18 15:29:29 +01:00
Ben McClelland
97cc6bf23b chore: run go modernize tool
This is a fixup of the codebase using:
go run golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest -fix ./...

This has no bahvior changes, and only updates safe changes for
modern go features.
2026-03-10 09:47:37 -07:00
Ben McClelland
8795c15621 feat: s3proxy default to credential chain with optional anonymous access
When access/secret are not provided, let AWS SDK v2 resolve credentials
from the default provider chain (env vars, IRSA, ECS/EC2 roles, etc.)
instead of forcing anonymous credentials.

Add an explicit anonymous credentials option for s3 proxy to force
backend anonymous access.

Fixes #1955
2026-03-09 17:41:50 -07:00
Ben McClelland
0f92d0ef4f Merge pull request #1916 from versity/ben/azure-test-falures
azure test failure fixes
2026-03-05 17:07:41 -08:00
niksis02
97bb70509f fix: change the way object metadata is stored in posix
Fixes #1909

Previously, the mapping between object metadata and posix object was as follows: for each metadata key, we stored a separate xattr with the `user.X-Amz-Meta.<key>` prefix. This resulted in syscall overhead when storing and deleting large numbers of metadata keys.
In addition, very long metadata keys caused failures because most posix filesystems limit xattr key lengths to 127–255 bytes, while S3 does not enforce such a per-key limit.

The logic has now been changed so that all object metadata is stored in a single xattr, `user.metadata`, as a JSON key/value object. For backward compatibility, metadata GET operations still fall back to the old mechanism (`metadata key -> xattr key`) when `user.metadata` is not present.

A new CLI utility has been added to convert all legacy object metadata to the new metadata format within the provided directory.

**Example usage:**

```
versitygw utils convert-xattr-metadata path/to/bucket
```

or

```
versitygw utils cxm path/to/bucket
```

It is recommended to run this command on bucket directories to convert all legacy metadata for every object in the bucket.
2026-03-06 01:44:14 +04:00
Ben McClelland
afbeb7cb6e fix: azure modernize part number loop check
The part number loop check can be simplified with slices.Contains.
2026-03-03 08:58:31 -08:00
Ben McClelland
271313b036 fix: azure close download body in CopyObject to prevent resource leak
When copying between two different Azure blobs, the source download
stream body was only consumed by PutObject but never explicitly closed.
If PutObject or any subsequent step returned an error, the underlying
HTTP connection held by the Azure SDK was never released, leaking both
the connection and any internal SDK retry goroutines attached to it.
Added a deferred close on downloadResp.Body immediately after the
successful DownloadStream call to ensure the body is always drained and
released regardless of the outcome.
2026-03-03 08:58:31 -08:00
Ben McClelland
dc31696e53 fix: azure ListBuckets pagination to use client-side continuation tokens
Azure's ListContainers Marker parameter requires an opaque internal token
(e.g. /accountname/containername) rather than a plain container name, so
passing MaxResults and our ContinuationToken directly to the Azure API
caused 400 OutOfRangeInput errors. Rework ListBuckets to iterate all Azure
pages client-side, skip entries at or before the ContinuationToken (matching
the posix backend's "start after" semantics), and stop once MaxBuckets items
have been collected, setting ContinuationToken to the last returned bucket
name. This avoids using Azure's NextMarker entirely and correctly handles
both unpaginated and paginated requests.
2026-03-03 08:58:31 -08:00
Ben McClelland
929048cbee fix: azure PresignedAuth_UploadPart test failure
Azure Storage's StageBlock REST API rejects Content-Length: 0
with InvalidHeaderValue. The tests (PresignedAuth_UploadPart,
UploadPart_success) upload a nil/empty body, which causes the
Azure SDK to send Content-Length: 0. Azurite is lenient and
accepts it; real Azure Storage does not.

Use a new metadata key ("Zerobytesparts") sett on the
.sgwtmp/multipart/<uploadId>/<object-hash> blob to track and
0 length parts.
2026-03-03 08:58:22 -08:00
Ben McClelland
fc52052e33 Merge pull request #1910 from versity/sis/posix-dir-obj-metadata-tagging
feat: add tagging support for directory objects in posix
2026-03-02 13:05:51 -08:00
Ben McClelland
75f5db445f Merge pull request #1905 from versity/ben/test-walk-versions
fix: improve WalkVersions() ancestor-directory guard for prefix filtering
2026-03-02 12:55:33 -08:00
niksis02
8550dba36f feat: add tagging support for directory objects in posix
Closes #1857

Adds object Tagging support for directory objects in `PutObject` posix. Updates the integration tests to test object metadata and tagging both for file and directory objects.
2026-03-02 18:56:50 +04:00
Ben McClelland
62da7be9cd Merge pull request #1907 from versity/ben/posix-cleanup
chore: fix typos and error return wrapping types in posix
2026-03-02 05:44:07 -08:00
Ben McClelland
fc5c0a36a6 chore: fix typos and error return wrapping types in posix
This is just a cleanup of typos, error messages and error types
to correctly wrap the returned errors in the posix backend. No
major logic changes.
2026-02-28 10:38:29 -08:00
Ben McClelland
7695be56b0 fix: store part checksums at destination path in UploadPartCopy
In sidecar mode, the three StoreAttribute/storeChecksums calls after the
copy loop were using objPath (the source object path) instead of the
destination part's bucket and partPath. This caused checksums and the
internal part-crc64nvme to be written under the source object's sidecar
directory, making them unresolvable when CompleteMultipartUploadWithCopy
tried to retrieve them. All three stores now use *upi.Bucket and partPath,
consistent with the etag store directly below them.
2026-02-28 10:24:15 -08:00
Ben McClelland
760f252936 fix: improve WalkVersions() ancestor-directory guard for prefix filtering
Replace the length-comparison condition with an explicit predicate that
is both more readable and correctly scoped: skip only when the visited
directory is a strict ancestor of the specified prefix (not a descendant
and not when prefix is empty).

Adds tests from the original bug report (#1864) to verify the fix and
guard against future regressions.
2026-02-28 10:10:17 -08:00
Ben McClelland
ca3c76b0f9 Merge pull request #1902 from s3-on-win/fix-1864-ListObjectVersions
fix: not return parent keys for ListObjectVersions
2026-02-28 10:00:43 -08:00
Ben McClelland
809ba9e580 Merge pull request #1899 from versity/sis/upload-part-crc64nvme
feat: optimize multipart upload checksum calculation.
2026-02-28 09:55:34 -08:00
Ben McClelland
98acad9c99 Merge pull request #1887 from versity/sis/complete-mp-checksum
fix: store final checksum on CompleteMultipartUpload
2026-02-28 09:54:55 -08:00
Ben McClelland
0ad928a4d8 Merge pull request #1894 from versity/sis/getobject-directory-object-checksum
feat: adds checksums for directory objects in posix
2026-02-28 09:39:51 -08:00
Kai Hambrecht
8234c317b8 Fix versitygw#1864 to not return parent keys for ListObjectVersions with prefix 2026-02-27 15:55:42 +01:00
niksis02
d03a33110d feat: optimize multipart upload checksum calculation.
This PR optimizes multipart upload checksum handling. When a checksum algorithm/type is specified at multipart-upload initiation, each `UploadPart` request computes, validates, and stores the corresponding part checksum. During `CompleteMultipartUpload`, the final checksum is derived either via composite checksum calculation or by composing the CRC-family checksums.

When **no** checksum algorithm is specified during multipart-upload initiation, each `UploadPart` may supply a different checksum algorithm for data-integrity verification. To support this scenario, a new mechanism has been implemented: for every `UploadPart`, a **crc64nvme** checksum is always computed.

* If the client uses crc64nvme for the part upload, a single hash reader is used.
* Otherwise, two hash readers are used—one for crc64nvme and one for the user-provided checksum.

The crc64nvme value is stored in part xattrs under `user.part-crc64nvme` and later used during `CompleteMultipartUpload` as a composable checksum source.

In `CompleteMultipartUpload`, the hash reader is entirely removed; the gateway no longer re-reads part data to compute the final checksum. The logic now follows two distinct paths:

1. **Checksum algorithm/type specified at MP initiation**

   * All required per-part checksums have already been stored.
   * If the checksum type is `FULL_OBJECT`, the gateway uses the composable path.
   * If the type is `COMPOSITE`, the gateway follows the checksum-combining path.

2. **No checksum algorithm specified at MP initiation**

   * The gateway loads the stored per-part `crc64nvme` values and composes them to compute the final checksum.

The previous `composableCRC` check has been removed because all `FULL_OBJECT` algorithms are inherently composable (`crc32`, `crc32c`, `crc64nvme`). Validation now relies solely on `checksum.Type`.
2026-02-27 15:13:21 +04:00
niksis02
4ebe40829e fix: store final checksum on CompleteMultipartUpload
Previously, if no object checksum type/algorithm was specified when initiating a multipart upload, the CompleteMultipartUpload request would compute the final object’s CRC64NVME checksum but not persist it. This logic has now been fixed, and in the scenario described above the checksum is stored on the final object. There should no longer be any case where a CompleteMultipartUpload request finishes without persisting the final object checksum.
2026-02-27 15:12:57 +04:00
Ben McClelland
b3eac9781f feat: add concurrency limiter to scoutfs
This brings scoutfs in-line with the posix concurrency limiter.
This fixes a hang with scoutfs due to not correctly initializing
the concurrency in posix leading to a concurrency of 0 allowed.

This also adds a sane default to the posix concurrency when not
initialized.
2026-02-26 17:34:29 -08:00
niksis02
24364754fd feat: adds checksums for directory objects in posix
Add data-integrity checksum support in `PutObject` in the POSIX backend for directory objects. Since the only way to upload a directory object is via `PutObject`, this logic validates and stores the checksum of the empty payload. Support for `GetObject` has also been added to retrieve and return directory-object checksums.
2026-02-26 22:36:56 +04:00
Ben McClelland
e2821fc855 feat: add option to disable s3proxy client data integrity checks
AWS introduced a relatively newer option for data integrity checks
that not all non-AWS server support yet. See this for mmore info:
https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html

This change adds a new option: disable-data-integrity-check
to disable the data integrity checks in the client sdk for the
servers that may not yet support this. Use this only when the s3
service for the proxy does not support the data integrity features.

Fixes #1867
2026-02-21 11:49:20 -08:00
niksis02
46bcc8af35 fix: fixes object default Content-Type
Fixes #1849

If no `Content-Type` is provided during object upload, S3 defaults it to `application/octet-stream`. This behavior was missing in the gateway, causing backends to persist an empty `Content-Type`, which Fiber then overrides with its default `text/plain`. The behavior has now been corrected for the object upload operations: `PutObject`, `CreateMultipartUpload`, and `CopyObject`.
2026-02-18 01:44:52 +04:00
niksis02
4b11f540cb feat: add posix concurrency-limiter
Closes #1815

Implements posix actions concurrency limiter. Since posix actions perform filesystem-heavy syscalls, a semaphore-based limiter is introduced to cap the maximum number of concurrent posix actions. When the limit is reached, additional action calls block until a slot becomes available.

For internal posix calls, the `no_acquire_slot` context key is used to prevent acquiring the limiter multiple times within a single action (e.g., PutObject internally calling PutObjectLegalHold).

The posix concurrency limit can be configured via the gateway posix subcommand flag (--concurrency) or the environment variable `VGW_POSIX_CONCURRENCY`. The default value is `5000`.
2026-02-17 14:59:32 +04:00
Andrii Bratanin
9c212997dc feat: allow anonymous access for s3proxy backend
* Update client.go to support anonymous S3 access

* Update s3.go to make access and secret parameters optional

* Update example.conf for more clear S3 access and secret usage

Fixes #1836
2026-02-11 11:03:02 -08:00
Ben McClelland
3856f99904 Merge pull request #1839 from versity/sis/deleteobject-if-match-etag-quotes
fix: fixes DeleteObject if-match quoted comparison
2026-02-11 08:18:35 -08:00
niksis02
89aa822a40 fix: fixes DeleteObject if-match quoted comparison
Fixes #1835

If-Match in DeleteObject is a precondition header that compares the client-provided ETag with the server-side ETag before deleting the object. Previously, the comparison failed when the client sent an unquoted ETag, because server ETags are stored with quotes. The implementation now trims quotes from both the input ETag and the server ETag before comparison to avoid mismatches. Both quoted and unquoted ETags are valid according to S3.
2026-02-11 16:45:36 +04:00
Ben McClelland
e702a4860a fix: CopyObject with URL-encoded special chars
CopyObject was failing with NoSuchKey when source keys contained special
characters like {} or spaces. The X-Amz-Copy-Source header is URL-encoded
by clients, but ParseCopySource wasn't decoding before filesystem access.

Added url.QueryUnescape() to properly decode bucket and object names,
fixing copy operations for keys with special characters.

Fixing this also uncovered an errors with azure blob url encoding with
similar special character handling. Added this fix in for the integration
tests to pass.

Fixes #1832
Fixes #1637
2026-02-10 14:55:18 -08:00
Ben McClelland
e805003872 Merge pull request #1816 from versity/sis/max-limiter-errors
fix: fixes list-limiters parsing and validation
2026-02-06 09:28:18 -08:00
Ben McClelland
b2a9b383ae Merge pull request #1803 from versity/sis/list-mp-delimiter
feat: adds delimiter support in ListMultipartUploads
2026-02-06 09:27:32 -08:00
niksis02
2365f9f1ae fix: fixes list-limiters parsing and validation
Fixes #1809
Fixes #1806
Fixes #1804
Fixes #1794

This PR focuses on correcting so-called "list-limiter" parsing and validation. The affected limiters include: `max-keys`, `max-uploads`, `max-parts`, `max-buckets`, `max-uploads` and `part-number-marker`. When a limiter value is outside the integer range, a specific `InvalidArgument` error is now returned. If the value is a valid integer but negative, a different `InvalidArgument` error is produced.

`max-buckets` has its own validation rules: completely invalid values and values outside the allowed range (`1 <= input <= 10000`) return distinct errors. For `ListObjectVersions`, negative `max-keys` values follow S3’s special-case behavior and return a different `InvalidArgument` error message.

Additionally, `GetObjectAttributes` now follows S3 semantics for `x-amz-max-parts`: S3 ignores invalid values, so the gateway now matches that behavior.
2026-02-06 14:21:56 +04:00
niksis02
2e6794007c feat: adds delimiter support in ListMultipartUploads
Fixes #1792
Fixes #1747
Fixes #1797
Fixes #1799

This PR primarily introduces delimiter support and several bug fixes for the `ListMultipartUploads` action in the POSIX and Azure backends. Delimiter handling is now implemented — when a delimiter is present in multipart-upload object key names, the backend collects and returns the appropriate common prefixes.
This functionality is achieved by introducing a common multipart-upload lister in the backend package. All backends (Azure, POSIX) now use this lister. The lister accepts a list that is already sorted and filtered by `KeyMarker` and `Prefix`.

Previously, the `KeyMarker` was required to exactly match an existing multipart-upload object key. This restriction is removed. The listing now relies on a lexicographical comparison between the provided `KeyMarker` and existing multipart-upload object keys.

Validation for `UploadIdMarker` is also added to correctly return an `InvalidArgument` error for invalid upload IDs. If `KeyMarker` is missing, the `UploadIdMarker` is ignored entirely. If `KeyMarker` is provided, a valid upload ID is one that matches an upload belonging to *the first object key after the KeyMarker*. For example, if the `KeyMarker` is `foo`, but the provided `UploadIdMarker` corresponds to an upload under `quxx`, it is invalid. It must match one of the uploads for the next object key equal to `foo`.

Finally, this PR fixes multipart-upload sorting. Multipart uploads must be sorted primarily lexicographically by their object key, and secondarily—when multiple uploads share the same object key—by their initiation time in ascending order.
2026-02-06 14:16:16 +04:00
John W Higgins
bcf341bdaa Add "lexical" sort to walker.go 2026-02-01 16:10:23 -08:00
John W Higgins
73e2df4105 Base import of cutdown version of io/fs/walk.go from golang as walker.go 2026-01-29 13:10:01 -08:00
John W Higgins
bef8f38f3e Revert of 34da1833 and 8e18b431 for backend/walk.go
Revert the changes with regards to lexical sorting but only for the walk.go
code itself. Leave the tests alone.
2026-01-29 13:10:01 -08:00
niksis02
7017ffa2a3 fix: removes object metadata loading from posix ListParts
In the POSIX `ListParts` implementation, there was a code snippet that loaded the object metadata, even though it wasn’t needed and never used in the response. This redundant code has now been removed.
2026-01-28 21:12:56 +04:00
khambrecht
f29206afb6 fix: put object on windows when parent directories dont already exists
The previous logic was not allowing put-object on windows when the parent directory did not already exist, and would
not always return the correct error if an ancestor in the path already existed as a file.

The problem is the different behavior of the os.Stat command in Windows compared to *nix in backend/posix/posix.go
in function PutObjectWithPostFunc. The os.Stat returns ENOTDIR on *nix if the parent object is a file instead of a
directory. On Windows, if the parent object does not exist at all, the return code of such os.Stat is ERROR_PATH_NOT_FOUND which is mapped to ENOTDIR. However this is inappropriate in this case. As a result, the
return code of the os.Stat is incorrectly interpreted as if the parent object is a file instead of the parent object does not
exist. Which then leads to a failed upload.

This fix validates the existing parent structure on put to make sure the correct error is returned or the put is successful.

Fixes #1702
2026-01-27 08:51:06 +09:00
Ben McClelland
01fc142c1e fix: correct spelling for debuglogger.InternalError() (#1784) 2026-01-24 06:44:54 -08:00