mirror of
https://github.com/versity/versitygw.git
synced 2026-01-03 10:35:15 +00:00
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:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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{
|
||||
|
||||
Reference in New Issue
Block a user