diff --git a/registry/storage/driver/s3-aws/s3.go b/registry/storage/driver/s3-aws/s3.go index 161b94fd3..29af8b070 100644 --- a/registry/storage/driver/s3-aws/s3.go +++ b/registry/storage/driver/s3-aws/s3.go @@ -1178,7 +1178,7 @@ func (d *driver) doWalk(parentCtx context.Context, objectCount *int64, from, sta for _, walkInfo := range walkInfos { // skip any results under the last skip directory - if prevSkipDir != "" && strings.HasPrefix(walkInfo.Path(), prevSkipDir) { + if isSubpath(walkInfo.Path(), prevSkipDir) { continue } @@ -1249,6 +1249,19 @@ func directoryDiff(prev, current string) []string { return paths } +func isSubpath(path, parent string) bool { + if parent == "" { + return false + } + if path == parent { + return true + } + if parent == "/" { + return strings.HasPrefix(path, "/") + } + return strings.HasPrefix(path, parent+"/") +} + func (d *driver) s3Path(path string) string { return strings.TrimLeft(strings.TrimRight(d.RootDirectory, "/")+path, "/") } diff --git a/registry/storage/driver/s3-aws/s3_test.go b/registry/storage/driver/s3-aws/s3_test.go index a4b303d3f..e5d6c8d5e 100644 --- a/registry/storage/driver/s3-aws/s3_test.go +++ b/registry/storage/driver/s3-aws/s3_test.go @@ -292,6 +292,55 @@ func TestGetS3LogLevelFromParam(t *testing.T) { } } +func TestIsSubpath(t *testing.T) { + tests := []struct { + name string + path string + parent string + expected bool + }{ + { + name: "empty parent", + path: "/tags/1.2", + parent: "", + expected: false, + }, + { + name: "same path", + path: "/tags/1.2", + parent: "/tags/1.2", + expected: true, + }, + { + name: "descendant path", + path: "/tags/1.2/current/link", + parent: "/tags/1.2", + expected: true, + }, + { + name: "sibling with lexical prefix", + path: "/tags/1.20/current/link", + parent: "/tags/1.2", + expected: false, + }, + { + name: "root parent", + path: "/tags/1.2", + parent: "/", + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := isSubpath(tt.path, tt.parent) + if actual != tt.expected { + t.Fatalf("isSubpath(%q, %q) = %t, want %t", tt.path, tt.parent, actual, tt.expected) + } + }) + } +} + func TestEmptyRootList(t *testing.T) { skipCheck(t)