From 72853a3bbfce27c55e6c316ee446ad35e6786260 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 12 Dec 2025 23:21:51 -0800 Subject: [PATCH] fix: prevent path doubling in versioned object listing (#7729) * fix: prevent path doubling in versioned object listing Fix path doubling bug in getLatestVersionEntryForListOperation that caused Velero/Kopia backups to fail when using S3 bucket versioning. The issue was that when creating logical entries for versioned object listing, the entry.Name was set to the full object path (e.g., 'kopia/logpaste/kopia.blobcfg') instead of just the base filename ('kopia.blobcfg'). When this entry was used in the list callback which combines dir + entry.Name, the paths got doubled: '/buckets/velero/kopia/logpaste/kopia/logpaste/kopia.blobcfg' This caused Kopia to fail loading pack indexes with the error: 'unable to load pack indexes despite 10 retries' The fix uses path.Base(object) to extract only the filename portion, matching how regular (non-versioned) entries work in the listing callback. Fixes: GitHub discussion #7573 * refactor: use path.Base directly in test instead of reimplementing Address code review feedback to simplify the test by using the standard library path.Base function directly instead of reimplementing it. * remove test: unit test for path.Base doesn't add much value --- weed/s3api/s3api_object_handlers_list.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/weed/s3api/s3api_object_handlers_list.go b/weed/s3api/s3api_object_handlers_list.go index a4e844df8..4d91efeb8 100644 --- a/weed/s3api/s3api_object_handlers_list.go +++ b/weed/s3api/s3api_object_handlers_list.go @@ -7,6 +7,7 @@ import ( "io" "net/http" "net/url" + "path" "sort" "strconv" "strings" @@ -729,8 +730,10 @@ func (s3a *S3ApiServer) getLatestVersionEntryForListOperation(bucket, object str // Create a logical entry that appears to be stored at the object path (not the versioned path) // This allows the list operation to show the logical object name while preserving all metadata + // Use path.Base to get just the filename, since the entry.Name should be the local name only + // (the directory path is already included in the 'dir' parameter passed to eachEntryFn) logicalEntry := &filer_pb.Entry{ - Name: strings.TrimPrefix(object, "/"), + Name: path.Base(object), IsDirectory: false, Attributes: latestVersionEntry.Attributes, Extended: latestVersionEntry.Extended,