| 
									
										
										
										
											2016-06-02 07:43:31 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * Minio Cloud Storage, (C) 2016 Minio, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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 ( | 
					
						
							|  |  |  | 	"sort" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-24 12:05:04 +08:00
										 |  |  | // list all errors that can be ignore in a bucket operation.
 | 
					
						
							|  |  |  | var bucketOpIgnoredErrs = append(baseIgnoredErrs, errDiskAccessDenied) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							|  |  |  | func (xl xlObjects) MakeBucket(bucket string) error { | 
					
						
							|  |  |  | 	// Verify if bucket is valid.
 | 
					
						
							|  |  |  | 	if !IsValidBucketName(bucket) { | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | 		return traceError(BucketNameInvalid{Bucket: bucket}) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize sync waitgroup.
 | 
					
						
							|  |  |  | 	var wg = &sync.WaitGroup{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize list of errors.
 | 
					
						
							|  |  |  | 	var dErrs = make([]error, len(xl.storageDisks)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make a volume entry on all underlying storage disks.
 | 
					
						
							|  |  |  | 	for index, disk := range xl.storageDisks { | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 		if disk == nil { | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | 			dErrs[index] = traceError(errDiskNotFound) | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 		wg.Add(1) | 
					
						
							|  |  |  | 		// Make a volume inside a go-routine.
 | 
					
						
							|  |  |  | 		go func(index int, disk StorageAPI) { | 
					
						
							|  |  |  | 			defer wg.Done() | 
					
						
							|  |  |  | 			err := disk.MakeVol(bucket) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | 				dErrs[index] = traceError(err) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}(index, disk) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Wait for all make vol to finish.
 | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-02 03:16:17 +08:00
										 |  |  | 	err := reduceWriteQuorumErrs(dErrs, bucketOpIgnoredErrs, xl.writeQuorum) | 
					
						
							|  |  |  | 	if errorCause(err) == errXLWriteQuorum { | 
					
						
							| 
									
										
										
										
											2016-06-18 02:57:51 +08:00
										 |  |  | 		// Purge successfully created buckets if we don't have writeQuorum.
 | 
					
						
							| 
									
										
										
										
											2016-11-17 08:42:23 +08:00
										 |  |  | 		undoMakeBucket(xl.storageDisks, bucket) | 
					
						
							| 
									
										
										
										
											2016-06-18 02:57:51 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 15:27:08 +08:00
										 |  |  | func (xl xlObjects) undoDeleteBucket(bucket string) { | 
					
						
							|  |  |  | 	// Initialize sync waitgroup.
 | 
					
						
							|  |  |  | 	var wg = &sync.WaitGroup{} | 
					
						
							|  |  |  | 	// Undo previous make bucket entry on all underlying storage disks.
 | 
					
						
							|  |  |  | 	for index, disk := range xl.storageDisks { | 
					
						
							|  |  |  | 		if disk == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		wg.Add(1) | 
					
						
							|  |  |  | 		// Delete a bucket inside a go-routine.
 | 
					
						
							|  |  |  | 		go func(index int, disk StorageAPI) { | 
					
						
							|  |  |  | 			defer wg.Done() | 
					
						
							|  |  |  | 			_ = disk.MakeVol(bucket) | 
					
						
							|  |  |  | 		}(index, disk) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Wait for all make vol to finish.
 | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-18 02:57:51 +08:00
										 |  |  | // undo make bucket operation upon quorum failure.
 | 
					
						
							| 
									
										
										
										
											2016-11-17 08:42:23 +08:00
										 |  |  | func undoMakeBucket(storageDisks []StorageAPI, bucket string) { | 
					
						
							| 
									
										
										
										
											2016-06-18 02:57:51 +08:00
										 |  |  | 	// Initialize sync waitgroup.
 | 
					
						
							|  |  |  | 	var wg = &sync.WaitGroup{} | 
					
						
							|  |  |  | 	// Undo previous make bucket entry on all underlying storage disks.
 | 
					
						
							| 
									
										
										
										
											2016-11-17 08:42:23 +08:00
										 |  |  | 	for index, disk := range storageDisks { | 
					
						
							| 
									
										
										
										
											2016-06-18 02:57:51 +08:00
										 |  |  | 		if disk == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		wg.Add(1) | 
					
						
							|  |  |  | 		// Delete a bucket inside a go-routine.
 | 
					
						
							|  |  |  | 		go func(index int, disk StorageAPI) { | 
					
						
							|  |  |  | 			defer wg.Done() | 
					
						
							|  |  |  | 			_ = disk.DeleteVol(bucket) | 
					
						
							|  |  |  | 		}(index, disk) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-18 02:57:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Wait for all make vol to finish.
 | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-02 07:43:31 +08:00
										 |  |  | // getBucketInfo - returns the BucketInfo from one of the load balanced disks.
 | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | func (xl xlObjects) getBucketInfo(bucketName string) (bucketInfo BucketInfo, err error) { | 
					
						
							| 
									
										
										
										
											2016-07-21 15:27:08 +08:00
										 |  |  | 	for _, disk := range xl.getLoadBalancedDisks() { | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 		if disk == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | 		var volInfo VolInfo | 
					
						
							|  |  |  | 		volInfo, err = disk.StatVol(bucketName) | 
					
						
							| 
									
										
										
										
											2016-07-08 13:10:27 +08:00
										 |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			bucketInfo = BucketInfo{ | 
					
						
							|  |  |  | 				Name:    volInfo.Name, | 
					
						
							|  |  |  | 				Created: volInfo.Created, | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-07-08 13:10:27 +08:00
										 |  |  | 			return bucketInfo, nil | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | 		err = traceError(err) | 
					
						
							| 
									
										
										
										
											2016-07-08 13:10:27 +08:00
										 |  |  | 		// For any reason disk went offline continue and pick the next one.
 | 
					
						
							| 
									
										
										
										
											2016-11-24 12:05:04 +08:00
										 |  |  | 		if isErrIgnored(err, bucketMetadataOpIgnoredErrs...) { | 
					
						
							| 
									
										
										
										
											2016-07-08 13:10:27 +08:00
										 |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2016-06-02 07:43:31 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		break | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-08 13:10:27 +08:00
										 |  |  | 	return BucketInfo{}, err | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | // GetBucketInfo - returns BucketInfo for a bucket.
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | func (xl xlObjects) GetBucketInfo(bucket string) (BucketInfo, error) { | 
					
						
							|  |  |  | 	// Verify if bucket is valid.
 | 
					
						
							|  |  |  | 	if !IsValidBucketName(bucket) { | 
					
						
							|  |  |  | 		return BucketInfo{}, BucketNameInvalid{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | 	bucketInfo, err := xl.getBucketInfo(bucket) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return BucketInfo{}, toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | 	return bucketInfo, nil | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | // listBuckets - returns list of all buckets from a disk picked at random.
 | 
					
						
							|  |  |  | func (xl xlObjects) listBuckets() (bucketsInfo []BucketInfo, err error) { | 
					
						
							| 
									
										
										
										
											2016-07-21 15:27:08 +08:00
										 |  |  | 	for _, disk := range xl.getLoadBalancedDisks() { | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 		if disk == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | 		var volsInfo []VolInfo | 
					
						
							|  |  |  | 		volsInfo, err = disk.ListVols() | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			// NOTE: The assumption here is that volumes across all disks in
 | 
					
						
							|  |  |  | 			// readQuorum have consistent view i.e they all have same number
 | 
					
						
							|  |  |  | 			// of buckets. This is essentially not verified since healing
 | 
					
						
							|  |  |  | 			// should take care of this.
 | 
					
						
							|  |  |  | 			var bucketsInfo []BucketInfo | 
					
						
							|  |  |  | 			for _, volInfo := range volsInfo { | 
					
						
							| 
									
										
										
										
											2017-02-17 06:52:14 +08:00
										 |  |  | 				if isReservedOrInvalidBucket(volInfo.Name) { | 
					
						
							| 
									
										
										
										
											2016-07-24 13:51:12 +08:00
										 |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | 				bucketsInfo = append(bucketsInfo, BucketInfo{ | 
					
						
							|  |  |  | 					Name:    volInfo.Name, | 
					
						
							|  |  |  | 					Created: volInfo.Created, | 
					
						
							|  |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-07-25 09:08:15 +08:00
										 |  |  | 			// For buckets info empty, loop once again to check
 | 
					
						
							| 
									
										
										
										
											2016-11-17 08:42:23 +08:00
										 |  |  | 			// if we have, can happen if disks were down.
 | 
					
						
							| 
									
										
										
										
											2016-07-25 09:08:15 +08:00
										 |  |  | 			if len(bucketsInfo) == 0 { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | 			return bucketsInfo, nil | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-07-08 13:10:27 +08:00
										 |  |  | 		// Ignore any disks not found.
 | 
					
						
							| 
									
										
										
										
											2016-11-24 12:05:04 +08:00
										 |  |  | 		if isErrIgnored(err, bucketMetadataOpIgnoredErrs...) { | 
					
						
							| 
									
										
										
										
											2016-07-08 13:10:27 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-02 07:43:31 +08:00
										 |  |  | 		break | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | 	return nil, err | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | // ListBuckets - lists all the buckets, sorted by its name.
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | func (xl xlObjects) ListBuckets() ([]BucketInfo, error) { | 
					
						
							|  |  |  | 	bucketInfos, err := xl.listBuckets() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, toObjectErr(err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-25 08:48:58 +08:00
										 |  |  | 	// Sort by bucket name before returning.
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	sort.Sort(byBucketName(bucketInfos)) | 
					
						
							|  |  |  | 	return bucketInfos, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | // DeleteBucket - deletes a bucket.
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | func (xl xlObjects) DeleteBucket(bucket string) error { | 
					
						
							|  |  |  | 	// Verify if bucket is valid.
 | 
					
						
							|  |  |  | 	if !IsValidBucketName(bucket) { | 
					
						
							|  |  |  | 		return BucketNameInvalid{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Collect if all disks report volume not found.
 | 
					
						
							|  |  |  | 	var wg = &sync.WaitGroup{} | 
					
						
							|  |  |  | 	var dErrs = make([]error, len(xl.storageDisks)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Remove a volume entry on all underlying storage disks.
 | 
					
						
							|  |  |  | 	for index, disk := range xl.storageDisks { | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 		if disk == nil { | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | 			dErrs[index] = traceError(errDiskNotFound) | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 		wg.Add(1) | 
					
						
							|  |  |  | 		// Delete volume inside a go-routine.
 | 
					
						
							|  |  |  | 		go func(index int, disk StorageAPI) { | 
					
						
							|  |  |  | 			defer wg.Done() | 
					
						
							| 
									
										
										
										
											2016-07-12 16:01:47 +08:00
										 |  |  | 			// Attempt to delete bucket.
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 			err := disk.DeleteVol(bucket) | 
					
						
							| 
									
										
										
										
											2016-07-12 16:01:47 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | 				dErrs[index] = traceError(err) | 
					
						
							| 
									
										
										
										
											2016-07-12 16:01:47 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Cleanup all the previously incomplete multiparts.
 | 
					
						
							| 
									
										
										
										
											2016-11-23 05:15:06 +08:00
										 |  |  | 			err = cleanupDir(disk, minioMetaMultipartBucket, bucket) | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				if errorCause(err) == errVolumeNotFound { | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 				dErrs[index] = err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}(index, disk) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Wait for all the delete vols to finish.
 | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-02 03:16:17 +08:00
										 |  |  | 	err := reduceWriteQuorumErrs(dErrs, bucketOpIgnoredErrs, xl.writeQuorum) | 
					
						
							|  |  |  | 	if errorCause(err) == errXLWriteQuorum { | 
					
						
							| 
									
										
										
										
											2016-07-21 15:27:08 +08:00
										 |  |  | 		xl.undoDeleteBucket(bucket) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-02-02 03:16:17 +08:00
										 |  |  | 	return toObjectErr(err, bucket) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } |