diff --git a/backend/posix/posix.go b/backend/posix/posix.go index b9efa56..e389593 100644 --- a/backend/posix/posix.go +++ b/backend/posix/posix.go @@ -2130,12 +2130,13 @@ func (p *Posix) fileToObj(bucket string) backend.GetObjFunc { return types.Object{}, fmt.Errorf("get fileinfo: %w", err) } - key := path + "/" + size := int64(0) return types.Object{ ETag: &etag, - Key: &key, + Key: &path, LastModified: backend.GetTimePtr(fi.ModTime()), + Size: &size, }, nil } diff --git a/backend/walk.go b/backend/walk.go index 157522a..7ffef17 100644 --- a/backend/walk.go +++ b/backend/walk.go @@ -95,7 +95,10 @@ func Walk(ctx context.Context, fileSystem fs.FS, prefix, delimiter, marker strin if err != nil { return fmt.Errorf("readdir %q: %w", path, err) } - if len(ents) == 0 { + + path += string(os.PathSeparator) + + if len(ents) == 0 && delimiter == "" { dirobj, err := getObj(path, d) if err == ErrSkipObj { return nil @@ -104,9 +107,13 @@ func Walk(ctx context.Context, fileSystem fs.FS, prefix, delimiter, marker strin return fmt.Errorf("directory to object %q: %w", path, err) } objects = append(objects, dirobj) + + return nil } - return nil + if len(ents) != 0 { + return nil + } } if !pastMarker { diff --git a/tests/integration/group-tests.go b/tests/integration/group-tests.go index f8ee085..6772b3d 100644 --- a/tests/integration/group-tests.go +++ b/tests/integration/group-tests.go @@ -180,6 +180,8 @@ func TestListObjectsV2(s *S3Conf) { ListObjectsV2_both_start_after_and_continuation_token(s) ListObjectsV2_start_after_not_in_list(s) ListObjectsV2_start_after_empty_result(s) + ListObjectsV2_both_delimiter_and_prefix(s) + ListObjectsV2_single_dir_object_with_delim_and_prefix(s) } func TestDeleteObject(s *S3Conf) { @@ -611,6 +613,8 @@ func GetIntTests() IntTests { "ListObjectsV2_both_start_after_and_continuation_token": ListObjectsV2_both_start_after_and_continuation_token, "ListObjectsV2_start_after_not_in_list": ListObjectsV2_start_after_not_in_list, "ListObjectsV2_start_after_empty_result": ListObjectsV2_start_after_empty_result, + "ListObjectsV2_both_delimiter_and_prefix": ListObjectsV2_both_delimiter_and_prefix, + "ListObjectsV2_single_dir_object_with_delim_and_prefix": ListObjectsV2_single_dir_object_with_delim_and_prefix, "DeleteObject_non_existing_object": DeleteObject_non_existing_object, "DeleteObject_non_existing_dir_object": DeleteObject_non_existing_dir_object, "DeleteObject_success": DeleteObject_success, diff --git a/tests/integration/tests.go b/tests/integration/tests.go index 7b87b52..8281858 100644 --- a/tests/integration/tests.go +++ b/tests/integration/tests.go @@ -3945,6 +3945,99 @@ func ListObjectsV2_start_after_empty_result(s *S3Conf) error { }) } +func ListObjectsV2_both_delimiter_and_prefix(s *S3Conf) error { + testName := "ListObjectsV2_both_delimiter_and_prefix" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + if err := putObjects(s3client, []string{ + "sample.jpg", + "photos/2006/January/sample.jpg", + "photos/2006/February/sample2.jpg", + "photos/2006/February/sample3.jpg", + "photos/2006/February/sample4.jpg", + }, bucket); err != nil { + return err + } + delim, prefix := "/", "photos/2006/" + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + res, err := s3client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{ + Bucket: &bucket, + Delimiter: &delim, + Prefix: &prefix, + }) + cancel() + if err != nil { + return err + } + + if res.Delimiter == nil || *res.Delimiter != delim { + return fmt.Errorf("expected the delimiter to be %v", delim) + } + if res.Prefix == nil || *res.Prefix != prefix { + return fmt.Errorf("expected the prefix to be %v", prefix) + } + if !comparePrefixes([]string{"photos/2006/February/", "photos/2006/January/"}, res.CommonPrefixes) { + return fmt.Errorf("expected the common prefixes to be %v, instead got %v", []string{"photos/2006/February/", "photos/2006/January/"}, res.CommonPrefixes) + } + if len(res.Contents) != 0 { + return fmt.Errorf("expected empty objects list, instead got %v", res.Contents) + } + + return nil + }) +} + +func ListObjectsV2_single_dir_object_with_delim_and_prefix(s *S3Conf) error { + testName := "ListObjectsV2_single_dir_object_with_delim_and_prefix" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + if err := putObjects(s3client, []string{"a/"}, bucket); err != nil { + return err + } + + delim, prefix := "/", "a" + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + res, err := s3client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{ + Bucket: &bucket, + Delimiter: &delim, + Prefix: &prefix, + }) + cancel() + if err != nil { + return err + } + + if !comparePrefixes([]string{"a/"}, res.CommonPrefixes) { + return fmt.Errorf("expected the common prefixes to be %v, instead got %v", []string{"a/"}, res.CommonPrefixes) + } + if len(res.Contents) != 0 { + return fmt.Errorf("expected empty objects list, instead got %v", res.Contents) + } + + prefix = "a/" + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + res, err = s3client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{ + Bucket: &bucket, + Delimiter: &delim, + Prefix: &prefix, + }) + cancel() + if err != nil { + return err + } + + if !compareObjects([]string{"a/"}, res.Contents) { + return fmt.Errorf("expected the object list to be %v, instead got %v", []string{"a/"}, res.Contents) + } + if len(res.CommonPrefixes) != 0 { + return fmt.Errorf("expected empty common prefixes, instead got %v", res.CommonPrefixes) + } + + return nil + }) +} + func DeleteObject_non_existing_object(s *S3Conf) error { testName := "DeleteObject_non_existing_object" return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {