Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions cmd/metacache-server-pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,21 @@ func (z *erasureServerPools) listPath(ctx context.Context, o *listPathOptions) (
if o.Marker != "" && o.Prefix != "" {
// Marker not common with prefix is not implemented. Send an empty response
if !HasPrefix(o.Marker, o.Prefix) {
return entries, io.EOF
return entries, z.listPathShortcutEOF(ctx, o.Bucket)
}
}

// With max keys of zero we have reached eof, return right here.
if o.Limit == 0 {
return entries, io.EOF
return entries, z.listPathShortcutEOF(ctx, o.Bucket)
}

// For delimiter and prefix as '/' we do not list anything at all
// along // with the prefix. On a flat namespace with 'prefix'
// as '/' we don't have any entries, since all the keys are
// of form 'keyName/...'
if strings.HasPrefix(o.Prefix, SlashSeparator) {
return entries, io.EOF
return entries, z.listPathShortcutEOF(ctx, o.Bucket)
}

// If delimiter is slashSeparator we must return directories of
Expand Down Expand Up @@ -254,6 +254,21 @@ func (z *erasureServerPools) listPath(ctx context.Context, o *listPathOptions) (
return entries, nil
}

// listPathShortcutEOF is used by the listPath shortcuts that return an empty
// result without ever consulting the storage layer (a slash-only prefix, a
// zero limit, or a marker outside the prefix). Because they bypass the per-set
// listing - which is what surfaces a missing bucket as errVolumeNotFound - they
// must verify bucket existence explicitly, otherwise a listing on a
// non-existent bucket is masked as an empty result. AWS S3 returns NoSuchBucket
// regardless of the prefix/marker/max-keys supplied.
// See https://github.com/pgsty/minio/issues/32
func (z *erasureServerPools) listPathShortcutEOF(ctx context.Context, bucket string) error {
if _, err := z.GetBucketInfo(ctx, bucket, BucketOptions{}); err != nil {
return err
}
return io.EOF
}

// listMerged will list across all sets and return a merged results stream.
// The result channel is closed when no more results are expected.
func (z *erasureServerPools) listMerged(ctx context.Context, o listPathOptions, results chan<- metaCacheEntry) error {
Expand Down
35 changes: 35 additions & 0 deletions cmd/object_api_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,41 @@ func testListObjectsTestsForNonExistentBucket(obj ObjectLayer, instanceType stri
}
}

// Wrapper for calling testListObjectsWithPrefixForNonExistentBucket for both Erasure and FS.
func TestListObjectsWithPrefixForNonExistentBucket(t *testing.T) {
ExecObjectLayerTest(t, testListObjectsWithPrefixForNonExistentBucket)
}

// Tests validate that ListObjects on a non-existent bucket fails with
// BucketNotFound even when a prefix (or other listing shortcut) is supplied.
// AWS S3 returns NoSuchBucket regardless of the prefix/marker/max-keys values.
// Regression: https://github.com/pgsty/minio/issues/32
func testListObjectsWithPrefixForNonExistentBucket(obj ObjectLayer, instanceType string, t TestErrHandler) {
testCases := []struct {
prefix string
marker string
maxKeys int
}{
{prefix: "/", maxKeys: 1000}, // slash-only prefix shortcut (the issue reproduction)
{prefix: "foo/bar", maxKeys: 1000}, // regular prefix
{prefix: "obj", maxKeys: 1}, // max-keys==1 shortcut
{prefix: "obj", maxKeys: 0}, // zero-limit shortcut
{prefix: "a", marker: "b", maxKeys: 1000}, // marker outside the prefix shortcut
}
for i, tc := range testCases {
result, err := obj.ListObjects(context.Background(), "bucket", tc.prefix, tc.marker, "", tc.maxKeys)
if err == nil {
t.Fatalf("%s: test %d (prefix %q): Expected error but found nil.", instanceType, i+1, tc.prefix)
}
if len(result.Objects) != 0 {
t.Fatalf("%s: test %d (prefix %q): Expected 0 objects, but found %d", instanceType, i+1, tc.prefix, len(result.Objects))
}
if err.Error() != "Bucket not found: bucket" {
t.Errorf("%s: test %d (prefix %q): Expected error msg `%s`, but found `%s`", instanceType, i+1, tc.prefix, "Bucket not found: bucket", err.Error())
}
}
}

// Wrapper for calling testNonExistentObjectInBucket for both Erasure and FS.
func TestNonExistentObjectInBucket(t *testing.T) {
ExecObjectLayerTest(t, testNonExistentObjectInBucket)
Expand Down