azure/s3 gateways: Pass ETag during GET call to avoid data corruption (#11024)

Both Azure & S3 gateways call for object information before returning
the stream of the object, however, the object content/length could be
modified meanwhile, which means it can return a corrupted object.

Use ETag to ensure that the object was not modified during the GET call
This commit is contained in:
Anis Elleuch 2020-12-17 18:11:14 +01:00 committed by GitHub
parent 970ddb424b
commit cffdb01279
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 19 additions and 3 deletions

View File

@ -812,7 +812,7 @@ func (a *azureObjects) GetObjectNInfo(ctx context.Context, bucket, object string
pr, pw := io.Pipe()
go func() {
err := a.GetObject(ctx, bucket, object, startOffset, length, pw, objInfo.ETag, opts)
err := a.GetObject(ctx, bucket, object, startOffset, length, pw, objInfo.InnerETag, opts)
pw.CloseWithError(err)
}()
// Setup cleanup function to cause the above go-routine to
@ -833,8 +833,13 @@ func (a *azureObjects) GetObject(ctx context.Context, bucket, object string, sta
return azureToObjectError(minio.InvalidRange{}, bucket, object)
}
accessCond := azblob.BlobAccessConditions{}
if etag != "" {
accessCond.ModifiedAccessConditions.IfMatch = azblob.ETag(etag)
}
blobURL := a.client.NewContainerURL(bucket).NewBlobURL(object)
blob, err := blobURL.Download(ctx, startOffset, length, azblob.BlobAccessConditions{}, false)
blob, err := blobURL.Download(ctx, startOffset, length, accessCond, false)
if err != nil {
return azureToObjectError(err, bucket, object)
}
@ -855,6 +860,8 @@ func (a *azureObjects) GetObjectInfo(ctx context.Context, bucket, object string,
return objInfo, azureToObjectError(err, bucket, object)
}
realETag := string(blob.ETag())
// Populate correct ETag's if possible, this code primarily exists
// because AWS S3 indicates that
//
@ -866,7 +873,7 @@ func (a *azureObjects) GetObjectInfo(ctx context.Context, bucket, object string,
//
// Some applications depend on this behavior refer https://github.com/minio/minio/issues/6550
// So we handle it here and make this consistent.
etag := minio.ToS3ETag(string(blob.ETag()))
etag := minio.ToS3ETag(realETag)
metadata := blob.NewMetadata()
contentMD5 := blob.ContentMD5()
switch {
@ -881,6 +888,7 @@ func (a *azureObjects) GetObjectInfo(ctx context.Context, bucket, object string,
Bucket: bucket,
UserDefined: azurePropertiesToS3Meta(metadata, blob.NewHTTPHeaders(), blob.ContentLength()),
ETag: etag,
InnerETag: realETag,
ModTime: blob.LastModified(),
Name: object,
Size: blob.ContentLength(),

View File

@ -431,6 +431,11 @@ func (l *s3Objects) GetObject(ctx context.Context, bucket string, key string, st
return minio.ErrorRespToObjectError(err, bucket, key)
}
}
if etag != "" {
opts.SetMatchETag(etag)
}
object, _, _, err := l.Client.GetObject(ctx, bucket, key, opts)
if err != nil {
return minio.ErrorRespToObjectError(err, bucket, key)

View File

@ -168,6 +168,9 @@ type ObjectInfo struct {
// Hex encoded unique entity tag of the object.
ETag string
// The ETag stored in the gateway backend
InnerETag string
// Version ID of this object.
VersionID string