| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  |  * Minio Cloud Storage, (C) 2016, 2017 Minio, Inc. | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +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-04-30 05:24:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-10-25 03:25:42 +08:00
										 |  |  | 	"encoding/hex" | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2017-05-10 08:46:46 +08:00
										 |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	"syscall" | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/errors" | 
					
						
							| 
									
										
										
										
											2017-10-22 13:30:34 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/hash" | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/lock" | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // fsObjects - Implements fs object layer.
 | 
					
						
							|  |  |  | type fsObjects struct { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Path to be exported over S3 API.
 | 
					
						
							|  |  |  | 	fsPath string | 
					
						
							| 
									
										
										
										
											2016-06-25 07:41:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Unique value to be used for all
 | 
					
						
							|  |  |  | 	// temporary transactions.
 | 
					
						
							|  |  |  | 	fsUUID string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-13 23:29:07 +08:00
										 |  |  | 	// This value shouldn't be touched, once initialized.
 | 
					
						
							|  |  |  | 	fsFormatRlk *lock.RLockedFile // Is a read lock on `format.json`.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// FS rw pool.
 | 
					
						
							|  |  |  | 	rwPool *fsIOPool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// ListObjects pool management.
 | 
					
						
							| 
									
										
										
										
											2016-06-25 07:41:57 +08:00
										 |  |  | 	listPool *treeWalkPool | 
					
						
							| 
									
										
										
										
											2016-11-21 15:42:53 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// To manage the appendRoutine go0routines
 | 
					
						
							|  |  |  | 	bgAppend *backgroundAppend | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | // Initializes meta volume on all the fs path.
 | 
					
						
							|  |  |  | func initMetaVolumeFS(fsPath, fsUUID string) error { | 
					
						
							|  |  |  | 	// This happens for the first time, but keep this here since this
 | 
					
						
							|  |  |  | 	// is the only place where it can be made less expensive
 | 
					
						
							|  |  |  | 	// optimizing all other calls. Create minio meta volume,
 | 
					
						
							|  |  |  | 	// if it doesn't exist yet.
 | 
					
						
							|  |  |  | 	metaBucketPath := pathJoin(fsPath, minioMetaBucket) | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	if err := os.MkdirAll(metaBucketPath, 0777); err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	metaTmpPath := pathJoin(fsPath, minioMetaTmpBucket, fsUUID) | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	if err := os.MkdirAll(metaTmpPath, 0777); err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	metaMultipartPath := pathJoin(fsPath, minioMetaMultipartBucket) | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	return os.MkdirAll(metaMultipartPath, 0777) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-16 04:43:40 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | // newFSObjectLayer - initialize new fs object layer.
 | 
					
						
							|  |  |  | func newFSObjectLayer(fsPath string) (ObjectLayer, error) { | 
					
						
							|  |  |  | 	if fsPath == "" { | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 		return nil, errInvalidArgument | 
					
						
							| 
									
										
										
										
											2016-07-02 16:59:28 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-13 08:40:28 +08:00
										 |  |  | 	var err error | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Disallow relative paths, figure out absolute paths.
 | 
					
						
							| 
									
										
										
										
											2017-06-13 08:40:28 +08:00
										 |  |  | 	fsPath, err = filepath.Abs(fsPath) | 
					
						
							| 
									
										
										
										
											2016-06-14 16:39:40 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 	fi, err := os.Stat((fsPath)) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		if !fi.IsDir() { | 
					
						
							|  |  |  | 			return nil, syscall.ENOTDIR | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if os.IsNotExist(err) { | 
					
						
							|  |  |  | 		// Disk not found create it.
 | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 		err = os.MkdirAll(fsPath, 0777) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-14 16:39:40 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-24 17:06:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	di, err := getDiskInfo((fsPath)) | 
					
						
							| 
									
										
										
										
											2017-07-11 09:14:48 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check if disk has minimum required total space.
 | 
					
						
							|  |  |  | 	if err = checkDiskMinTotal(di); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Assign a new UUID for FS minio mode. Each server instance
 | 
					
						
							|  |  |  | 	// gets its own UUID for temporary file transaction.
 | 
					
						
							|  |  |  | 	fsUUID := mustGetUUID() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-26 10:11:50 +08:00
										 |  |  | 	// Initialize meta volume, if volume already exists ignores it.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if err = initMetaVolumeFS(fsPath, fsUUID); err != nil { | 
					
						
							| 
									
										
										
										
											2016-11-26 10:11:50 +08:00
										 |  |  | 		return nil, fmt.Errorf("Unable to initialize '.minio.sys' meta volume, %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-13 23:29:07 +08:00
										 |  |  | 	// Initialize `format.json`, this function also returns.
 | 
					
						
							|  |  |  | 	rlk, err := initFormatFS(fsPath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 13:01:58 +08:00
										 |  |  | 	// Initialize fs objects.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	fs := &fsObjects{ | 
					
						
							| 
									
										
										
										
											2017-03-08 04:25:40 +08:00
										 |  |  | 		fsPath: fsPath, | 
					
						
							|  |  |  | 		fsUUID: fsUUID, | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		rwPool: &fsIOPool{ | 
					
						
							|  |  |  | 			readersMap: make(map[string]*lock.RLockedFile), | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 		listPool: newTreeWalkPool(globalLookupTimeout), | 
					
						
							| 
									
										
										
										
											2016-11-21 15:42:53 +08:00
										 |  |  | 		bgAppend: &backgroundAppend{ | 
					
						
							|  |  |  | 			infoMap: make(map[string]bgAppendPartsInfo), | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2016-08-05 13:01:58 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-13 23:29:07 +08:00
										 |  |  | 	// Once the filesystem has initialized hold the read lock for
 | 
					
						
							|  |  |  | 	// the life time of the server. This is done to ensure that under
 | 
					
						
							|  |  |  | 	// shared backend mode for FS, remote servers do not migrate
 | 
					
						
							|  |  |  | 	// or cause changes on backend format.
 | 
					
						
							|  |  |  | 	fs.fsFormatRlk = rlk | 
					
						
							| 
									
										
										
										
											2017-05-05 23:49:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Initialize and load bucket policies.
 | 
					
						
							| 
									
										
										
										
											2017-07-11 09:14:48 +08:00
										 |  |  | 	if err = initBucketPolicies(fs); err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		return nil, fmt.Errorf("Unable to load all bucket policies. %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize a new event notifier.
 | 
					
						
							| 
									
										
										
										
											2017-07-11 09:14:48 +08:00
										 |  |  | 	if err = initEventNotifier(fs); err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		return nil, fmt.Errorf("Unable to initialize event notification. %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-01 10:11:42 +08:00
										 |  |  | 	// Start background process to cleanup old multipart objects in `.minio.sys`.
 | 
					
						
							|  |  |  | 	go cleanupStaleMultipartUploads(multipartCleanupInterval, multipartExpiry, fs, fs.listMultipartUploadsCleanup, globalServiceDoneCh) | 
					
						
							| 
									
										
										
										
											2017-08-05 01:45:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 13:01:58 +08:00
										 |  |  | 	// Return successfully initialized object layer.
 | 
					
						
							|  |  |  | 	return fs, nil | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | // Should be called when process shuts down.
 | 
					
						
							|  |  |  | func (fs fsObjects) Shutdown() error { | 
					
						
							| 
									
										
										
										
											2017-08-11 05:11:57 +08:00
										 |  |  | 	fs.fsFormatRlk.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Cleanup and delete tmp uuid.
 | 
					
						
							|  |  |  | 	return fsRemoveAll(pathJoin(fs.fsPath, minioMetaTmpBucket, fs.fsUUID)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-27 05:13:10 +08:00
										 |  |  | // StorageInfo - returns underlying storage statistics.
 | 
					
						
							|  |  |  | func (fs fsObjects) StorageInfo() StorageInfo { | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	info, err := getDiskInfo((fs.fsPath)) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	errorIf(err, "Unable to get disk info %#v", fs.fsPath) | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	storageInfo := StorageInfo{ | 
					
						
							| 
									
										
										
										
											2016-05-27 05:13:10 +08:00
										 |  |  | 		Total: info.Total, | 
					
						
							|  |  |  | 		Free:  info.Free, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	storageInfo.Backend.Type = FS | 
					
						
							|  |  |  | 	return storageInfo | 
					
						
							| 
									
										
										
										
											2016-05-27 05:13:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | /// Bucket operations
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | // getBucketDir - will convert incoming bucket names to
 | 
					
						
							|  |  |  | // corresponding valid bucket names on the backend in a platform
 | 
					
						
							|  |  |  | // compatible way for all operating systems.
 | 
					
						
							|  |  |  | func (fs fsObjects) getBucketDir(bucket string) (string, error) { | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	// Verify if bucket is valid.
 | 
					
						
							|  |  |  | 	if !IsValidBucketName(bucket) { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return "", errors.Trace(BucketNameInvalid{Bucket: bucket}) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	bucketDir := pathJoin(fs.fsPath, bucket) | 
					
						
							|  |  |  | 	return bucketDir, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (fs fsObjects) statBucketDir(bucket string) (os.FileInfo, error) { | 
					
						
							|  |  |  | 	bucketDir, err := fs.getBucketDir(bucket) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	st, err := fsStatVolume(bucketDir) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-27 07:40:10 +08:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return st, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MakeBucket - create a new bucket, returns if it
 | 
					
						
							|  |  |  | // already exists.
 | 
					
						
							| 
									
										
										
										
											2017-06-02 00:43:20 +08:00
										 |  |  | func (fs fsObjects) MakeBucketWithLocation(bucket, location string) error { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	bucketDir, err := fs.getBucketDir(bucket) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err = fsMkdir(bucketDir); err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-27 07:40:10 +08:00
										 |  |  | 		return toObjectErr(err, bucket) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | // GetBucketInfo - fetch bucket metadata info.
 | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | func (fs fsObjects) GetBucketInfo(bucket string) (bi BucketInfo, e error) { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	st, err := fs.statBucketDir(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
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 	// As os.Stat() doesn't carry other than ModTime(), use ModTime() as CreatedTime.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	createdTime := st.ModTime() | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	return BucketInfo{ | 
					
						
							|  |  |  | 		Name:    bucket, | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		Created: createdTime, | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	}, nil | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | // ListBuckets - list all s3 compatible buckets (directories) at fsPath.
 | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | func (fs fsObjects) ListBuckets() ([]BucketInfo, error) { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if err := checkPathLength(fs.fsPath); err != nil { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return nil, errors.Trace(err) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	var bucketInfos []BucketInfo | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	entries, err := readDir((fs.fsPath)) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return nil, toObjectErr(errors.Trace(errDiskNotFound)) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for _, entry := range entries { | 
					
						
							| 
									
										
										
										
											2017-02-17 06:52:14 +08:00
										 |  |  | 		// Ignore all reserved bucket names and invalid bucket names.
 | 
					
						
							|  |  |  | 		if isReservedOrInvalidBucket(entry) { | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		var fi os.FileInfo | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 		fi, err = fsStatVolume(pathJoin(fs.fsPath, entry)) | 
					
						
							| 
									
										
										
										
											2017-08-11 04:36:11 +08:00
										 |  |  | 		// There seems like no practical reason to check for errors
 | 
					
						
							|  |  |  | 		// at this point, if there are indeed errors we can simply
 | 
					
						
							|  |  |  | 		// just ignore such buckets and list only those which
 | 
					
						
							|  |  |  | 		// return proper Stat information instead.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2017-08-11 04:36:11 +08:00
										 |  |  | 			// Ignore any errors returned here.
 | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 		bucketInfos = append(bucketInfos, BucketInfo{ | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 			Name: fi.Name(), | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 			// As os.Stat() doesnt carry CreatedTime, use ModTime() as CreatedTime.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 			Created: fi.ModTime(), | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Sort bucket infos by bucket name.
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	sort.Sort(byBucketName(bucketInfos)) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Succes.
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	return bucketInfos, nil | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | // DeleteBucket - delete a bucket and all the metadata associated
 | 
					
						
							|  |  |  | // with the bucket including pending multipart, object metadata.
 | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | func (fs fsObjects) DeleteBucket(bucket string) error { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	bucketDir, err := fs.getBucketDir(bucket) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return toObjectErr(err, bucket) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-12 16:01:47 +08:00
										 |  |  | 	// Attempt to delete regular bucket.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if err = fsRemoveDir(bucketDir); err != nil { | 
					
						
							|  |  |  | 		return toObjectErr(err, bucket) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-12 16:01:47 +08:00
										 |  |  | 	// Cleanup all the previously incomplete multiparts.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	minioMetaMultipartBucketDir := pathJoin(fs.fsPath, minioMetaMultipartBucket, bucket) | 
					
						
							|  |  |  | 	if err = fsRemoveAll(minioMetaMultipartBucketDir); err != nil { | 
					
						
							| 
									
										
										
										
											2016-07-12 16:01:47 +08:00
										 |  |  | 		return toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Cleanup all the bucket metadata.
 | 
					
						
							|  |  |  | 	minioMetadataBucketDir := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket) | 
					
						
							|  |  |  | 	if err = fsRemoveAll(minioMetadataBucketDir); err != nil { | 
					
						
							|  |  |  | 		return toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// Object Operations
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | // CopyObject - copy object source object to destination object.
 | 
					
						
							|  |  |  | // if source object and destination object are same we only
 | 
					
						
							|  |  |  | // update metadata.
 | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | func (fs fsObjects) CopyObject(srcBucket, srcObject, dstBucket, dstObject string, metadata map[string]string) (oi ObjectInfo, e error) { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if _, err := fs.statBucketDir(srcBucket); err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 		return oi, toObjectErr(err, srcBucket) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | 	// Stat the file to get file size.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	fi, err := fsStatFile(pathJoin(fs.fsPath, srcBucket, srcObject)) | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 		return oi, toObjectErr(err, srcBucket, srcObject) | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check if this request is only metadata update.
 | 
					
						
							| 
									
										
										
										
											2017-02-19 05:41:59 +08:00
										 |  |  | 	cpMetadataOnly := isStringEqual(pathJoin(srcBucket, srcObject), pathJoin(dstBucket, dstObject)) | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | 	if cpMetadataOnly { | 
					
						
							| 
									
										
										
										
											2017-06-13 08:40:28 +08:00
										 |  |  | 		fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, srcBucket, srcObject, fsMetaJSONFile) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		var wlk *lock.LockedFile | 
					
						
							|  |  |  | 		wlk, err = fs.rwPool.Write(fsMetaPath) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 			return oi, toObjectErr(errors.Trace(err), srcBucket, srcObject) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		// This close will allow for locks to be synchronized on `fs.json`.
 | 
					
						
							|  |  |  | 		defer wlk.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | 		// Save objects' metadata in `fs.json`.
 | 
					
						
							|  |  |  | 		fsMeta := newFSMetaV1() | 
					
						
							|  |  |  | 		fsMeta.Meta = metadata | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		if _, err = fsMeta.WriteTo(wlk); err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 			return oi, toObjectErr(err, srcBucket, srcObject) | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		// Return the new object info.
 | 
					
						
							|  |  |  | 		return fsMeta.ToObjectInfo(srcBucket, srcObject, fi), nil | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Length of the file to read.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	length := fi.Size() | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize pipe.
 | 
					
						
							|  |  |  | 	pipeReader, pipeWriter := io.Pipe() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2017-02-25 01:20:40 +08:00
										 |  |  | 		var startOffset int64 // Read the whole file.
 | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | 		if gerr := fs.GetObject(srcBucket, srcObject, startOffset, length, pipeWriter); gerr != nil { | 
					
						
							|  |  |  | 			errorIf(gerr, "Unable to read %s/%s.", srcBucket, srcObject) | 
					
						
							|  |  |  | 			pipeWriter.CloseWithError(gerr) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		pipeWriter.Close() // Close writer explicitly signalling we wrote all data.
 | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-22 13:30:34 +08:00
										 |  |  | 	hashReader, err := hash.NewReader(pipeReader, length, "", "") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return oi, toObjectErr(err, dstBucket, dstObject) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	objInfo, err := fs.PutObject(dstBucket, dstObject, hashReader, metadata) | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 		return oi, toObjectErr(err, dstBucket, dstObject) | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Explicitly close the reader.
 | 
					
						
							|  |  |  | 	pipeReader.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return objInfo, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // GetObject - reads an object from the disk.
 | 
					
						
							|  |  |  | // Supports additional parameters like offset and length
 | 
					
						
							|  |  |  | // which are synonymous with HTTP Range requests.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // startOffset indicates the starting read location of the object.
 | 
					
						
							|  |  |  | // length indicates the total length of the object.
 | 
					
						
							| 
									
										
										
										
											2016-06-25 18:03:27 +08:00
										 |  |  | func (fs fsObjects) GetObject(bucket, object string, offset int64, length int64, writer io.Writer) (err error) { | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	if err = checkBucketAndObjectNamesFS(bucket, object); err != nil { | 
					
						
							| 
									
										
										
										
											2016-12-02 15:15:17 +08:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if _, err = fs.statBucketDir(bucket); err != nil { | 
					
						
							|  |  |  | 		return toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-22 03:29:32 +08:00
										 |  |  | 	// Offset cannot be negative.
 | 
					
						
							|  |  |  | 	if offset < 0 { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return toObjectErr(errors.Trace(errUnexpected), bucket, object) | 
					
						
							| 
									
										
										
										
											2016-07-07 16:30:34 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-08 22:46:49 +08:00
										 |  |  | 	// Writer cannot be nil.
 | 
					
						
							|  |  |  | 	if writer == nil { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return toObjectErr(errors.Trace(errUnexpected), bucket, object) | 
					
						
							| 
									
										
										
										
											2016-07-08 22:46:49 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-08 10:49:45 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	// If its a directory request, we return an empty body.
 | 
					
						
							|  |  |  | 	if hasSuffix(object, slashSeparator) { | 
					
						
							|  |  |  | 		_, err = writer.Write([]byte("")) | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return toObjectErr(errors.Trace(err), bucket, object) | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if bucket != minioMetaBucket { | 
					
						
							| 
									
										
										
										
											2017-06-13 08:40:28 +08:00
										 |  |  | 		fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, object, fsMetaJSONFile) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		_, err = fs.rwPool.Open(fsMetaPath) | 
					
						
							|  |  |  | 		if err != nil && err != errFileNotFound { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 			return toObjectErr(errors.Trace(err), bucket, object) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		defer fs.rwPool.Close(fsMetaPath) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Read the object, doesn't exist returns an s3 compatible error.
 | 
					
						
							|  |  |  | 	fsObjPath := pathJoin(fs.fsPath, bucket, object) | 
					
						
							|  |  |  | 	reader, size, err := fsOpenFile(fsObjPath, offset) | 
					
						
							| 
									
										
										
										
											2016-07-08 10:49:45 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-27 07:40:10 +08:00
										 |  |  | 		return toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2016-07-08 10:49:45 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	defer reader.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bufSize := int64(readSizeV1) | 
					
						
							|  |  |  | 	if length > 0 && bufSize > length { | 
					
						
							|  |  |  | 		bufSize = length | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-08 10:49:45 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-22 03:29:32 +08:00
										 |  |  | 	// For negative length we read everything.
 | 
					
						
							|  |  |  | 	if length < 0 { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		length = size - offset | 
					
						
							| 
									
										
										
										
											2016-07-08 22:46:49 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-22 03:29:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Reply back invalid range if the input offset and length fall out of range.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if offset > size || offset+length > size { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return errors.Trace(InvalidRange{offset, length, size}) | 
					
						
							| 
									
										
										
										
											2016-07-08 10:49:45 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-06 11:59:54 +08:00
										 |  |  | 	// Allocate a staging buffer.
 | 
					
						
							|  |  |  | 	buf := make([]byte, int(bufSize)) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	_, err = io.CopyBuffer(writer, io.LimitReader(reader, length), buf) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 	return toObjectErr(errors.Trace(err), bucket, object) | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | // getObjectInfo - wrapper for reading object metadata and constructs ObjectInfo.
 | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | func (fs fsObjects) getObjectInfo(bucket, object string) (oi ObjectInfo, e error) { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	fsMeta := fsMetaV1{} | 
					
						
							| 
									
										
										
										
											2017-11-17 08:00:27 +08:00
										 |  |  | 	fi, err := fsStatDir(pathJoin(fs.fsPath, bucket, object)) | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 	if err != nil && errors.Cause(err) != errFileAccessDenied { | 
					
						
							| 
									
										
										
										
											2017-11-17 08:00:27 +08:00
										 |  |  | 		return oi, toObjectErr(err, bucket, object) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if fi != nil { | 
					
						
							|  |  |  | 		// If file found and request was with object ending with "/", consider it
 | 
					
						
							|  |  |  | 		// as directory and return object info
 | 
					
						
							|  |  |  | 		if hasSuffix(object, slashSeparator) { | 
					
						
							|  |  |  | 			return fsMeta.ToObjectInfo(bucket, object, fi), nil | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-11-17 08:00:27 +08:00
										 |  |  | 		return oi, toObjectErr(errFileNotFound, bucket, object) | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-13 08:40:28 +08:00
										 |  |  | 	fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, object, fsMetaJSONFile) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Read `fs.json` to perhaps contend with
 | 
					
						
							|  |  |  | 	// parallel Put() operations.
 | 
					
						
							|  |  |  | 	rlk, err := fs.rwPool.Open(fsMetaPath) | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		// Read from fs metadata only if it exists.
 | 
					
						
							|  |  |  | 		defer fs.rwPool.Close(fsMetaPath) | 
					
						
							| 
									
										
										
										
											2017-01-26 04:29:06 +08:00
										 |  |  | 		if _, rerr := fsMeta.ReadFrom(rlk.LockedFile); rerr != nil { | 
					
						
							| 
									
										
										
										
											2017-01-27 02:19:07 +08:00
										 |  |  | 			// `fs.json` can be empty due to previously failed
 | 
					
						
							|  |  |  | 			// PutObject() transaction, if we arrive at such
 | 
					
						
							|  |  |  | 			// a situation we just ignore and continue.
 | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 			if errors.Cause(rerr) != io.EOF { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 				return oi, toObjectErr(rerr, bucket, object) | 
					
						
							| 
									
										
										
										
											2017-01-27 02:19:07 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-17 12:42:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Ignore if `fs.json` is not available, this is true for pre-existing data.
 | 
					
						
							|  |  |  | 	if err != nil && err != errFileNotFound { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return oi, toObjectErr(errors.Trace(err), bucket, object) | 
					
						
							| 
									
										
										
										
											2016-11-30 08:47:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Stat the file to get file size.
 | 
					
						
							| 
									
										
										
										
											2017-11-17 08:00:27 +08:00
										 |  |  | 	fi, err = fsStatFile(pathJoin(fs.fsPath, bucket, object)) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 		return oi, toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-11-30 08:47:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	return fsMeta.ToObjectInfo(bucket, object, fi), nil | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | // Checks bucket and object name validity, returns nil if both are valid.
 | 
					
						
							|  |  |  | func checkBucketAndObjectNamesFS(bucket, object string) error { | 
					
						
							|  |  |  | 	// Verify if bucket is valid.
 | 
					
						
							|  |  |  | 	if !IsValidBucketName(bucket) { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return errors.Trace(BucketNameInvalid{Bucket: bucket}) | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Verify if object is valid.
 | 
					
						
							|  |  |  | 	if len(object) == 0 { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return errors.Trace(ObjectNameInvalid{Bucket: bucket, Object: object}) | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if !IsValidObjectPrefix(object) { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return errors.Trace(ObjectNameInvalid{Bucket: bucket, Object: object}) | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | // GetObjectInfo - reads object metadata and replies back ObjectInfo.
 | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | func (fs fsObjects) GetObjectInfo(bucket, object string) (oi ObjectInfo, e error) { | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	if err := checkBucketAndObjectNamesFS(bucket, object); err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 		return oi, err | 
					
						
							| 
									
										
										
										
											2016-09-03 03:18:35 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if _, err := fs.statBucketDir(bucket); err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 		return oi, toObjectErr(err, bucket) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-03 03:18:35 +08:00
										 |  |  | 	return fs.getObjectInfo(bucket, object) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-10 08:46:46 +08:00
										 |  |  | // This function does the following check, suppose
 | 
					
						
							|  |  |  | // object is "a/b/c/d", stat makes sure that objects ""a/b/c""
 | 
					
						
							|  |  |  | // "a/b" and "a" do not exist.
 | 
					
						
							|  |  |  | func (fs fsObjects) parentDirIsObject(bucket, parent string) bool { | 
					
						
							|  |  |  | 	var isParentDirObject func(string) bool | 
					
						
							|  |  |  | 	isParentDirObject = func(p string) bool { | 
					
						
							| 
									
										
										
										
											2017-09-26 05:47:58 +08:00
										 |  |  | 		if p == "." || p == "/" { | 
					
						
							| 
									
										
										
										
											2017-05-10 08:46:46 +08:00
										 |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if _, err := fsStatFile(pathJoin(fs.fsPath, bucket, p)); err == nil { | 
					
						
							| 
									
										
										
										
											2017-09-26 05:47:58 +08:00
										 |  |  | 			// If there is already a file at prefix "p", return true.
 | 
					
						
							| 
									
										
										
										
											2017-05-10 08:46:46 +08:00
										 |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-09-26 05:47:58 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-10 08:46:46 +08:00
										 |  |  | 		// Check if there is a file as one of the parent paths.
 | 
					
						
							|  |  |  | 		return isParentDirObject(path.Dir(p)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return isParentDirObject(parent) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | // PutObject - creates an object upon reading from the input stream
 | 
					
						
							|  |  |  | // until EOF, writes data directly to configured filesystem path.
 | 
					
						
							|  |  |  | // Additionally writes `fs.json` which carries the necessary metadata
 | 
					
						
							|  |  |  | // for future object operations.
 | 
					
						
							| 
									
										
										
										
											2017-10-22 13:30:34 +08:00
										 |  |  | func (fs fsObjects) PutObject(bucket string, object string, data *hash.Reader, metadata map[string]string) (objInfo ObjectInfo, retErr error) { | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	// No metadata is set, allocate a new one.
 | 
					
						
							|  |  |  | 	if metadata == nil { | 
					
						
							|  |  |  | 		metadata = make(map[string]string) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-15 03:06:24 +08:00
										 |  |  | 	var err error | 
					
						
							| 
									
										
										
										
											2017-05-26 00:22:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Validate if bucket name is valid and exists.
 | 
					
						
							|  |  |  | 	if _, err = fs.statBucketDir(bucket); err != nil { | 
					
						
							|  |  |  | 		return ObjectInfo{}, toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	fsMeta := newFSMetaV1() | 
					
						
							|  |  |  | 	fsMeta.Meta = metadata | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 00:22:43 +08:00
										 |  |  | 	// This is a special case with size as '0' and object ends
 | 
					
						
							|  |  |  | 	// with a slash separator, we treat it like a valid operation
 | 
					
						
							|  |  |  | 	// and return success.
 | 
					
						
							| 
									
										
										
										
											2017-09-20 03:40:27 +08:00
										 |  |  | 	if isObjectDir(object, data.Size()) { | 
					
						
							| 
									
										
										
										
											2017-05-10 08:46:46 +08:00
										 |  |  | 		// Check if an object is present as one of the parent dir.
 | 
					
						
							|  |  |  | 		if fs.parentDirIsObject(bucket, path.Dir(object)) { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 			return ObjectInfo{}, toObjectErr(errors.Trace(errFileAccessDenied), bucket, object) | 
					
						
							| 
									
										
										
										
											2017-05-10 08:46:46 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 		if err = fsMkdirAll(pathJoin(fs.fsPath, bucket, object)); err != nil { | 
					
						
							|  |  |  | 			return ObjectInfo{}, toObjectErr(err, bucket, object) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var fi os.FileInfo | 
					
						
							|  |  |  | 		if fi, err = fsStatDir(pathJoin(fs.fsPath, bucket, object)); err != nil { | 
					
						
							|  |  |  | 			return ObjectInfo{}, toObjectErr(err, bucket, object) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return fsMeta.ToObjectInfo(bucket, object, fi), nil | 
					
						
							| 
									
										
										
										
											2017-01-21 08:33:01 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-05-10 05:32:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-02 15:15:17 +08:00
										 |  |  | 	if err = checkPutObjectArgs(bucket, object, fs); err != nil { | 
					
						
							|  |  |  | 		return ObjectInfo{}, err | 
					
						
							| 
									
										
										
										
											2016-05-14 02:52:36 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-10 08:46:46 +08:00
										 |  |  | 	// Check if an object is present as one of the parent dir.
 | 
					
						
							|  |  |  | 	if fs.parentDirIsObject(bucket, path.Dir(object)) { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return ObjectInfo{}, toObjectErr(errors.Trace(errFileAccessDenied), bucket, object) | 
					
						
							| 
									
										
										
										
											2017-05-10 08:46:46 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-07 00:38:01 +08:00
										 |  |  | 	// Validate input data size and it can never be less than zero.
 | 
					
						
							|  |  |  | 	if data.Size() < 0 { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return ObjectInfo{}, errors.Trace(errInvalidArgument) | 
					
						
							| 
									
										
										
										
											2017-10-07 00:38:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	var wlk *lock.LockedFile | 
					
						
							|  |  |  | 	if bucket != minioMetaBucket { | 
					
						
							| 
									
										
										
										
											2017-04-15 03:06:24 +08:00
										 |  |  | 		bucketMetaDir := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix) | 
					
						
							| 
									
										
										
										
											2017-06-13 08:40:28 +08:00
										 |  |  | 		fsMetaPath := pathJoin(bucketMetaDir, bucket, object, fsMetaJSONFile) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		wlk, err = fs.rwPool.Create(fsMetaPath) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 			return ObjectInfo{}, toObjectErr(errors.Trace(err), bucket, object) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		// This close will allow for locks to be synchronized on `fs.json`.
 | 
					
						
							|  |  |  | 		defer wlk.Close() | 
					
						
							| 
									
										
										
										
											2017-04-15 03:06:24 +08:00
										 |  |  | 		defer func() { | 
					
						
							|  |  |  | 			// Remove meta file when PutObject encounters any error
 | 
					
						
							|  |  |  | 			if retErr != nil { | 
					
						
							|  |  |  | 				tmpDir := pathJoin(fs.fsPath, minioMetaTmpBucket, fs.fsUUID) | 
					
						
							|  |  |  | 				fsRemoveMeta(bucketMetaDir, fsMetaPath, tmpDir) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}() | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-29 15:42:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-09 13:00:31 +08:00
										 |  |  | 	// Uploaded object will first be written to the temporary location which will eventually
 | 
					
						
							|  |  |  | 	// be renamed to the actual location. It is first written to the temporary location
 | 
					
						
							|  |  |  | 	// so that cleaning it up will be easy if the server goes down.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	tempObj := mustGetUUID() | 
					
						
							| 
									
										
										
										
											2016-05-14 02:52:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-04 03:53:12 +08:00
										 |  |  | 	// Allocate a buffer to Read() from request body
 | 
					
						
							|  |  |  | 	bufSize := int64(readSizeV1) | 
					
						
							| 
									
										
										
										
											2017-09-20 03:40:27 +08:00
										 |  |  | 	if size := data.Size(); size > 0 && bufSize > size { | 
					
						
							| 
									
										
										
										
											2016-12-04 03:53:12 +08:00
										 |  |  | 		bufSize = size | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-10-22 13:30:34 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-04 03:53:12 +08:00
										 |  |  | 	buf := make([]byte, int(bufSize)) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	fsTmpObjPath := pathJoin(fs.fsPath, minioMetaTmpBucket, fs.fsUUID, tempObj) | 
					
						
							| 
									
										
										
										
											2017-09-20 03:40:27 +08:00
										 |  |  | 	bytesWritten, err := fsCreateFile(fsTmpObjPath, data, buf, data.Size()) | 
					
						
							| 
									
										
										
										
											2016-12-04 03:53:12 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		fsRemoveFile(fsTmpObjPath) | 
					
						
							| 
									
										
										
										
											2016-12-04 03:53:12 +08:00
										 |  |  | 		errorIf(err, "Failed to create object %s/%s", bucket, object) | 
					
						
							|  |  |  | 		return ObjectInfo{}, toObjectErr(err, bucket, object) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-05 16:04:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-25 03:25:42 +08:00
										 |  |  | 	metadata["etag"] = hex.EncodeToString(data.MD5Current()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-04 03:53:12 +08:00
										 |  |  | 	// Should return IncompleteBody{} error when reader has fewer
 | 
					
						
							|  |  |  | 	// bytes than specified in request header.
 | 
					
						
							| 
									
										
										
										
											2017-09-20 03:40:27 +08:00
										 |  |  | 	if bytesWritten < data.Size() { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		fsRemoveFile(fsTmpObjPath) | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return ObjectInfo{}, errors.Trace(IncompleteBody{}) | 
					
						
							| 
									
										
										
										
											2016-05-14 02:52:36 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-04 03:53:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-20 13:52:03 +08:00
										 |  |  | 	// Delete the temporary object in the case of a
 | 
					
						
							|  |  |  | 	// failure. If PutObject succeeds, then there would be
 | 
					
						
							|  |  |  | 	// nothing to delete.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	defer fsRemoveFile(fsTmpObjPath) | 
					
						
							| 
									
										
										
										
											2016-05-14 02:52:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-05 16:04:50 +08:00
										 |  |  | 	// Entire object was written to the temp location, now it's safe to rename it to the actual location.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	fsNSObjPath := pathJoin(fs.fsPath, bucket, object) | 
					
						
							|  |  |  | 	if err = fsRenameFile(fsTmpObjPath, fsNSObjPath); err != nil { | 
					
						
							|  |  |  | 		return ObjectInfo{}, toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2016-05-14 02:52:36 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-28 03:33:08 +08:00
										 |  |  | 	if bucket != minioMetaBucket { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		// Write FS metadata after a successful namespace operation.
 | 
					
						
							|  |  |  | 		if _, err = fsMeta.WriteTo(wlk); err != nil { | 
					
						
							|  |  |  | 			return ObjectInfo{}, toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2016-11-28 03:33:08 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-07-22 08:31:14 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-11-12 08:36:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Stat the file to fetch timestamp, size.
 | 
					
						
							|  |  |  | 	fi, err := fsStatFile(pathJoin(fs.fsPath, bucket, object)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-27 07:40:10 +08:00
										 |  |  | 		return ObjectInfo{}, toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Success.
 | 
					
						
							|  |  |  | 	return fsMeta.ToObjectInfo(bucket, object, fi), nil | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-22 08:31:14 +08:00
										 |  |  | // DeleteObject - deletes an object from a bucket, this operation is destructive
 | 
					
						
							|  |  |  | // and there are no rollbacks supported.
 | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | func (fs fsObjects) DeleteObject(bucket, object string) error { | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	if err := checkBucketAndObjectNamesFS(bucket, object); err != nil { | 
					
						
							| 
									
										
										
										
											2016-12-02 15:15:17 +08:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-11 01:20:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if _, err := fs.statBucketDir(bucket); err != nil { | 
					
						
							|  |  |  | 		return toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	minioMetaBucketDir := pathJoin(fs.fsPath, minioMetaBucket) | 
					
						
							| 
									
										
										
										
											2017-06-13 08:40:28 +08:00
										 |  |  | 	fsMetaPath := pathJoin(minioMetaBucketDir, bucketMetaPrefix, bucket, object, fsMetaJSONFile) | 
					
						
							| 
									
										
										
										
											2016-11-28 03:33:08 +08:00
										 |  |  | 	if bucket != minioMetaBucket { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		rwlk, lerr := fs.rwPool.Write(fsMetaPath) | 
					
						
							|  |  |  | 		if lerr == nil { | 
					
						
							|  |  |  | 			// This close will allow for fs locks to be synchronized on `fs.json`.
 | 
					
						
							|  |  |  | 			defer rwlk.Close() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if lerr != nil && lerr != errFileNotFound { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 			return toObjectErr(errors.Trace(lerr), bucket, object) | 
					
						
							| 
									
										
										
										
											2016-11-28 03:33:08 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-07-22 08:31:14 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Delete the object.
 | 
					
						
							|  |  |  | 	if err := fsDeleteFile(pathJoin(fs.fsPath, bucket), pathJoin(fs.fsPath, bucket, object)); err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-27 07:40:10 +08:00
										 |  |  | 		return toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if bucket != minioMetaBucket { | 
					
						
							|  |  |  | 		// Delete the metadata object.
 | 
					
						
							|  |  |  | 		err := fsDeleteFile(minioMetaBucketDir, fsMetaPath) | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		if err != nil && errors.Cause(err) != errFileNotFound { | 
					
						
							| 
									
										
										
										
											2017-01-27 07:40:10 +08:00
										 |  |  | 			return toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | // list of all errors that can be ignored in tree walk operation in FS
 | 
					
						
							|  |  |  | var fsTreeWalkIgnoredErrs = append(baseIgnoredErrs, []error{ | 
					
						
							|  |  |  | 	errFileNotFound, | 
					
						
							|  |  |  | 	errVolumeNotFound, | 
					
						
							|  |  |  | }...) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Returns function "listDir" of the type listDirFunc.
 | 
					
						
							|  |  |  | // isLeaf - is used by listDir function to check if an entry
 | 
					
						
							|  |  |  | // is a leaf or non-leaf entry.
 | 
					
						
							|  |  |  | func (fs fsObjects) listDirFactory(isLeaf isLeafFunc) listDirFunc { | 
					
						
							|  |  |  | 	// listDir - lists all the entries at a given prefix and given entry in the prefix.
 | 
					
						
							|  |  |  | 	listDir := func(bucket, prefixDir, prefixEntry string) (entries []string, delayIsLeaf bool, err error) { | 
					
						
							|  |  |  | 		entries, err = readDir(pathJoin(fs.fsPath, bucket, prefixDir)) | 
					
						
							| 
									
										
										
										
											2017-01-27 07:39:22 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, false, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		entries, delayIsLeaf = filterListEntries(bucket, prefixDir, entries, prefixEntry, isLeaf) | 
					
						
							|  |  |  | 		return entries, delayIsLeaf, nil | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Return list factory instance.
 | 
					
						
							|  |  |  | 	return listDir | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | // getObjectETag is a helper function, which returns only the md5sum
 | 
					
						
							|  |  |  | // of the file on the disk.
 | 
					
						
							|  |  |  | func (fs fsObjects) getObjectETag(bucket, entry string) (string, error) { | 
					
						
							| 
									
										
										
										
											2017-06-13 08:40:28 +08:00
										 |  |  | 	fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, entry, fsMetaJSONFile) | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Read `fs.json` to perhaps contend with
 | 
					
						
							|  |  |  | 	// parallel Put() operations.
 | 
					
						
							|  |  |  | 	rlk, err := fs.rwPool.Open(fsMetaPath) | 
					
						
							|  |  |  | 	// Ignore if `fs.json` is not available, this is true for pre-existing data.
 | 
					
						
							|  |  |  | 	if err != nil && err != errFileNotFound { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return "", toObjectErr(errors.Trace(err), bucket, entry) | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If file is not found, we don't need to proceed forward.
 | 
					
						
							|  |  |  | 	if err == errFileNotFound { | 
					
						
							|  |  |  | 		return "", nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Read from fs metadata only if it exists.
 | 
					
						
							|  |  |  | 	defer fs.rwPool.Close(fsMetaPath) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-24 08:59:14 +08:00
										 |  |  | 	// Fetch the size of the underlying file.
 | 
					
						
							|  |  |  | 	fi, err := rlk.LockedFile.Stat() | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return "", toObjectErr(errors.Trace(err), bucket, entry) | 
					
						
							| 
									
										
										
										
											2017-08-24 08:59:14 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// `fs.json` can be empty due to previously failed
 | 
					
						
							|  |  |  | 	// PutObject() transaction, if we arrive at such
 | 
					
						
							|  |  |  | 	// a situation we just ignore and continue.
 | 
					
						
							|  |  |  | 	if fi.Size() == 0 { | 
					
						
							|  |  |  | 		return "", nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Wrap the locked file in a ReadAt() backend section reader to
 | 
					
						
							|  |  |  | 	// make sure the underlying offsets don't move.
 | 
					
						
							|  |  |  | 	fsMetaBuf, err := ioutil.ReadAll(io.NewSectionReader(rlk.LockedFile, 0, fi.Size())) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return "", errors.Trace(err) | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 	// Check if FS metadata is valid, if not return error.
 | 
					
						
							|  |  |  | 	if !isFSMetaValid(parseFSVersion(fsMetaBuf), parseFSFormat(fsMetaBuf)) { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 		return "", toObjectErr(errors.Trace(errCorruptedFormat), bucket, entry) | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return extractETag(parseFSMetaMap(fsMetaBuf)), nil | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-18 04:26:08 +08:00
										 |  |  | // ListObjects - list all objects at prefix upto maxKeys., optionally delimited by '/'. Maintains the list pool
 | 
					
						
							|  |  |  | // state for future re-entrant list requests.
 | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | func (fs fsObjects) ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (loi ListObjectsInfo, e error) { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if err := checkListObjsArgs(bucket, prefix, marker, delimiter, fs); err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 		return loi, err | 
					
						
							| 
									
										
										
										
											2016-05-27 05:43:17 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if _, err := fs.statBucketDir(bucket); err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 		return loi, err | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// With max keys of zero we have reached eof, return right here.
 | 
					
						
							|  |  |  | 	if maxKeys == 0 { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 		return loi, nil | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-28 06:43:51 +08:00
										 |  |  | 	// For delimiter and prefix as '/' we do not list anything at all
 | 
					
						
							|  |  |  | 	// since according to s3 spec we stop at the 'delimiter'
 | 
					
						
							|  |  |  | 	// along // with the prefix. On a flat namespace with 'prefix'
 | 
					
						
							|  |  |  | 	// as '/' we don't have any entries, since all the keys are
 | 
					
						
							|  |  |  | 	// of form 'keyName/...'
 | 
					
						
							|  |  |  | 	if delimiter == slashSeparator && prefix == slashSeparator { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 		return loi, nil | 
					
						
							| 
									
										
										
										
											2016-05-28 06:43:51 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	// 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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Convert entry to ObjectInfo
 | 
					
						
							|  |  |  | 	entryToObjectInfo := func(entry string) (objInfo ObjectInfo, err error) { | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 		// Protect the entry from concurrent deletes, or renames.
 | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 		objectLock := globalNSMutex.NewNSLock(bucket, entry) | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | 		if err = objectLock.GetRLock(globalListingTimeout); err != nil { | 
					
						
							|  |  |  | 			return ObjectInfo{}, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if hasSuffix(entry, slashSeparator) { | 
					
						
							|  |  |  | 			var fi os.FileInfo | 
					
						
							|  |  |  | 			fi, err = fsStatDir(pathJoin(fs.fsPath, bucket, entry)) | 
					
						
							|  |  |  | 			objectLock.RUnlock() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return objInfo, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Success.
 | 
					
						
							|  |  |  | 			return ObjectInfo{ | 
					
						
							|  |  |  | 				// Object name needs to be full path.
 | 
					
						
							|  |  |  | 				Name:    entry, | 
					
						
							|  |  |  | 				Bucket:  bucket, | 
					
						
							|  |  |  | 				Size:    fi.Size(), | 
					
						
							|  |  |  | 				ModTime: fi.ModTime(), | 
					
						
							|  |  |  | 				IsDir:   fi.IsDir(), | 
					
						
							|  |  |  | 			}, nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 		var etag string | 
					
						
							|  |  |  | 		etag, err = fs.getObjectETag(bucket, entry) | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 		objectLock.RUnlock() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return ObjectInfo{}, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		// Stat the file to get file size.
 | 
					
						
							|  |  |  | 		var fi os.FileInfo | 
					
						
							|  |  |  | 		fi, err = fsStatFile(pathJoin(fs.fsPath, bucket, entry)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-27 07:40:10 +08:00
										 |  |  | 			return ObjectInfo{}, toObjectErr(err, bucket, entry) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Success.
 | 
					
						
							|  |  |  | 		return ObjectInfo{ | 
					
						
							|  |  |  | 			Name:    entry, | 
					
						
							|  |  |  | 			Bucket:  bucket, | 
					
						
							|  |  |  | 			Size:    fi.Size(), | 
					
						
							|  |  |  | 			ModTime: fi.ModTime(), | 
					
						
							|  |  |  | 			IsDir:   fi.IsDir(), | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 			ETag:    etag, | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 		}, nil | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-18 02:36:33 +08:00
										 |  |  | 	heal := false // true only for xl.ListObjectsHeal()
 | 
					
						
							|  |  |  | 	walkResultCh, endWalkCh := fs.listPool.Release(listParams{bucket, recursive, marker, prefix, heal}) | 
					
						
							| 
									
										
										
										
											2016-06-25 07:41:57 +08:00
										 |  |  | 	if walkResultCh == nil { | 
					
						
							|  |  |  | 		endWalkCh = make(chan struct{}) | 
					
						
							| 
									
										
										
										
											2016-07-18 06:16:52 +08:00
										 |  |  | 		isLeaf := func(bucket, object string) bool { | 
					
						
							|  |  |  | 			// bucket argument is unused as we don't need to StatFile
 | 
					
						
							|  |  |  | 			// to figure if it's a file, just need to check that the
 | 
					
						
							|  |  |  | 			// object string does not end with "/".
 | 
					
						
							| 
									
										
										
										
											2017-02-17 06:52:14 +08:00
										 |  |  | 			return !hasSuffix(object, slashSeparator) | 
					
						
							| 
									
										
										
										
											2016-07-18 06:16:52 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		listDir := fs.listDirFactory(isLeaf) | 
					
						
							| 
									
										
										
										
											2016-07-18 06:16:52 +08:00
										 |  |  | 		walkResultCh = startTreeWalk(bucket, prefix, marker, recursive, listDir, isLeaf, endWalkCh) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-14 03:48:02 +08:00
										 |  |  | 	var objInfos []ObjectInfo | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	var eof bool | 
					
						
							|  |  |  | 	var nextMarker string | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// List until maxKeys requested.
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	for i := 0; i < maxKeys; { | 
					
						
							| 
									
										
										
										
											2016-06-25 07:41:57 +08:00
										 |  |  | 		walkResult, ok := <-walkResultCh | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 		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.
 | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 			if errors.Cause(walkResult.err) == errFileNotFound { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 				return loi, nil | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 			return loi, toObjectErr(walkResult.err, bucket, prefix) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-11-14 03:48:02 +08:00
										 |  |  | 		objInfo, err := entryToObjectInfo(walkResult.entry) | 
					
						
							| 
									
										
										
										
											2016-05-27 05:43:17 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2017-08-24 08:59:14 +08:00
										 |  |  | 			errorIf(err, "Unable to fetch object info for %s", walkResult.entry) | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 			return loi, nil | 
					
						
							| 
									
										
										
										
											2016-05-27 05:43:17 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-11-14 03:48:02 +08:00
										 |  |  | 		nextMarker = objInfo.Name | 
					
						
							|  |  |  | 		objInfos = append(objInfos, objInfo) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 		if walkResult.end { | 
					
						
							|  |  |  | 			eof = true | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		i++ | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Save list routine for the next marker if we haven't reached EOF.
 | 
					
						
							| 
									
										
										
										
											2016-08-18 02:36:33 +08:00
										 |  |  | 	params := listParams{bucket, recursive, nextMarker, prefix, heal} | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	if !eof { | 
					
						
							| 
									
										
										
										
											2016-06-25 07:41:57 +08:00
										 |  |  | 		fs.listPool.Set(params, walkResultCh, endWalkCh) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	result := ListObjectsInfo{IsTruncated: !eof} | 
					
						
							| 
									
										
										
										
											2016-11-14 03:48:02 +08:00
										 |  |  | 	for _, objInfo := range objInfos { | 
					
						
							|  |  |  | 		result.NextMarker = objInfo.Name | 
					
						
							|  |  |  | 		if objInfo.IsDir { | 
					
						
							|  |  |  | 			result.Prefixes = append(result.Prefixes, objInfo.Name) | 
					
						
							| 
									
										
										
										
											2016-07-25 06:52:12 +08:00
										 |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-11-14 03:48:02 +08:00
										 |  |  | 		result.Objects = append(result.Objects, objInfo) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Success.
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	return result, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-18 02:36:33 +08:00
										 |  |  | // HealObject - no-op for fs. Valid only for XL.
 | 
					
						
							| 
									
										
										
										
											2017-04-01 08:55:15 +08:00
										 |  |  | func (fs fsObjects) HealObject(bucket, object string) (int, int, error) { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 	return 0, 0, errors.Trace(NotImplemented{}) | 
					
						
							| 
									
										
										
										
											2016-08-18 02:36:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-15 10:57:40 +08:00
										 |  |  | // HealBucket - no-op for fs, Valid only for XL.
 | 
					
						
							|  |  |  | func (fs fsObjects) HealBucket(bucket string) error { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 	return errors.Trace(NotImplemented{}) | 
					
						
							| 
									
										
										
										
											2016-10-15 10:57:40 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ListObjectsHeal - list all objects to be healed. Valid only for XL
 | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | func (fs fsObjects) ListObjectsHeal(bucket, prefix, marker, delimiter string, maxKeys int) (loi ListObjectsInfo, e error) { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 	return loi, errors.Trace(NotImplemented{}) | 
					
						
							| 
									
										
										
										
											2016-08-18 02:36:33 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-01-20 01:34:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // ListBucketsHeal - list all buckets to be healed. Valid only for XL
 | 
					
						
							|  |  |  | func (fs fsObjects) ListBucketsHeal() ([]BucketInfo, error) { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 	return []BucketInfo{}, errors.Trace(NotImplemented{}) | 
					
						
							| 
									
										
										
										
											2017-01-20 01:34:18 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-03-16 15:15:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (fs fsObjects) ListUploadsHeal(bucket, prefix, marker, uploadIDMarker, | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 	delimiter string, maxUploads int) (lmi ListMultipartsInfo, e error) { | 
					
						
							| 
									
										
										
										
											2017-11-26 03:58:29 +08:00
										 |  |  | 	return lmi, errors.Trace(NotImplemented{}) | 
					
						
							| 
									
										
										
										
											2017-03-16 15:15:06 +08:00
										 |  |  | } |