| 
									
										
										
										
											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 ( | 
					
						
							|  |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2016-05-07 01:19:09 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2016-04-30 08:52:17 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-06 06:00:03 +08:00
										 |  |  | // Common initialization needed for both object layers.
 | 
					
						
							|  |  |  | func initObjectLayer(storage StorageAPI) error { | 
					
						
							|  |  |  | 	// 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.
 | 
					
						
							|  |  |  | 	if err := storage.MakeVol(minioMetaBucket); err != nil { | 
					
						
							|  |  |  | 		if err != errVolumeExists { | 
					
						
							|  |  |  | 			return toObjectErr(err, minioMetaBucket) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Cleanup all temp entries upon start.
 | 
					
						
							| 
									
										
										
										
											2016-05-09 01:15:34 +08:00
										 |  |  | 	err := cleanupDir(storage, minioMetaBucket, tmpMetaPrefix) | 
					
						
							| 
									
										
										
										
											2016-05-06 06:00:03 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return toObjectErr(err, minioMetaBucket, tmpMetaPrefix) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-09 01:15:34 +08:00
										 |  |  | // Cleanup a directory recursively.
 | 
					
						
							|  |  |  | func cleanupDir(storage StorageAPI, volume, dirPath string) error { | 
					
						
							|  |  |  | 	var delFunc func(string) error | 
					
						
							|  |  |  | 	// Function to delete entries recursively.
 | 
					
						
							|  |  |  | 	delFunc = func(entryPath string) error { | 
					
						
							|  |  |  | 		if !strings.HasSuffix(entryPath, slashSeparator) { | 
					
						
							|  |  |  | 			// No trailing "/" means that this is a file which can be deleted.
 | 
					
						
							|  |  |  | 			return storage.DeleteFile(volume, entryPath) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// If it's a directory, list and call delFunc() for each entry.
 | 
					
						
							|  |  |  | 		entries, err := storage.ListDir(volume, entryPath) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			if err == errFileNotFound { | 
					
						
							|  |  |  | 				// if dirPath prefix never existed.
 | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for _, entry := range entries { | 
					
						
							|  |  |  | 			err = delFunc(pathJoin(entryPath, entry)) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-09 15:46:54 +08:00
										 |  |  | 	return delFunc(retainSlash(pathJoin(dirPath))) | 
					
						
							| 
									
										
										
										
											2016-05-09 01:15:34 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-30 08:52:17 +08:00
										 |  |  | /// 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) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	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-07 01:19:09 +08:00
										 |  |  | func listObjectsCommon(layer ObjectLayer, bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, error) { | 
					
						
							| 
									
										
										
										
											2016-05-08 16:58:05 +08:00
										 |  |  | 	var storage StorageAPI | 
					
						
							| 
									
										
										
										
											2016-05-07 01:19:09 +08:00
										 |  |  | 	switch l := layer.(type) { | 
					
						
							|  |  |  | 	case xlObjects: | 
					
						
							| 
									
										
										
										
											2016-05-08 16:58:05 +08:00
										 |  |  | 		storage = l.storage | 
					
						
							| 
									
										
										
										
											2016-05-07 01:19:09 +08:00
										 |  |  | 	case fsObjects: | 
					
						
							| 
									
										
										
										
											2016-05-08 16:58:05 +08:00
										 |  |  | 		storage = l.storage | 
					
						
							| 
									
										
										
										
											2016-05-07 01:19:09 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Verify if bucket is valid.
 | 
					
						
							|  |  |  | 	if !IsValidBucketName(bucket) { | 
					
						
							|  |  |  | 		return ListObjectsInfo{}, BucketNameInvalid{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	// Verify if bucket exists.
 | 
					
						
							| 
									
										
										
										
											2016-05-08 16:58:05 +08:00
										 |  |  | 	if !isBucketExist(storage, bucket) { | 
					
						
							| 
									
										
										
										
											2016-05-07 01:19:09 +08:00
										 |  |  | 		return ListObjectsInfo{}, BucketNotFound{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !IsValidObjectPrefix(prefix) { | 
					
						
							|  |  |  | 		return ListObjectsInfo{}, ObjectNameInvalid{Bucket: bucket, Object: prefix} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Verify if delimiter is anything other than '/', which we do not support.
 | 
					
						
							|  |  |  | 	if delimiter != "" && delimiter != slashSeparator { | 
					
						
							|  |  |  | 		return ListObjectsInfo{}, UnsupportedDelimiter{ | 
					
						
							|  |  |  | 			Delimiter: delimiter, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Verify if marker has prefix.
 | 
					
						
							|  |  |  | 	if marker != "" { | 
					
						
							|  |  |  | 		if !strings.HasPrefix(marker, prefix) { | 
					
						
							|  |  |  | 			return ListObjectsInfo{}, InvalidMarkerPrefixCombination{ | 
					
						
							|  |  |  | 				Marker: marker, | 
					
						
							|  |  |  | 				Prefix: prefix, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	// With max keys of zero we have reached eof, return right here.
 | 
					
						
							| 
									
										
										
										
											2016-05-07 01:19:09 +08:00
										 |  |  | 	if maxKeys == 0 { | 
					
						
							|  |  |  | 		return ListObjectsInfo{}, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Over flowing count - reset to maxObjectList.
 | 
					
						
							|  |  |  | 	if maxKeys < 0 || maxKeys > maxObjectList { | 
					
						
							|  |  |  | 		maxKeys = maxObjectList | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Default is recursive, if delimiter is set then list non recursive.
 | 
					
						
							|  |  |  | 	recursive := true | 
					
						
							|  |  |  | 	if delimiter == slashSeparator { | 
					
						
							|  |  |  | 		recursive = false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	walker := lookupTreeWalk(layer, listParams{bucket, recursive, marker, prefix}) | 
					
						
							|  |  |  | 	if walker == nil { | 
					
						
							|  |  |  | 		walker = startTreeWalk(layer, bucket, prefix, marker, recursive) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var fileInfos []FileInfo | 
					
						
							|  |  |  | 	var eof bool | 
					
						
							|  |  |  | 	var nextMarker string | 
					
						
							|  |  |  | 	for i := 0; i < maxKeys; { | 
					
						
							|  |  |  | 		walkResult, ok := <-walker.ch | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			// Closed channel.
 | 
					
						
							|  |  |  | 			eof = true | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// For any walk error return right away.
 | 
					
						
							|  |  |  | 		if walkResult.err != nil { | 
					
						
							|  |  |  | 			// File not found is a valid case.
 | 
					
						
							|  |  |  | 			if walkResult.err == errFileNotFound { | 
					
						
							|  |  |  | 				return ListObjectsInfo{}, nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return ListObjectsInfo{}, toObjectErr(walkResult.err, bucket, prefix) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		fileInfo := walkResult.fileInfo | 
					
						
							|  |  |  | 		nextMarker = fileInfo.Name | 
					
						
							|  |  |  | 		fileInfos = append(fileInfos, fileInfo) | 
					
						
							|  |  |  | 		if walkResult.end { | 
					
						
							|  |  |  | 			eof = true | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		i++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	params := listParams{bucket, recursive, nextMarker, prefix} | 
					
						
							|  |  |  | 	if !eof { | 
					
						
							|  |  |  | 		saveTreeWalk(layer, params, walker) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	result := ListObjectsInfo{IsTruncated: !eof} | 
					
						
							|  |  |  | 	for _, fileInfo := range fileInfos { | 
					
						
							|  |  |  | 		// With delimiter set we fill in NextMarker and Prefixes.
 | 
					
						
							|  |  |  | 		if delimiter == slashSeparator { | 
					
						
							|  |  |  | 			result.NextMarker = fileInfo.Name | 
					
						
							|  |  |  | 			if fileInfo.Mode.IsDir() { | 
					
						
							|  |  |  | 				result.Prefixes = append(result.Prefixes, fileInfo.Name) | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		result.Objects = append(result.Objects, ObjectInfo{ | 
					
						
							|  |  |  | 			Name:    fileInfo.Name, | 
					
						
							|  |  |  | 			ModTime: fileInfo.ModTime, | 
					
						
							|  |  |  | 			Size:    fileInfo.Size, | 
					
						
							|  |  |  | 			IsDir:   false, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return result, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-30 08:52:17 +08:00
										 |  |  | // checks whether bucket exists.
 | 
					
						
							| 
									
										
										
										
											2016-05-08 16:58:05 +08:00
										 |  |  | func isBucketExist(storage StorageAPI, bucketName string) bool { | 
					
						
							| 
									
										
										
										
											2016-04-30 08:52:17 +08:00
										 |  |  | 	// Check whether bucket exists.
 | 
					
						
							| 
									
										
										
										
											2016-05-08 16:58:05 +08:00
										 |  |  | 	_, err := storage.StatVol(bucketName) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if err == errVolumeNotFound { | 
					
						
							|  |  |  | 			return false | 
					
						
							| 
									
										
										
										
											2016-04-30 08:52:17 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-17 05:31:28 +08:00
										 |  |  | 		errorIf(err, "Stat failed on bucket "+bucketName+".") | 
					
						
							| 
									
										
										
										
											2016-05-08 16:58:05 +08:00
										 |  |  | 		return false | 
					
						
							| 
									
										
										
										
											2016-04-30 08:52:17 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-08 16:58:05 +08:00
										 |  |  | 	return true | 
					
						
							| 
									
										
										
										
											2016-04-30 08:52:17 +08:00
										 |  |  | } |