CopyObject metadata (#265)

* fix: Object tag actions cleanup

* fix: Fixes #249, Changed ListObjects default max-keys from -1 to 1000

* fix: Fixes #250, Added support to provide a marker not from the objects list and list the objects after the provided marker in ListObjects(V2) actions

* feat: Closes #256, Addded a check step, to compare object metadatas and allow the copying to itself, if the metadata has been changed

* fix: Simplified range assignment in CopyObject posix function
This commit is contained in:
Jon Austin
2023-09-26 18:09:09 -07:00
committed by GitHub
parent dbcffb4984
commit 9cb357ecc5
4 changed files with 64 additions and 6 deletions

View File

@@ -449,6 +449,20 @@ func loadUserMetaData(path string, m map[string]string) (contentType, contentEnc
return
}
func compareUserMetadata(meta1, meta2 map[string]string) bool {
if len(meta1) != len(meta2) {
return false
}
for key, val := range meta1 {
if meta2[key] != val {
return false
}
}
return true
}
func isValidMeta(val string) bool {
if strings.HasPrefix(val, "user."+metaHdr) {
return true
@@ -1243,10 +1257,6 @@ func (p *Posix) CopyObject(ctx context.Context, input *s3.CopyObjectInput) (*s3.
dstObject := *input.Key
owner := *input.ExpectedBucketOwner
if fmt.Sprintf("%v/%v", srcBucket, srcObject) == fmt.Sprintf("%v/%v", dstBucket, dstObject) {
return &s3.CopyObjectOutput{}, s3err.GetAPIError(s3err.ErrInvalidCopyDest)
}
_, err := os.Stat(srcBucket)
if errors.Is(err, fs.ErrNotExist) {
return nil, s3err.GetAPIError(s3err.ErrNoSuchBucket)
@@ -1294,12 +1304,29 @@ func (p *Posix) CopyObject(ctx context.Context, input *s3.CopyObjectInput) (*s3.
return nil, fmt.Errorf("stat object: %w", err)
}
etag, err := p.PutObject(ctx, &s3.PutObjectInput{Bucket: &dstBucket, Key: &dstObject, Body: f, ContentLength: fInfo.Size()})
meta := make(map[string]string)
loadUserMetaData(objPath, meta)
dstObjdPath := filepath.Join(dstBucket, dstObject)
if dstObjdPath == objPath {
if compareUserMetadata(meta, input.Metadata) {
return &s3.CopyObjectOutput{}, s3err.GetAPIError(s3err.ErrInvalidCopyDest)
} else {
for key := range meta {
xattr.Remove(dstObjdPath, key)
}
for k, v := range input.Metadata {
xattr.Set(dstObjdPath, fmt.Sprintf("user.%v.%v", metaHdr, k), []byte(v))
}
}
}
etag, err := p.PutObject(ctx, &s3.PutObjectInput{Bucket: &dstBucket, Key: &dstObject, Body: f, ContentLength: fInfo.Size(), Metadata: meta})
if err != nil {
return nil, err
}
fi, err := os.Stat(filepath.Join(dstBucket, dstObject))
fi, err := os.Stat(dstObjdPath)
if err != nil {
return nil, fmt.Errorf("stat dst object: %w", err)
}

View File

@@ -95,6 +95,7 @@ func TestCopyObject(s *S3Conf) {
CopyObject_non_existing_dst_bucket(s)
CopyObject_not_owned_source_bucket(s)
CopyObject_copy_to_itself(s)
CopyObject_to_itself_with_new_metadata(s)
CopyObject_success(s)
}

View File

@@ -1904,6 +1904,32 @@ func CopyObject_copy_to_itself(s *S3Conf) {
})
}
func CopyObject_to_itself_with_new_metadata(s *S3Conf) {
testName := "CopyObject_to_itself_with_new_metadata"
actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
obj := "my-obj"
err := putObjects(s3client, []string{obj}, bucket)
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
_, err = s3client.CopyObject(ctx, &s3.CopyObjectInput{
Bucket: &bucket,
Key: &obj,
CopySource: getPtr(fmt.Sprintf("%v/%v", bucket, obj)),
Metadata: map[string]string{
"Hello": "World",
},
})
cancel()
if err != nil {
return err
}
return nil
})
}
func CopyObject_success(s *S3Conf) {
testName := "CopyObject_success"
actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {

View File

@@ -593,6 +593,9 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
return SendXMLResponse(ctx, nil, s3err.GetAPIError(s3err.ErrInvalidCopySource), &MetaOpts{Logger: c.logger, Action: "CopyObject", BucketOwner: parsedAcl.Owner})
}
}
metadata := utils.GetUserMetaData(&ctx.Request().Header)
res, err := c.be.CopyObject(ctx.Context(), &s3.CopyObjectInput{
Bucket: &bucket,
Key: &keyStart,
@@ -602,6 +605,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
CopySourceIfModifiedSince: &mtime,
CopySourceIfUnmodifiedSince: &umtime,
ExpectedBucketOwner: &access,
Metadata: metadata,
})
if err == nil {
return SendXMLResponse(ctx, res, err, &MetaOpts{