From 5e484f23550b38ad341b34740d6e9f40eae7a5e4 Mon Sep 17 00:00:00 2001 From: jonaustin09 Date: Mon, 22 Jul 2024 11:30:32 -0400 Subject: [PATCH] fix: Fixed CopySource parsing to handle the values starting with '/' in CopyObject action in posix and azure backends. --- backend/azure/azure.go | 9 +++-- backend/posix/posix.go | 8 ++++- tests/integration/group-tests.go | 2 ++ tests/integration/tests.go | 60 ++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 3 deletions(-) diff --git a/backend/azure/azure.go b/backend/azure/azure.go index 89d1d78..5ecfe09 100644 --- a/backend/azure/azure.go +++ b/backend/azure/azure.go @@ -744,7 +744,12 @@ func (az *Azure) CopyObject(ctx context.Context, input *s3.CopyObjectInput) (*s3 return nil, azureErrToS3Err(err) } - if strings.Join([]string{*input.Bucket, *input.Key}, "/") == *input.CopySource && isMetaSame(res.Metadata, input.Metadata) { + cpSrc := *input.CopySource + if cpSrc[0] == '/' { + cpSrc = cpSrc[1:] + } + + if strings.Join([]string{*input.Bucket, *input.Key}, "/") == cpSrc && isMetaSame(res.Metadata, input.Metadata) { return nil, s3err.GetAPIError(s3err.ErrInvalidCopyDest) } @@ -758,7 +763,7 @@ func (az *Azure) CopyObject(ctx context.Context, input *s3.CopyObjectInput) (*s3 return nil, err } - resp, err := client.CopyFromURL(ctx, az.serviceURL+"/"+*input.CopySource, &blob.CopyFromURLOptions{ + resp, err := client.CopyFromURL(ctx, az.serviceURL+"/"+cpSrc, &blob.CopyFromURLOptions{ BlobTags: tags, Metadata: parseMetadata(input.Metadata), }) diff --git a/backend/posix/posix.go b/backend/posix/posix.go index e80aa1f..c5cab07 100644 --- a/backend/posix/posix.go +++ b/backend/posix/posix.go @@ -1938,7 +1938,13 @@ func (p *Posix) CopyObject(ctx context.Context, input *s3.CopyObjectInput) (*s3. if input.ExpectedBucketOwner == nil { return nil, s3err.GetAPIError(s3err.ErrInvalidRequest) } - srcBucket, srcObject, ok := strings.Cut(*input.CopySource, "/") + + cpSrc := *input.CopySource + if cpSrc[0] == '/' { + cpSrc = cpSrc[1:] + } + + srcBucket, srcObject, ok := strings.Cut(cpSrc, "/") if !ok { return nil, s3err.GetAPIError(s3err.ErrInvalidCopySource) } diff --git a/tests/integration/group-tests.go b/tests/integration/group-tests.go index 431bf11..fcd3819 100644 --- a/tests/integration/group-tests.go +++ b/tests/integration/group-tests.go @@ -197,6 +197,7 @@ func TestCopyObject(s *S3Conf) { CopyObject_not_owned_source_bucket(s) CopyObject_copy_to_itself(s) CopyObject_to_itself_with_new_metadata(s) + CopyObject_CopySource_starting_with_slash(s) CopyObject_success(s) } @@ -609,6 +610,7 @@ func GetIntTests() IntTests { "CopyObject_not_owned_source_bucket": CopyObject_not_owned_source_bucket, "CopyObject_copy_to_itself": CopyObject_copy_to_itself, "CopyObject_to_itself_with_new_metadata": CopyObject_to_itself_with_new_metadata, + "CopyObject_CopySource_starting_with_slash": CopyObject_CopySource_starting_with_slash, "CopyObject_success": CopyObject_success, "PutObjectTagging_non_existing_object": PutObjectTagging_non_existing_object, "PutObjectTagging_long_tags": PutObjectTagging_long_tags, diff --git a/tests/integration/tests.go b/tests/integration/tests.go index 1589e32..611f906 100644 --- a/tests/integration/tests.go +++ b/tests/integration/tests.go @@ -4215,6 +4215,66 @@ func CopyObject_to_itself_with_new_metadata(s *S3Conf) error { }) } +func CopyObject_CopySource_starting_with_slash(s *S3Conf) error { + testName := "CopyObject_CopySource_starting_with_slash" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + dataLength, obj := int64(1234567), "src-obj" + dstBucket := getBucketName() + if err := setup(s, dstBucket); err != nil { + return err + } + + csum, _, err := putObjectWithData(dataLength, &s3.PutObjectInput{ + Bucket: &bucket, + Key: &obj, + }, s3client) + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.CopyObject(ctx, &s3.CopyObjectInput{ + Bucket: &dstBucket, + Key: &obj, + CopySource: getPtr(fmt.Sprintf("/%v/%v", bucket, obj)), + }) + cancel() + if err != nil { + return err + } + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + out, err := s3client.GetObject(ctx, &s3.GetObjectInput{ + Bucket: &dstBucket, + Key: &obj, + }) + defer cancel() + if err != nil { + return err + } + if *out.ContentLength != dataLength { + return fmt.Errorf("expected content-length %v, instead got %v", dataLength, out.ContentLength) + } + + defer out.Body.Close() + + bdy, err := io.ReadAll(out.Body) + if err != nil { + return err + } + outCsum := sha256.Sum256(bdy) + if outCsum != csum { + return fmt.Errorf("invalid object data") + } + + if err := teardown(s, dstBucket); err != nil { + return err + } + + return nil + }) +} + func CopyObject_success(s *S3Conf) error { testName := "CopyObject_success" return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {