| 
									
										
										
										
											2016-06-02 07:43:31 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2016-2020 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2016-06-02 07:43:31 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2020-10-14 09:28:42 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-15 00:38:05 +08:00
										 |  |  | 	"github.com/minio/minio-go/v7/pkg/s3utils" | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/sync/errgroup" | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-24 12:05:04 +08:00
										 |  |  | // list all errors that can be ignore in a bucket operation.
 | 
					
						
							| 
									
										
										
										
											2020-07-25 04:16:11 +08:00
										 |  |  | var bucketOpIgnoredErrs = append(baseIgnoredErrs, errDiskAccessDenied, errUnformattedDisk) | 
					
						
							| 
									
										
										
										
											2016-11-24 12:05:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-21 08:57:12 +08:00
										 |  |  | // list all errors that can be ignored in a bucket metadata operation.
 | 
					
						
							|  |  |  | var bucketMetadataOpIgnoredErrs = append(bucketOpIgnoredErrs, errVolumeNotFound) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | /// Bucket operations
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MakeBucket - make a bucket.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | func (er erasureObjects) MakeBucketWithLocation(ctx context.Context, bucket string, opts BucketOptions) error { | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	// Verify if bucket is valid.
 | 
					
						
							| 
									
										
										
										
											2019-03-06 02:42:32 +08:00
										 |  |  | 	if err := s3utils.CheckValidBucketNameStrict(bucket); err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		return BucketNameInvalid{Bucket: bucket} | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	storageDisks := er.getDisks() | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 	g := errgroup.WithNErrs(len(storageDisks)) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Make a volume entry on all underlying storage disks.
 | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 	for index := range storageDisks { | 
					
						
							|  |  |  | 		index := index | 
					
						
							|  |  |  | 		g.Go(func() error { | 
					
						
							|  |  |  | 			if storageDisks[index] != nil { | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 				if err := storageDisks[index].MakeVol(ctx, bucket); err != nil { | 
					
						
							| 
									
										
										
										
											2020-10-14 09:28:42 +08:00
										 |  |  | 					if !errors.Is(err, errVolumeExists) { | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 						logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return err | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 				return nil | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 			return errDiskNotFound | 
					
						
							|  |  |  | 		}, index) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 00:32:16 +08:00
										 |  |  | 	writeQuorum := getWriteQuorum(len(storageDisks)) | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 	err := reduceWriteQuorumErrs(ctx, g.Wait(), bucketOpIgnoredErrs, writeQuorum) | 
					
						
							| 
									
										
										
										
											2017-02-02 03:16:17 +08:00
										 |  |  | 	return toObjectErr(err, bucket) | 
					
						
							| 
									
										
										
										
											2016-06-18 02:57:51 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | func undoDeleteBucket(storageDisks []StorageAPI, bucket string) { | 
					
						
							|  |  |  | 	g := errgroup.WithNErrs(len(storageDisks)) | 
					
						
							| 
									
										
										
										
											2016-07-21 15:27:08 +08:00
										 |  |  | 	// Undo previous make bucket entry on all underlying storage disks.
 | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 	for index := range storageDisks { | 
					
						
							|  |  |  | 		if storageDisks[index] == nil { | 
					
						
							| 
									
										
										
										
											2016-07-21 15:27:08 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 		index := index | 
					
						
							|  |  |  | 		g.Go(func() error { | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 			_ = storageDisks[index].MakeVol(context.Background(), bucket) | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 			return nil | 
					
						
							|  |  |  | 		}, index) | 
					
						
							| 
									
										
										
										
											2016-07-21 15:27:08 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Wait for all make vol to finish.
 | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 	g.Wait() | 
					
						
							| 
									
										
										
										
											2016-07-21 15:27:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-02 07:43:31 +08:00
										 |  |  | // getBucketInfo - returns the BucketInfo from one of the load balanced disks.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | func (er erasureObjects) getBucketInfo(ctx context.Context, bucketName string) (bucketInfo BucketInfo, err error) { | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 	storageDisks := er.getDisks() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	g := errgroup.WithNErrs(len(storageDisks)) | 
					
						
							|  |  |  | 	var bucketsInfo = make([]BucketInfo, len(storageDisks)) | 
					
						
							|  |  |  | 	// Undo previous make bucket entry on all underlying storage disks.
 | 
					
						
							|  |  |  | 	for index := range storageDisks { | 
					
						
							|  |  |  | 		index := index | 
					
						
							|  |  |  | 		g.Go(func() error { | 
					
						
							|  |  |  | 			if storageDisks[index] == nil { | 
					
						
							|  |  |  | 				return errDiskNotFound | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			volInfo, err := storageDisks[index].StatVol(ctx, bucketName) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			bucketsInfo[index] = BucketInfo(volInfo) | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		}, index) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	errs := g.Wait() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, err := range errs { | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			return bucketsInfo[i], nil | 
					
						
							| 
									
										
										
										
											2016-06-02 07:43:31 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-13 06:38:35 +08:00
										 |  |  | 	// If all our errors were ignored, then we try to
 | 
					
						
							|  |  |  | 	// reduce to one error based on read quorum.
 | 
					
						
							|  |  |  | 	// `nil` is deliberately passed for ignoredErrs
 | 
					
						
							|  |  |  | 	// because these errors were already ignored.
 | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 	readQuorum := getReadQuorum(len(storageDisks)) | 
					
						
							|  |  |  | 	return BucketInfo{}, reduceReadQuorumErrs(ctx, errs, nil, readQuorum) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | // GetBucketInfo - returns BucketInfo for a bucket.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | func (er erasureObjects) GetBucketInfo(ctx context.Context, bucket string) (bi BucketInfo, e error) { | 
					
						
							|  |  |  | 	bucketInfo, err := er.getBucketInfo(ctx, bucket) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 		return bi, toObjectErr(err, bucket) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | 	return bucketInfo, nil | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-06 09:58:48 +08:00
										 |  |  | // Dangling buckets should be handled appropriately, in this following situation
 | 
					
						
							|  |  |  | // we actually have quorum error to be `nil` but we have some disks where
 | 
					
						
							|  |  |  | // the bucket delete returned `errVolumeNotEmpty` but this is not correct
 | 
					
						
							|  |  |  | // can only happen if there are dangling objects in a bucket. Under such
 | 
					
						
							|  |  |  | // a situation we simply attempt a full delete of the bucket including
 | 
					
						
							|  |  |  | // the dangling objects. All of this happens under a lock and there
 | 
					
						
							|  |  |  | // is no way a user can create buckets and sneak in objects into namespace,
 | 
					
						
							|  |  |  | // so it is safer to do.
 | 
					
						
							|  |  |  | func deleteDanglingBucket(ctx context.Context, storageDisks []StorageAPI, dErrs []error, bucket string) { | 
					
						
							|  |  |  | 	for index, err := range dErrs { | 
					
						
							|  |  |  | 		if err == errVolumeNotEmpty { | 
					
						
							|  |  |  | 			// Attempt to delete bucket again.
 | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 			if derr := storageDisks[index].DeleteVol(ctx, bucket, false); derr == errVolumeNotEmpty { | 
					
						
							| 
									
										
										
										
											2021-03-25 00:08:05 +08:00
										 |  |  | 				_ = storageDisks[index].Delete(ctx, bucket, "", true) | 
					
						
							| 
									
										
										
										
											2019-02-06 09:58:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 				_ = storageDisks[index].DeleteVol(ctx, bucket, false) | 
					
						
							| 
									
										
										
										
											2019-02-06 09:58:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				// Cleanup all the previously incomplete multiparts.
 | 
					
						
							| 
									
										
										
										
											2021-03-25 00:08:05 +08:00
										 |  |  | 				_ = storageDisks[index].Delete(ctx, minioMetaMultipartBucket, bucket, true) | 
					
						
							| 
									
										
										
										
											2019-02-06 09:58:48 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | // DeleteBucket - deletes a bucket.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | func (er erasureObjects) DeleteBucket(ctx context.Context, bucket string, forceDelete bool) error { | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	// Collect if all disks report volume not found.
 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	defer ObjectPathUpdated(bucket + slashSeparator) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	storageDisks := er.getDisks() | 
					
						
							| 
									
										
										
										
											2019-02-06 09:58:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 	g := errgroup.WithNErrs(len(storageDisks)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for index := range storageDisks { | 
					
						
							|  |  |  | 		index := index | 
					
						
							|  |  |  | 		g.Go(func() error { | 
					
						
							|  |  |  | 			if storageDisks[index] != nil { | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 				if err := storageDisks[index].DeleteVol(ctx, bucket, forceDelete); err != nil { | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 					return err | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-03-25 00:08:05 +08:00
										 |  |  | 				if err := storageDisks[index].Delete(ctx, minioMetaMultipartBucket, bucket, true); err != errFileNotFound { | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 					return err | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return nil | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 			return errDiskNotFound | 
					
						
							|  |  |  | 		}, index) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Wait for all the delete vols to finish.
 | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 	dErrs := g.Wait() | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-28 12:52:59 +08:00
										 |  |  | 	if forceDelete { | 
					
						
							|  |  |  | 		for _, err := range dErrs { | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				undoDeleteBucket(storageDisks, bucket) | 
					
						
							|  |  |  | 				return toObjectErr(err, bucket) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 00:32:16 +08:00
										 |  |  | 	writeQuorum := getWriteQuorum(len(storageDisks)) | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	err := reduceWriteQuorumErrs(ctx, dErrs, bucketOpIgnoredErrs, writeQuorum) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if err == errErasureWriteQuorum { | 
					
						
							| 
									
										
										
										
											2019-10-15 00:44:51 +08:00
										 |  |  | 		undoDeleteBucket(storageDisks, bucket) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-10 07:19:30 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-06 09:58:48 +08:00
										 |  |  | 	// If we reduce quorum to nil, means we have deleted buckets properly
 | 
					
						
							|  |  |  | 	// on some servers in quorum, we should look for volumeNotEmpty errors
 | 
					
						
							|  |  |  | 	// and delete those buckets as well.
 | 
					
						
							| 
									
										
										
										
											2020-09-11 21:39:19 +08:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// let this call succeed, even if client cancels the context
 | 
					
						
							|  |  |  | 	// this is to ensure that we don't leave any stale content
 | 
					
						
							|  |  |  | 	deleteDanglingBucket(context.Background(), storageDisks, dErrs, bucket) | 
					
						
							| 
									
										
										
										
											2019-02-06 09:58:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-10 07:19:30 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsNotificationSupported returns whether bucket notification is applicable for this layer.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | func (er erasureObjects) IsNotificationSupported() bool { | 
					
						
							| 
									
										
										
										
											2018-02-10 07:19:30 +08:00
										 |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | // IsListenSupported returns whether listen bucket notification is applicable for this layer.
 | 
					
						
							|  |  |  | func (er erasureObjects) IsListenSupported() bool { | 
					
						
							| 
									
										
										
										
											2018-12-06 06:03:42 +08:00
										 |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  | // IsEncryptionSupported returns whether server side encryption is implemented for this layer.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | func (er erasureObjects) IsEncryptionSupported() bool { | 
					
						
							| 
									
										
										
										
											2018-02-10 07:19:30 +08:00
										 |  |  | 	return true | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // IsCompressionSupported returns whether compression is applicable for this layer.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | func (er erasureObjects) IsCompressionSupported() bool { | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-05-24 02:09:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // IsTaggingSupported indicates whether erasureObjects implements tagging support.
 | 
					
						
							|  |  |  | func (er erasureObjects) IsTaggingSupported() bool { | 
					
						
							| 
									
										
										
										
											2020-05-24 02:09:35 +08:00
										 |  |  | 	return true | 
					
						
							|  |  |  | } |