From cffdb01279926c08f9d6468ac0d52d480628fb42 Mon Sep 17 00:00:00 2001 From: Anis Elleuch Date: Thu, 17 Dec 2020 18:11:14 +0100 Subject: [PATCH] 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 --- cmd/gateway/azure/gateway-azure.go | 14 +++++++++++--- cmd/gateway/s3/gateway-s3.go | 5 +++++ cmd/object-api-datatypes.go | 3 +++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/cmd/gateway/azure/gateway-azure.go b/cmd/gateway/azure/gateway-azure.go index 035d2b53d..217db7e64 100644 --- a/cmd/gateway/azure/gateway-azure.go +++ b/cmd/gateway/azure/gateway-azure.go @@ -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(), diff --git a/cmd/gateway/s3/gateway-s3.go b/cmd/gateway/s3/gateway-s3.go index 1c3e2f1c2..328daa264 100644 --- a/cmd/gateway/s3/gateway-s3.go +++ b/cmd/gateway/s3/gateway-s3.go @@ -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) diff --git a/cmd/object-api-datatypes.go b/cmd/object-api-datatypes.go index 6b26c88b2..c9d585118 100644 --- a/cmd/object-api-datatypes.go +++ b/cmd/object-api-datatypes.go @@ -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