| 
									
										
										
										
											2016-04-30 08:52:17 +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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package main | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2016-05-03 07:57:31 +08:00
										 |  |  | 	"crypto/md5" | 
					
						
							|  |  |  | 	"encoding/hex" | 
					
						
							|  |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2016-04-30 08:52:17 +08:00
										 |  |  | 	"path" | 
					
						
							|  |  |  | 	"sort" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// Common object layer functions.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // makeBucket - create a bucket, is a common function for both object layers.
 | 
					
						
							|  |  |  | func makeBucket(storage StorageAPI, bucket string) error { | 
					
						
							|  |  |  | 	// Verify if bucket is valid.
 | 
					
						
							|  |  |  | 	if !IsValidBucketName(bucket) { | 
					
						
							|  |  |  | 		return BucketNameInvalid{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := storage.MakeVol(bucket); err != nil { | 
					
						
							|  |  |  | 		return toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// This happens for the first time, but keep this here since this
 | 
					
						
							|  |  |  | 	// is the only place where it can be made expensive optimizing all
 | 
					
						
							|  |  |  | 	// other calls.
 | 
					
						
							|  |  |  | 	// Create minio meta volume, if it doesn't exist yet.
 | 
					
						
							| 
									
										
										
										
											2016-05-02 09:13:10 +08:00
										 |  |  | 	if err := storage.MakeVol(minioMetaBucket); err != nil { | 
					
						
							| 
									
										
										
										
											2016-04-30 08:52:17 +08:00
										 |  |  | 		if err != errVolumeExists { | 
					
						
							| 
									
										
										
										
											2016-05-02 09:13:10 +08:00
										 |  |  | 			return toObjectErr(err, minioMetaBucket) | 
					
						
							| 
									
										
										
										
											2016-04-30 08:52:17 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // getBucketInfo - fetch bucket info, is a common function for both object layers.
 | 
					
						
							|  |  |  | func getBucketInfo(storage StorageAPI, bucket string) (BucketInfo, error) { | 
					
						
							|  |  |  | 	// Verify if bucket is valid.
 | 
					
						
							|  |  |  | 	if !IsValidBucketName(bucket) { | 
					
						
							|  |  |  | 		return BucketInfo{}, BucketNameInvalid{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vi, err := storage.StatVol(bucket) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return BucketInfo{}, toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return BucketInfo{ | 
					
						
							|  |  |  | 		Name:    bucket, | 
					
						
							|  |  |  | 		Created: vi.Created, | 
					
						
							|  |  |  | 		Total:   vi.Total, | 
					
						
							|  |  |  | 		Free:    vi.Free, | 
					
						
							|  |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // listBuckets - list all buckets, is a common function for both object layers.
 | 
					
						
							|  |  |  | func listBuckets(storage StorageAPI) ([]BucketInfo, error) { | 
					
						
							|  |  |  | 	var bucketInfos []BucketInfo | 
					
						
							|  |  |  | 	vols, err := storage.ListVols() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, toObjectErr(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, vol := range vols { | 
					
						
							|  |  |  | 		// StorageAPI can send volume names which are incompatible
 | 
					
						
							|  |  |  | 		// with buckets, handle it and skip them.
 | 
					
						
							|  |  |  | 		if !IsValidBucketName(vol.Name) { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		bucketInfos = append(bucketInfos, BucketInfo{ | 
					
						
							|  |  |  | 			Name:    vol.Name, | 
					
						
							|  |  |  | 			Created: vol.Created, | 
					
						
							|  |  |  | 			Total:   vol.Total, | 
					
						
							|  |  |  | 			Free:    vol.Free, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sort.Sort(byBucketName(bucketInfos)) | 
					
						
							|  |  |  | 	return bucketInfos, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // deleteBucket - deletes a bucket, is a common function for both the layers.
 | 
					
						
							|  |  |  | func deleteBucket(storage StorageAPI, bucket string) error { | 
					
						
							|  |  |  | 	// Verify if bucket is valid.
 | 
					
						
							|  |  |  | 	if !IsValidBucketName(bucket) { | 
					
						
							|  |  |  | 		return BucketNameInvalid{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := storage.DeleteVol(bucket); err != nil { | 
					
						
							| 
									
										
										
										
											2016-05-05 16:35:21 +08:00
										 |  |  | 		return toObjectErr(err, bucket) | 
					
						
							| 
									
										
										
										
											2016-04-30 08:52:17 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-03 07:57:31 +08:00
										 |  |  | // putObjectCommon - create an object, is a common function for both object layers.
 | 
					
						
							|  |  |  | func putObjectCommon(storage StorageAPI, bucket string, object string, size int64, data io.Reader, metadata map[string]string) (string, error) { | 
					
						
							|  |  |  | 	// Verify if bucket is valid.
 | 
					
						
							|  |  |  | 	if !IsValidBucketName(bucket) { | 
					
						
							| 
									
										
										
										
											2016-05-04 07:10:24 +08:00
										 |  |  | 		return "", BucketNameInvalid{Bucket: bucket} | 
					
						
							| 
									
										
										
										
											2016-05-03 07:57:31 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if !IsValidObjectName(object) { | 
					
						
							| 
									
										
										
										
											2016-05-04 07:10:24 +08:00
										 |  |  | 		return "", ObjectNameInvalid{ | 
					
						
							| 
									
										
										
										
											2016-05-03 07:57:31 +08:00
										 |  |  | 			Bucket: bucket, | 
					
						
							|  |  |  | 			Object: object, | 
					
						
							| 
									
										
										
										
											2016-05-04 07:10:24 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-03 07:57:31 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Check whether the bucket exists.
 | 
					
						
							|  |  |  | 	if isExist, err := isBucketExist(storage, bucket); err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} else if !isExist { | 
					
						
							|  |  |  | 		return "", BucketNotFound{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-04 07:10:24 +08:00
										 |  |  | 	tempObj := path.Join(tmpMetaPrefix, bucket, object) | 
					
						
							|  |  |  | 	fileWriter, err := storage.CreateFile(minioMetaBucket, tempObj) | 
					
						
							| 
									
										
										
										
											2016-05-03 07:57:31 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", toObjectErr(err, bucket, object) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize md5 writer.
 | 
					
						
							|  |  |  | 	md5Writer := md5.New() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Instantiate a new multi writer.
 | 
					
						
							|  |  |  | 	multiWriter := io.MultiWriter(md5Writer, fileWriter) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Instantiate checksum hashers and create a multiwriter.
 | 
					
						
							|  |  |  | 	if size > 0 { | 
					
						
							|  |  |  | 		if _, err = io.CopyN(multiWriter, data, size); err != nil { | 
					
						
							|  |  |  | 			if clErr := safeCloseAndRemove(fileWriter); clErr != nil { | 
					
						
							|  |  |  | 				return "", clErr | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-05-05 03:18:40 +08:00
										 |  |  | 			return "", toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2016-05-03 07:57:31 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if _, err = io.Copy(multiWriter, data); err != nil { | 
					
						
							|  |  |  | 			if clErr := safeCloseAndRemove(fileWriter); clErr != nil { | 
					
						
							|  |  |  | 				return "", clErr | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-05-05 03:18:40 +08:00
										 |  |  | 			return "", toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2016-05-03 07:57:31 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	newMD5Hex := hex.EncodeToString(md5Writer.Sum(nil)) | 
					
						
							|  |  |  | 	// md5Hex representation.
 | 
					
						
							|  |  |  | 	var md5Hex string | 
					
						
							|  |  |  | 	if len(metadata) != 0 { | 
					
						
							|  |  |  | 		md5Hex = metadata["md5Sum"] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if md5Hex != "" { | 
					
						
							|  |  |  | 		if newMD5Hex != md5Hex { | 
					
						
							|  |  |  | 			if err = safeCloseAndRemove(fileWriter); err != nil { | 
					
						
							|  |  |  | 				return "", err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return "", BadDigest{md5Hex, newMD5Hex} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err = fileWriter.Close() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-04 07:10:24 +08:00
										 |  |  | 	err = storage.RenameFile(minioMetaBucket, tempObj, bucket, object) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-05-05 03:18:40 +08:00
										 |  |  | 		if derr := storage.DeleteFile(minioMetaBucket, tempObj); derr != nil { | 
					
						
							|  |  |  | 			return "", derr | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return "", toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2016-05-04 07:10:24 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-03 07:57:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Return md5sum, successfully wrote object.
 | 
					
						
							|  |  |  | 	return newMD5Hex, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-30 08:52:17 +08:00
										 |  |  | // checks whether bucket exists.
 | 
					
						
							|  |  |  | func isBucketExist(storage StorageAPI, bucketName string) (bool, error) { | 
					
						
							|  |  |  | 	// Check whether bucket exists.
 | 
					
						
							|  |  |  | 	if _, e := storage.StatVol(bucketName); e != nil { | 
					
						
							|  |  |  | 		if e == errVolumeNotFound { | 
					
						
							|  |  |  | 			return false, nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return false, e | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true, nil | 
					
						
							|  |  |  | } |