| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2019-12-26 14:05:54 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2019-12-26 14:05:54 +08:00
										 |  |  | 	"os/user" | 
					
						
							| 
									
										
										
										
											2017-05-10 08:46:46 +08:00
										 |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2018-02-01 05:17:24 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 	"sync/atomic" | 
					
						
							| 
									
										
										
										
											2020-05-26 02:32:53 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-06 06:51:27 +08:00
										 |  |  | 	jsoniter "github.com/json-iterator/go" | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	"github.com/minio/madmin-go" | 
					
						
							| 
									
										
										
										
											2020-07-15 00:38:05 +08:00
										 |  |  | 	"github.com/minio/minio-go/v7/pkg/s3utils" | 
					
						
							|  |  |  | 	"github.com/minio/minio-go/v7/pkg/tags" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/color" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/config" | 
					
						
							|  |  |  | 	xhttp "github.com/minio/minio/internal/http" | 
					
						
							|  |  |  | 	xioutil "github.com/minio/minio/internal/ioutil" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/lock" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/mountinfo" | 
					
						
							| 
									
										
										
										
											2021-05-30 12:16:42 +08:00
										 |  |  | 	"github.com/minio/pkg/bucket/policy" | 
					
						
							| 
									
										
										
										
											2021-05-29 06:17:01 +08:00
										 |  |  | 	"github.com/minio/pkg/mimedb" | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | // Default etag is used for pre-existing objects.
 | 
					
						
							|  |  |  | var defaultEtag = "00000000000000000000000000000000-1" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-21 04:21:12 +08:00
										 |  |  | // FSObjects - Implements fs object layer.
 | 
					
						
							|  |  |  | type FSObjects struct { | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	GatewayUnsupported | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Path to be exported over S3 API.
 | 
					
						
							|  |  |  | 	fsPath string | 
					
						
							| 
									
										
										
										
											2018-03-29 05:14:06 +08:00
										 |  |  | 	// meta json filename, varies by fs / cache backend.
 | 
					
						
							|  |  |  | 	metaJSONFile string | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2019-04-18 00:52:08 +08:00
										 |  |  | 	listPool *TreeWalkPool | 
					
						
							| 
									
										
										
										
											2016-11-21 15:42:53 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 	diskMount bool | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-01 05:17:24 +08:00
										 |  |  | 	appendFileMap   map[string]*fsAppendFile | 
					
						
							|  |  |  | 	appendFileMapMu sync.Mutex | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-01 05:17:24 +08:00
										 |  |  | 	// To manage the appendRoutine go-routines
 | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | 	nsMutex *nsLockMap | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-01 05:17:24 +08:00
										 |  |  | // Represents the background append file.
 | 
					
						
							|  |  |  | type fsAppendFile struct { | 
					
						
							|  |  |  | 	sync.Mutex | 
					
						
							|  |  |  | 	parts    []PartInfo // List of parts appended.
 | 
					
						
							|  |  |  | 	filePath string     // Absolute path of the file in the temp location.
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							| 
									
										
										
										
											2018-05-09 10:04:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 	if err := os.MkdirAll(metaBucketPath, 0o777); err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	metaTmpPath := pathJoin(fsPath, minioMetaTmpBucket, fsUUID) | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 	if err := os.MkdirAll(metaTmpPath, 0o777); err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 	if err := os.MkdirAll(pathJoin(fsPath, dataUsageBucket), 0o777); err != nil { | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	metaMultipartPath := pathJoin(fsPath, minioMetaMultipartBucket) | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 	return os.MkdirAll(metaMultipartPath, 0o777) | 
					
						
							| 
									
										
										
										
											2016-09-16 04:43:40 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-30 05:38:26 +08:00
										 |  |  | // NewFSObjectLayer - initialize new fs object layer.
 | 
					
						
							|  |  |  | func NewFSObjectLayer(fsPath string) (ObjectLayer, error) { | 
					
						
							| 
									
										
										
										
											2020-04-10 00:30:02 +08:00
										 |  |  | 	ctx := GlobalContext | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	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 | 
					
						
							| 
									
										
										
										
											2021-01-23 07:38:21 +08:00
										 |  |  | 	if fsPath, err = getValidPath(fsPath); err != nil { | 
					
						
							| 
									
										
										
										
											2018-06-29 03:01:05 +08:00
										 |  |  | 		if err == errMinDiskSize { | 
					
						
							| 
									
										
										
										
											2020-05-08 07:12:16 +08:00
										 |  |  | 			return nil, config.ErrUnableToWriteInBackend(err).Hint(err.Error()) | 
					
						
							| 
									
										
										
										
											2018-06-29 03:01:05 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-05-08 07:12:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-26 14:05:54 +08:00
										 |  |  | 		// Show a descriptive error with a hint about how to fix it.
 | 
					
						
							|  |  |  | 		var username string | 
					
						
							|  |  |  | 		if u, err := user.Current(); err == nil { | 
					
						
							|  |  |  | 			username = u.Username | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			username = "<your-username>" | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-17 06:32:29 +08:00
										 |  |  | 		hint := fmt.Sprintf("Use 'sudo chown -R %s %s && sudo chmod u+rxw %s' to provide sufficient permissions.", username, fsPath, fsPath) | 
					
						
							| 
									
										
										
										
											2019-12-26 14:05:54 +08:00
										 |  |  | 		return nil, config.ErrUnableToWriteInBackend(err).Hint(hint) | 
					
						
							| 
									
										
										
										
											2017-07-11 09:14:48 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2018-05-09 10:04:36 +08:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-11-26 10:11:50 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-13 23:29:07 +08:00
										 |  |  | 	// Initialize `format.json`, this function also returns.
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	rlk, err := initFormatFS(ctx, fsPath) | 
					
						
							| 
									
										
										
										
											2017-06-13 23:29:07 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 13:01:58 +08:00
										 |  |  | 	// Initialize fs objects.
 | 
					
						
							| 
									
										
										
										
											2018-02-21 04:21:12 +08:00
										 |  |  | 	fs := &FSObjects{ | 
					
						
							| 
									
										
										
										
											2018-03-29 05:14:06 +08:00
										 |  |  | 		fsPath:       fsPath, | 
					
						
							| 
									
										
										
										
											2018-03-30 05:38:26 +08:00
										 |  |  | 		metaJSONFile: fsMetaJSONFile, | 
					
						
							| 
									
										
										
										
											2018-03-29 05:14:06 +08:00
										 |  |  | 		fsUUID:       fsUUID, | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		rwPool: &fsIOPool{ | 
					
						
							|  |  |  | 			readersMap: make(map[string]*lock.RLockedFile), | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2018-06-05 09:35:41 +08:00
										 |  |  | 		nsMutex:       newNSLock(false), | 
					
						
							| 
									
										
										
										
											2019-04-18 00:52:08 +08:00
										 |  |  | 		listPool:      NewTreeWalkPool(globalLookupTimeout), | 
					
						
							| 
									
										
										
										
											2018-06-05 09:35:41 +08:00
										 |  |  | 		appendFileMap: make(map[string]*fsAppendFile), | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 		diskMount:     mountinfo.IsLikelyMountPoint(fsPath), | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-05 01:52:28 +08:00
										 |  |  | 	go fs.cleanupStaleUploads(ctx) | 
					
						
							| 
									
										
										
										
											2020-08-21 04:17:42 +08:00
										 |  |  | 	go intDataUpdateTracker.start(ctx, fsPath) | 
					
						
							| 
									
										
										
										
											2018-02-16 09: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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | // NewNSLock - initialize a new namespace RWLocker instance.
 | 
					
						
							| 
									
										
										
										
											2020-11-05 00:25:42 +08:00
										 |  |  | func (fs *FSObjects) NewNSLock(bucket string, objects ...string) RWLocker { | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	// lockers are explicitly 'nil' for FS mode since there are only local lockers
 | 
					
						
							| 
									
										
										
										
											2020-11-05 00:25:42 +08:00
										 |  |  | 	return fs.nsMutex.NewNSLock(nil, bucket, objects...) | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-23 04:09:24 +08:00
										 |  |  | // SetDriveCounts no-op
 | 
					
						
							|  |  |  | func (fs *FSObjects) SetDriveCounts() []int { | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2020-08-06 04:31:12 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-21 04:21:12 +08:00
										 |  |  | // Shutdown - should be called when process shuts down.
 | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  | func (fs *FSObjects) Shutdown(ctx context.Context) 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.
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	return fsRemoveAll(ctx, pathJoin(fs.fsPath, minioMetaTmpBucket, fs.fsUUID)) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-22 01:35:19 +08:00
										 |  |  | // BackendInfo - returns backend information
 | 
					
						
							| 
									
										
										
										
											2021-03-05 06:36:23 +08:00
										 |  |  | func (fs *FSObjects) BackendInfo() madmin.BackendInfo { | 
					
						
							|  |  |  | 	return madmin.BackendInfo{Type: madmin.FS} | 
					
						
							| 
									
										
										
										
											2020-12-22 01:35:19 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | // LocalStorageInfo - returns underlying storage statistics.
 | 
					
						
							|  |  |  | func (fs *FSObjects) LocalStorageInfo(ctx context.Context) (StorageInfo, []error) { | 
					
						
							|  |  |  | 	return fs.StorageInfo(ctx) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-27 05:13:10 +08:00
										 |  |  | // StorageInfo - returns underlying storage statistics.
 | 
					
						
							| 
									
										
										
										
											2021-01-05 01:42:09 +08:00
										 |  |  | func (fs *FSObjects) StorageInfo(ctx context.Context) (StorageInfo, []error) { | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 	di, err := getDiskInfo(fs.fsPath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-05-29 04:03:04 +08:00
										 |  |  | 		return StorageInfo{}, []error{err} | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	storageInfo := StorageInfo{ | 
					
						
							| 
									
										
										
										
											2020-07-14 00:51:07 +08:00
										 |  |  | 		Disks: []madmin.Disk{ | 
					
						
							|  |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2021-06-30 22:42:38 +08:00
										 |  |  | 				State:          madmin.DriveStateOk, | 
					
						
							| 
									
										
										
										
											2020-07-14 00:51:07 +08:00
										 |  |  | 				TotalSpace:     di.Total, | 
					
						
							| 
									
										
										
										
											2020-09-30 13:54:02 +08:00
										 |  |  | 				UsedSpace:      di.Used, | 
					
						
							| 
									
										
										
										
											2020-07-14 00:51:07 +08:00
										 |  |  | 				AvailableSpace: di.Free, | 
					
						
							|  |  |  | 				DrivePath:      fs.fsPath, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2016-05-27 05:13:10 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-05 06:36:23 +08:00
										 |  |  | 	storageInfo.Backend.Type = madmin.FS | 
					
						
							| 
									
										
										
										
											2020-05-29 04:03:04 +08:00
										 |  |  | 	return storageInfo, nil | 
					
						
							| 
									
										
										
										
											2016-05-27 05:13:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | // NSScanner returns data usage stats of the current FS deployment
 | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | func (fs *FSObjects) NSScanner(ctx context.Context, bf *bloomFilter, updates chan<- DataUsageInfo, wantCycle uint32) error { | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 	defer close(updates) | 
					
						
							| 
									
										
										
										
											2020-05-26 02:32:53 +08:00
										 |  |  | 	// Load bucket totals
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	var totalCache dataUsageCache | 
					
						
							|  |  |  | 	err := totalCache.load(ctx, fs, dataUsageCacheName) | 
					
						
							| 
									
										
										
										
											2020-05-26 02:32:53 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	totalCache.Info.Name = dataUsageRoot | 
					
						
							| 
									
										
										
										
											2020-05-26 02:32:53 +08:00
										 |  |  | 	buckets, err := fs.ListBuckets(ctx) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-19 01:41:36 +08:00
										 |  |  | 	if len(buckets) == 0 { | 
					
						
							|  |  |  | 		totalCache.keepBuckets(buckets) | 
					
						
							|  |  |  | 		updates <- totalCache.dui(dataUsageRoot, buckets) | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-16 00:24:41 +08:00
										 |  |  | 	for i, b := range buckets { | 
					
						
							|  |  |  | 		if isReservedOrInvalidBucket(b.Name, false) { | 
					
						
							|  |  |  | 			// Delete bucket...
 | 
					
						
							|  |  |  | 			buckets = append(buckets[:i], buckets[i+1:]...) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-19 01:41:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-17 23:54:41 +08:00
										 |  |  | 	totalCache.Info.BloomFilter = bf.bytes() | 
					
						
							| 
									
										
										
										
											2020-05-26 02:32:53 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	// Clear totals.
 | 
					
						
							|  |  |  | 	var root dataUsageEntry | 
					
						
							|  |  |  | 	if r := totalCache.root(); r != nil { | 
					
						
							|  |  |  | 		root.Children = r.Children | 
					
						
							| 
									
										
										
										
											2020-05-26 02:32:53 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	totalCache.replace(dataUsageRoot, "", root) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Delete all buckets that does not exist anymore.
 | 
					
						
							|  |  |  | 	totalCache.keepBuckets(buckets) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, b := range buckets { | 
					
						
							|  |  |  | 		// Load bucket cache.
 | 
					
						
							|  |  |  | 		var bCache dataUsageCache | 
					
						
							|  |  |  | 		err := bCache.load(ctx, fs, path.Join(b.Name, dataUsageCacheName)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if bCache.Info.Name == "" { | 
					
						
							|  |  |  | 			bCache.Info.Name = b.Name | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		bCache.Info.BloomFilter = totalCache.Info.BloomFilter | 
					
						
							| 
									
										
										
										
											2021-09-16 00:24:41 +08:00
										 |  |  | 		bCache.Info.NextCycle = wantCycle | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 		upds := make(chan dataUsageEntry, 1) | 
					
						
							|  |  |  | 		var wg sync.WaitGroup | 
					
						
							|  |  |  | 		wg.Add(1) | 
					
						
							|  |  |  | 		go func() { | 
					
						
							|  |  |  | 			defer wg.Done() | 
					
						
							|  |  |  | 			for update := range upds { | 
					
						
							|  |  |  | 				totalCache.replace(b.Name, dataUsageRoot, update) | 
					
						
							|  |  |  | 				if intDataUpdateTracker.debug { | 
					
						
							| 
									
										
										
										
											2021-09-16 00:24:41 +08:00
										 |  |  | 					logger.Info(color.Green("NSScanner:")+" Got update: %v", len(totalCache.Cache)) | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				cloned := totalCache.clone() | 
					
						
							|  |  |  | 				updates <- cloned.dui(dataUsageRoot, buckets) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}() | 
					
						
							|  |  |  | 		bCache.Info.updates = upds | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 		cache, err := fs.scanBucket(ctx, b.Name, bCache) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		select { | 
					
						
							|  |  |  | 		case <-ctx.Done(): | 
					
						
							|  |  |  | 			return ctx.Err() | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 		cache.Info.BloomFilter = nil | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 		wg.Wait() | 
					
						
							| 
									
										
										
										
											2020-07-01 00:32:36 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if cache.root() == nil { | 
					
						
							|  |  |  | 			if intDataUpdateTracker.debug { | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 				logger.Info(color.Green("NSScanner:") + " No root added. Adding empty") | 
					
						
							| 
									
										
										
										
											2020-07-01 00:32:36 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			cache.replace(cache.Info.Name, dataUsageRoot, dataUsageEntry{}) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		if cache.Info.LastUpdate.After(bCache.Info.LastUpdate) { | 
					
						
							|  |  |  | 			if intDataUpdateTracker.debug { | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 				logger.Info(color.Green("NSScanner:")+" Saving bucket %q cache with %d entries", b.Name, len(cache.Cache)) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			logger.LogIf(ctx, cache.save(ctx, fs, path.Join(b.Name, dataUsageCacheName))) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Merge, save and send update.
 | 
					
						
							|  |  |  | 		// We do it even if unchanged.
 | 
					
						
							|  |  |  | 		cl := cache.clone() | 
					
						
							|  |  |  | 		entry := cl.flatten(*cl.root()) | 
					
						
							|  |  |  | 		totalCache.replace(cl.Info.Name, dataUsageRoot, entry) | 
					
						
							|  |  |  | 		if intDataUpdateTracker.debug { | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 			logger.Info(color.Green("NSScanner:")+" Saving totals cache with %d entries", len(totalCache.Cache)) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-07-29 04:20:50 +08:00
										 |  |  | 		totalCache.Info.LastUpdate = time.Now() | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		logger.LogIf(ctx, totalCache.save(ctx, fs, dataUsageCacheName)) | 
					
						
							|  |  |  | 		cloned := totalCache.clone() | 
					
						
							|  |  |  | 		updates <- cloned.dui(dataUsageRoot, buckets) | 
					
						
							| 
									
										
										
										
											2022-02-01 03:07:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | // scanBucket scans a single bucket in FS mode.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | // The updated cache for the bucket is returned.
 | 
					
						
							|  |  |  | // A partially updated bucket may be returned.
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | func (fs *FSObjects) scanBucket(ctx context.Context, bucket string, cache dataUsageCache) (dataUsageCache, error) { | 
					
						
							| 
									
										
										
										
											2021-06-04 10:18:59 +08:00
										 |  |  | 	defer close(cache.Info.updates) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	// Get bucket policy
 | 
					
						
							|  |  |  | 	// Check if the current bucket has a configured lifecycle policy
 | 
					
						
							|  |  |  | 	lc, err := globalLifecycleSys.Get(bucket) | 
					
						
							|  |  |  | 	if err == nil && lc.HasActiveRules("", true) { | 
					
						
							|  |  |  | 		if intDataUpdateTracker.debug { | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 			logger.Info(color.Green("scanBucket:") + " lifecycle: Active rules found") | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		cache.Info.lifeCycle = lc | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Load bucket info.
 | 
					
						
							| 
									
										
										
										
											2022-01-27 00:34:56 +08:00
										 |  |  | 	cache, err = scanDataFolder(ctx, -1, -1, fs.fsPath, cache, func(item scannerItem) (sizeSummary, error) { | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		bucket, object := item.bucket, item.objectPath() | 
					
						
							| 
									
										
										
										
											2021-02-24 16:14:16 +08:00
										 |  |  | 		fsMetaBytes, err := xioutil.ReadFile(pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, object, fs.metaJSONFile)) | 
					
						
							| 
									
										
										
										
											2020-11-24 00:36:49 +08:00
										 |  |  | 		if err != nil && !osIsNotExist(err) { | 
					
						
							| 
									
										
										
										
											2020-12-14 04:05:54 +08:00
										 |  |  | 			if intDataUpdateTracker.debug { | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 				logger.Info(color.Green("scanBucket:")+" object return unexpected error: %v/%v: %w", item.bucket, item.objectPath(), err) | 
					
						
							| 
									
										
										
										
											2020-12-14 04:05:54 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-12-08 05:47:48 +08:00
										 |  |  | 			return sizeSummary{}, errSkipFile | 
					
						
							| 
									
										
										
										
											2020-05-26 02:32:53 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		fsMeta := newFSMetaV1() | 
					
						
							|  |  |  | 		metaOk := false | 
					
						
							| 
									
										
										
										
											2020-05-26 02:32:53 +08:00
										 |  |  | 		if len(fsMetaBytes) > 0 { | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 			json := jsoniter.ConfigCompatibleWithStandardLibrary | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 			if err = json.Unmarshal(fsMetaBytes, &fsMeta); err == nil { | 
					
						
							|  |  |  | 				metaOk = true | 
					
						
							| 
									
										
										
										
											2020-05-26 02:32:53 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		if !metaOk { | 
					
						
							|  |  |  | 			fsMeta = fs.defaultFsJSON(object) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-05-26 02:32:53 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		// Stat the file.
 | 
					
						
							|  |  |  | 		fi, fiErr := os.Stat(item.Path) | 
					
						
							|  |  |  | 		if fiErr != nil { | 
					
						
							| 
									
										
										
										
											2020-12-14 04:05:54 +08:00
										 |  |  | 			if intDataUpdateTracker.debug { | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 				logger.Info(color.Green("scanBucket:")+" object path missing: %v: %w", item.Path, fiErr) | 
					
						
							| 
									
										
										
										
											2020-12-14 04:05:54 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-12-08 05:47:48 +08:00
										 |  |  | 			return sizeSummary{}, errSkipFile | 
					
						
							| 
									
										
										
										
											2020-05-26 02:32:53 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		oi := fsMeta.ToObjectInfo(bucket, object, fi) | 
					
						
							| 
									
										
										
										
											2021-10-03 00:31:05 +08:00
										 |  |  | 		atomic.AddUint64(&globalScannerStats.accTotalVersions, 1) | 
					
						
							|  |  |  | 		atomic.AddUint64(&globalScannerStats.accTotalObjects, 1) | 
					
						
							| 
									
										
										
										
											2021-08-27 05:06:04 +08:00
										 |  |  | 		sz := item.applyActions(ctx, fs, oi, &sizeSummary{}) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		if sz >= 0 { | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 			return sizeSummary{totalSize: sz, versions: 1}, nil | 
					
						
							| 
									
										
										
										
											2020-05-26 02:32:53 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 		return sizeSummary{totalSize: fi.Size(), versions: 1}, nil | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return cache, err | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  | // Bucket operations
 | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | func (fs *FSObjects) getBucketDir(ctx context.Context, bucket string) (string, error) { | 
					
						
							| 
									
										
										
										
											2018-04-24 11:27:33 +08:00
										 |  |  | 	if bucket == "" || bucket == "." || bucket == ".." { | 
					
						
							|  |  |  | 		return "", errVolumeNotFound | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	bucketDir := pathJoin(fs.fsPath, bucket) | 
					
						
							|  |  |  | 	return bucketDir, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | func (fs *FSObjects) statBucketDir(ctx context.Context, bucket string) (os.FileInfo, error) { | 
					
						
							|  |  |  | 	bucketDir, err := fs.getBucketDir(ctx, bucket) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	st, err := fsStatVolume(ctx, 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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // MakeBucketWithLocation - create a new bucket, returns if it already exists.
 | 
					
						
							|  |  |  | func (fs *FSObjects) MakeBucketWithLocation(ctx context.Context, bucket string, opts BucketOptions) error { | 
					
						
							|  |  |  | 	if opts.LockEnabled || opts.VersioningEnabled { | 
					
						
							| 
									
										
										
										
											2020-05-09 04:44:44 +08:00
										 |  |  | 		return NotImplemented{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-24 11:27:33 +08:00
										 |  |  | 	// Verify if bucket is valid.
 | 
					
						
							| 
									
										
										
										
											2019-03-06 02:42:32 +08:00
										 |  |  | 	if s3utils.CheckValidBucketNameStrict(bucket) != nil { | 
					
						
							|  |  |  | 		return BucketNameInvalid{Bucket: bucket} | 
					
						
							| 
									
										
										
										
											2018-04-24 11:27:33 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 23:25:48 +08:00
										 |  |  | 	defer NSUpdated(bucket, slashSeparator) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	bucketDir, err := fs.getBucketDir(ctx, bucket) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	if err = fsMkdir(ctx, 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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-09 04:44:44 +08:00
										 |  |  | 	meta := newBucketMetadata(bucket) | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	if err := meta.Save(ctx, fs); err != nil { | 
					
						
							| 
									
										
										
										
											2020-05-09 04:44:44 +08:00
										 |  |  | 		return toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	globalBucketMetadataSys.Set(bucket, meta) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-04 04:18:54 +08:00
										 |  |  | // GetBucketPolicy - only needed for FS in NAS mode
 | 
					
						
							|  |  |  | func (fs *FSObjects) GetBucketPolicy(ctx context.Context, bucket string) (*policy.Policy, error) { | 
					
						
							|  |  |  | 	meta, err := loadBucketMetadata(ctx, fs, bucket) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, BucketPolicyNotFound{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if meta.policyConfig == nil { | 
					
						
							|  |  |  | 		return nil, BucketPolicyNotFound{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return meta.policyConfig, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SetBucketPolicy - only needed for FS in NAS mode
 | 
					
						
							|  |  |  | func (fs *FSObjects) SetBucketPolicy(ctx context.Context, bucket string, p *policy.Policy) error { | 
					
						
							|  |  |  | 	meta, err := loadBucketMetadata(ctx, fs, bucket) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 	json := jsoniter.ConfigCompatibleWithStandardLibrary | 
					
						
							| 
									
										
										
										
											2020-06-04 04:18:54 +08:00
										 |  |  | 	configData, err := json.Marshal(p) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	meta.PolicyConfigJSON = configData | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return meta.Save(ctx, fs) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DeleteBucketPolicy - only needed for FS in NAS mode
 | 
					
						
							|  |  |  | func (fs *FSObjects) DeleteBucketPolicy(ctx context.Context, bucket string) error { | 
					
						
							|  |  |  | 	meta, err := loadBucketMetadata(ctx, fs, bucket) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	meta.PolicyConfigJSON = nil | 
					
						
							|  |  |  | 	return meta.Save(ctx, fs) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | // GetBucketInfo - fetch bucket metadata info.
 | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  | func (fs *FSObjects) GetBucketInfo(ctx context.Context, bucket string) (bi BucketInfo, e error) { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	st, err := fs.statBucketDir(ctx, bucket) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 		return bi, toObjectErr(err, bucket) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	createdTime := st.ModTime() | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	meta, err := globalBucketMetadataSys.Get(bucket) | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		createdTime = meta.Created | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  | func (fs *FSObjects) ListBuckets(ctx context.Context) ([]BucketInfo, error) { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if err := checkPathLength(fs.fsPath); err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-10 07:20:51 +08:00
										 |  |  | 	entries, err := readDirWithOpts(fs.fsPath, readDirOpts{count: -1, followDirSymlink: true}) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		logger.LogIf(ctx, errDiskNotFound) | 
					
						
							|  |  |  | 		return nil, toObjectErr(errDiskNotFound) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-15 11:44:18 +08:00
										 |  |  | 	bucketInfos := make([]BucketInfo, 0, len(entries)) | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2019-03-06 02:42:32 +08:00
										 |  |  | 		if isReservedOrInvalidBucket(entry, false) { | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		var fi os.FileInfo | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		fi, err = fsStatVolume(ctx, 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
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 		created := fi.ModTime() | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 		meta, err := globalBucketMetadataSys.Get(fi.Name()) | 
					
						
							| 
									
										
										
										
											2020-05-09 04:44:44 +08:00
										 |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			created = meta.Created | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 		bucketInfos = append(bucketInfos, BucketInfo{ | 
					
						
							| 
									
										
										
										
											2020-05-09 04:44:44 +08:00
										 |  |  | 			Name:    fi.Name(), | 
					
						
							|  |  |  | 			Created: created, | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Sort bucket infos by bucket name.
 | 
					
						
							| 
									
										
										
										
											2020-12-16 09:34:54 +08:00
										 |  |  | 	sort.Slice(bucketInfos, func(i, j int) bool { | 
					
						
							|  |  |  | 		return bucketInfos[i].Name < bucketInfos[j].Name | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2021-10-07 01:24:40 +08:00
										 |  |  | func (fs *FSObjects) DeleteBucket(ctx context.Context, bucket string, opts DeleteBucketOptions) error { | 
					
						
							| 
									
										
										
										
											2021-05-17 23:25:48 +08:00
										 |  |  | 	defer NSUpdated(bucket, slashSeparator) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	bucketDir, err := fs.getBucketDir(ctx, bucket) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return toObjectErr(err, bucket) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 01:24:40 +08:00
										 |  |  | 	if !opts.Force { | 
					
						
							| 
									
										
										
										
											2020-03-28 12:52:59 +08:00
										 |  |  | 		// Attempt to delete regular bucket.
 | 
					
						
							|  |  |  | 		if err = fsRemoveDir(ctx, bucketDir); err != nil { | 
					
						
							|  |  |  | 			return toObjectErr(err, bucket) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		tmpBucketPath := pathJoin(fs.fsPath, minioMetaTmpBucket, bucket+"."+mustGetUUID()) | 
					
						
							| 
									
										
										
										
											2021-11-19 08:09:12 +08:00
										 |  |  | 		if err = Rename(bucketDir, tmpBucketPath); err != nil { | 
					
						
							| 
									
										
										
										
											2020-03-28 12:52:59 +08:00
										 |  |  | 			return toObjectErr(err, bucket) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		go func() { | 
					
						
							|  |  |  | 			fsRemoveAll(ctx, tmpBucketPath) // ignore returned error if any.
 | 
					
						
							|  |  |  | 		}() | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Cleanup all the bucket metadata.
 | 
					
						
							|  |  |  | 	minioMetadataBucketDir := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket) | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	if err = fsRemoveAll(ctx, minioMetadataBucketDir); err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		return toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-10 07:19:30 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-13 10:34:30 +08:00
										 |  |  | 	// Delete all bucket metadata.
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	deleteBucketMetadata(ctx, fs, bucket) | 
					
						
							| 
									
										
										
										
											2018-02-10 07:19:30 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  | // Object Operations
 | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | func (fs *FSObjects) CopyObject(ctx context.Context, srcBucket, srcObject, dstBucket, dstObject string, srcInfo ObjectInfo, srcOpts, dstOpts ObjectOptions) (oi ObjectInfo, err error) { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if srcOpts.VersionID != "" && srcOpts.VersionID != nullVersionID { | 
					
						
							|  |  |  | 		return oi, VersionNotFound{ | 
					
						
							|  |  |  | 			Bucket:    srcBucket, | 
					
						
							|  |  |  | 			Object:    srcObject, | 
					
						
							|  |  |  | 			VersionID: srcOpts.VersionID, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 	cpSrcDstSame := isStringEqual(pathJoin(srcBucket, srcObject), pathJoin(dstBucket, dstObject)) | 
					
						
							| 
									
										
										
										
											2021-05-17 23:25:48 +08:00
										 |  |  | 	defer NSUpdated(dstBucket, dstObject) | 
					
						
							| 
									
										
										
										
											2020-04-28 01:06:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | 	if !cpSrcDstSame { | 
					
						
							| 
									
										
										
										
											2020-11-05 00:25:42 +08:00
										 |  |  | 		objectDWLock := fs.NewNSLock(dstBucket, dstObject) | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 		lkctx, err := objectDWLock.GetLock(ctx, globalOperationTimeout) | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | 			return oi, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 		ctx = lkctx.Context() | 
					
						
							|  |  |  | 		defer objectDWLock.Unlock(lkctx.Cancel) | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	if _, err := fs.statBucketDir(ctx, srcBucket); err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 		return oi, toObjectErr(err, srcBucket) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 	if cpSrcDstSame && srcInfo.metadataOnly { | 
					
						
							| 
									
										
										
										
											2018-03-29 05:14:06 +08:00
										 |  |  | 		fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, srcBucket, srcObject, fs.metaJSONFile) | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 		wlk, err := fs.rwPool.Write(fsMetaPath) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-09-16 00:18:41 +08:00
										 |  |  | 			wlk, err = fs.rwPool.Create(fsMetaPath) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 				return oi, toObjectErr(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() | 
					
						
							| 
									
										
										
										
											2019-06-13 07:42:04 +08:00
										 |  |  | 		if _, err = fsMeta.ReadFrom(ctx, wlk); err != nil { | 
					
						
							|  |  |  | 			// For any error to read fsMeta, set default ETag and proceed.
 | 
					
						
							|  |  |  | 			fsMeta = fs.defaultFsJSON(srcObject) | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-11 02:37:22 +08:00
										 |  |  | 		fsMeta.Meta = cloneMSS(srcInfo.UserDefined) | 
					
						
							| 
									
										
										
										
											2018-03-10 02:50:39 +08:00
										 |  |  | 		fsMeta.Meta["etag"] = srcInfo.ETag | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-28 22:36:38 +08:00
										 |  |  | 		fsObjectPath := pathJoin(fs.fsPath, srcBucket, srcObject) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Update object modtime
 | 
					
						
							|  |  |  | 		err = fsTouch(ctx, fsObjectPath) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return oi, toObjectErr(err, srcBucket, srcObject) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Stat the file to get object info
 | 
					
						
							|  |  |  | 		fi, err := fsStatFile(ctx, fsObjectPath) | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return oi, toObjectErr(err, srcBucket, srcObject) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-05-14 01:12:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-07 03:03:57 +08:00
										 |  |  | 	if err := checkPutObjectArgs(ctx, dstBucket, dstObject, fs); err != nil { | 
					
						
							| 
									
										
										
										
											2019-05-14 01:12:06 +08:00
										 |  |  | 		return ObjectInfo{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-09 13:31:06 +08:00
										 |  |  | 	objInfo, err := fs.putObject(ctx, dstBucket, dstObject, srcInfo.PutObjReader, ObjectOptions{ServerSideEncryption: dstOpts.ServerSideEncryption, UserDefined: srcInfo.UserDefined}) | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return objInfo, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | // GetObjectNInfo - returns object info and a reader for object
 | 
					
						
							|  |  |  | // content.
 | 
					
						
							| 
									
										
										
										
											2018-09-27 18:06:45 +08:00
										 |  |  | func (fs *FSObjects) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error) { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if opts.VersionID != "" && opts.VersionID != nullVersionID { | 
					
						
							|  |  |  | 		return nil, VersionNotFound{ | 
					
						
							|  |  |  | 			Bucket:    bucket, | 
					
						
							|  |  |  | 			Object:    object, | 
					
						
							|  |  |  | 			VersionID: opts.VersionID, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 	if err = checkGetObjArgs(ctx, bucket, object); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, err = fs.statBucketDir(ctx, bucket); err != nil { | 
					
						
							|  |  |  | 		return nil, toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 	nsUnlocker := func() {} | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if lockType != noLock { | 
					
						
							|  |  |  | 		// Lock the object before reading.
 | 
					
						
							| 
									
										
										
										
											2020-11-05 00:25:42 +08:00
										 |  |  | 		lock := fs.NewNSLock(bucket, object) | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  | 		switch lockType { | 
					
						
							|  |  |  | 		case writeLock: | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 			lkctx, err := lock.GetLock(ctx, globalOperationTimeout) | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 			ctx = lkctx.Context() | 
					
						
							|  |  |  | 			nsUnlocker = func() { lock.Unlock(lkctx.Cancel) } | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  | 		case readLock: | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 			lkctx, err := lock.GetRLock(ctx, globalOperationTimeout) | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 			ctx = lkctx.Context() | 
					
						
							|  |  |  | 			nsUnlocker = func() { lock.RUnlock(lkctx.Cancel) } | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Otherwise we get the object info
 | 
					
						
							|  |  |  | 	var objInfo ObjectInfo | 
					
						
							|  |  |  | 	if objInfo, err = fs.getObjectInfo(ctx, bucket, object); err != nil { | 
					
						
							|  |  |  | 		nsUnlocker() | 
					
						
							|  |  |  | 		return nil, toObjectErr(err, bucket, object) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-22 04:08:01 +08:00
										 |  |  | 	// For a directory, we need to return a reader that returns no bytes.
 | 
					
						
							| 
									
										
										
										
											2019-12-06 15:16:06 +08:00
										 |  |  | 	if HasSuffix(object, SlashSeparator) { | 
					
						
							| 
									
										
										
										
											2019-02-27 07:09:14 +08:00
										 |  |  | 		// The lock taken above is released when
 | 
					
						
							|  |  |  | 		// objReader.Close() is called by the caller.
 | 
					
						
							| 
									
										
										
										
											2020-04-21 13:01:59 +08:00
										 |  |  | 		return NewGetObjectReaderFromReader(bytes.NewBuffer(nil), objInfo, opts, nsUnlocker) | 
					
						
							| 
									
										
										
										
											2019-02-27 07:09:14 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 	// Take a rwPool lock for NFS gateway type deployment
 | 
					
						
							|  |  |  | 	rwPoolUnlocker := func() {} | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  | 	if bucket != minioMetaBucket && lockType != noLock { | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 		fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, object, fs.metaJSONFile) | 
					
						
							|  |  |  | 		_, err = fs.rwPool.Open(fsMetaPath) | 
					
						
							|  |  |  | 		if err != nil && err != errFileNotFound { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 			nsUnlocker() | 
					
						
							|  |  |  | 			return nil, toObjectErr(err, bucket, object) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Need to clean up lock after getObject is
 | 
					
						
							|  |  |  | 		// completed.
 | 
					
						
							|  |  |  | 		rwPoolUnlocker = func() { fs.rwPool.Close(fsMetaPath) } | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-01 09:37:58 +08:00
										 |  |  | 	objReaderFn, off, length, err := NewGetObjectReader(rs, objInfo, opts) | 
					
						
							| 
									
										
										
										
											2021-01-23 04:09:24 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-05-01 09:37:58 +08:00
										 |  |  | 		rwPoolUnlocker() | 
					
						
							|  |  |  | 		nsUnlocker() | 
					
						
							| 
									
										
										
										
											2021-01-23 04:09:24 +08:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Read the object, doesn't exist returns an s3 compatible error.
 | 
					
						
							|  |  |  | 	fsObjPath := pathJoin(fs.fsPath, bucket, object) | 
					
						
							|  |  |  | 	readCloser, size, err := fsOpenFile(ctx, fsObjPath, off) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		rwPoolUnlocker() | 
					
						
							|  |  |  | 		nsUnlocker() | 
					
						
							|  |  |  | 		return nil, toObjectErr(err, bucket, object) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-01-23 04:09:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 	closeFn := func() { | 
					
						
							|  |  |  | 		readCloser.Close() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-01-23 04:09:24 +08:00
										 |  |  | 	reader := io.LimitReader(readCloser, length) | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Check if range is valid
 | 
					
						
							|  |  |  | 	if off > size || off+length > size { | 
					
						
							|  |  |  | 		err = InvalidRange{off, length, size} | 
					
						
							| 
									
										
										
										
											2019-10-12 09:50:54 +08:00
										 |  |  | 		logger.LogIf(ctx, err, logger.Application) | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 		closeFn() | 
					
						
							|  |  |  | 		rwPoolUnlocker() | 
					
						
							|  |  |  | 		nsUnlocker() | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-25 00:44:00 +08:00
										 |  |  | 	return objReaderFn(reader, h, closeFn, rwPoolUnlocker, nsUnlocker) | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | // Create a new fs.json file, if the existing one is corrupt. Should happen very rarely.
 | 
					
						
							|  |  |  | func (fs *FSObjects) createFsJSON(object, fsMetaPath string) error { | 
					
						
							|  |  |  | 	fsMeta := newFSMetaV1() | 
					
						
							| 
									
										
										
										
											2020-09-08 00:25:28 +08:00
										 |  |  | 	fsMeta.Meta = map[string]string{ | 
					
						
							|  |  |  | 		"etag":         GenETag(), | 
					
						
							|  |  |  | 		"content-type": mimedb.TypeByExtension(path.Ext(object)), | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | 	wlk, werr := fs.rwPool.Create(fsMetaPath) | 
					
						
							|  |  |  | 	if werr == nil { | 
					
						
							|  |  |  | 		_, err := fsMeta.WriteTo(wlk) | 
					
						
							|  |  |  | 		wlk.Close() | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return werr | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Used to return default etag values when a pre-existing object's meta data is queried.
 | 
					
						
							|  |  |  | func (fs *FSObjects) defaultFsJSON(object string) fsMetaV1 { | 
					
						
							|  |  |  | 	fsMeta := newFSMetaV1() | 
					
						
							| 
									
										
										
										
											2020-09-08 00:25:28 +08:00
										 |  |  | 	fsMeta.Meta = map[string]string{ | 
					
						
							|  |  |  | 		"etag":         defaultEtag, | 
					
						
							|  |  |  | 		"content-type": mimedb.TypeByExtension(path.Ext(object)), | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | 	return fsMeta | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-12 17:53:14 +08:00
										 |  |  | func (fs *FSObjects) getObjectInfoNoFSLock(ctx context.Context, bucket, object string) (oi ObjectInfo, e error) { | 
					
						
							|  |  |  | 	fsMeta := fsMetaV1{} | 
					
						
							|  |  |  | 	if HasSuffix(object, SlashSeparator) { | 
					
						
							|  |  |  | 		fi, err := fsStatDir(ctx, pathJoin(fs.fsPath, bucket, object)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return oi, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return fsMeta.ToObjectInfo(bucket, object, fi), nil | 
					
						
							| 
									
										
										
										
											2021-09-08 23:15:42 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !globalCLIContext.StrictS3Compat { | 
					
						
							|  |  |  | 		// Stat the file to get file size.
 | 
					
						
							|  |  |  | 		fi, err := fsStatFile(ctx, pathJoin(fs.fsPath, bucket, object)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return oi, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return fsMeta.ToObjectInfo(bucket, object, fi), nil | 
					
						
							| 
									
										
										
										
											2020-08-12 17:53:14 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, object, fs.metaJSONFile) | 
					
						
							|  |  |  | 	// Read `fs.json` to perhaps contend with
 | 
					
						
							|  |  |  | 	// parallel Put() operations.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rc, _, err := fsOpenFile(ctx, fsMetaPath, 0) | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		fsMetaBuf, rerr := ioutil.ReadAll(rc) | 
					
						
							|  |  |  | 		rc.Close() | 
					
						
							|  |  |  | 		if rerr == nil { | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 			json := jsoniter.ConfigCompatibleWithStandardLibrary | 
					
						
							| 
									
										
										
										
											2020-08-12 17:53:14 +08:00
										 |  |  | 			if rerr = json.Unmarshal(fsMetaBuf, &fsMeta); rerr != nil { | 
					
						
							|  |  |  | 				// For any error to read fsMeta, set default ETag and proceed.
 | 
					
						
							|  |  |  | 				fsMeta = fs.defaultFsJSON(object) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// For any error to read fsMeta, set default ETag and proceed.
 | 
					
						
							|  |  |  | 			fsMeta = fs.defaultFsJSON(object) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Return a default etag and content-type based on the object's extension.
 | 
					
						
							|  |  |  | 	if err == errFileNotFound { | 
					
						
							|  |  |  | 		fsMeta = fs.defaultFsJSON(object) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Ignore if `fs.json` is not available, this is true for pre-existing data.
 | 
					
						
							|  |  |  | 	if err != nil && err != errFileNotFound { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 		return oi, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Stat the file to get file size.
 | 
					
						
							|  |  |  | 	fi, err := fsStatFile(ctx, pathJoin(fs.fsPath, bucket, object)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return oi, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fsMeta.ToObjectInfo(bucket, object, fi), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | // getObjectInfo - wrapper for reading object metadata and constructs ObjectInfo.
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | func (fs *FSObjects) getObjectInfo(ctx context.Context, bucket, object string) (oi ObjectInfo, e error) { | 
					
						
							| 
									
										
										
										
											2020-08-22 04:08:01 +08:00
										 |  |  | 	if strings.HasSuffix(object, SlashSeparator) && !fs.isObjectDir(bucket, object) { | 
					
						
							|  |  |  | 		return oi, errFileNotFound | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	fsMeta := fsMetaV1{} | 
					
						
							| 
									
										
										
										
											2019-12-06 15:16:06 +08:00
										 |  |  | 	if HasSuffix(object, SlashSeparator) { | 
					
						
							| 
									
										
										
										
											2018-05-09 10:09:15 +08:00
										 |  |  | 		fi, err := fsStatDir(ctx, pathJoin(fs.fsPath, bucket, object)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return oi, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return fsMeta.ToObjectInfo(bucket, object, fi), nil | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-29 05:14:06 +08:00
										 |  |  | 	fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, object, fs.metaJSONFile) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Read `fs.json` to perhaps contend with
 | 
					
						
							|  |  |  | 	// parallel Put() operations.
 | 
					
						
							| 
									
										
										
										
											2018-03-29 05:14:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	rlk, err := fs.rwPool.Open(fsMetaPath) | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		// Read from fs metadata only if it exists.
 | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | 		_, rerr := fsMeta.ReadFrom(ctx, rlk.LockedFile) | 
					
						
							|  |  |  | 		fs.rwPool.Close(fsMetaPath) | 
					
						
							| 
									
										
										
										
											2018-11-10 02:34:59 +08:00
										 |  |  | 		if rerr != nil { | 
					
						
							| 
									
										
										
										
											2019-06-13 07:42:04 +08:00
										 |  |  | 			// For any error to read fsMeta, set default ETag and proceed.
 | 
					
						
							| 
									
										
										
										
											2018-11-10 02:34:59 +08:00
										 |  |  | 			fsMeta = fs.defaultFsJSON(object) | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-17 12:42:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | 	// Return a default etag and content-type based on the object's extension.
 | 
					
						
							|  |  |  | 	if err == errFileNotFound { | 
					
						
							|  |  |  | 		fsMeta = fs.defaultFsJSON(object) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | 		return oi, err | 
					
						
							| 
									
										
										
										
											2016-11-30 08:47:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Stat the file to get file size.
 | 
					
						
							| 
									
										
										
										
											2018-05-09 10:09:15 +08:00
										 |  |  | 	fi, err := fsStatFile(ctx, pathJoin(fs.fsPath, bucket, object)) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | 		return oi, err | 
					
						
							| 
									
										
										
										
											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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | // getObjectInfoWithLock - reads object metadata and replies back ObjectInfo.
 | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | func (fs *FSObjects) getObjectInfoWithLock(ctx context.Context, bucket, object string) (oi ObjectInfo, err error) { | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | 	// Lock the object before reading.
 | 
					
						
							| 
									
										
										
										
											2020-11-05 00:25:42 +08:00
										 |  |  | 	lk := fs.NewNSLock(bucket, object) | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 	lkctx, err := lk.GetRLock(ctx, globalOperationTimeout) | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | 		return oi, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 	ctx = lkctx.Context() | 
					
						
							|  |  |  | 	defer lk.RUnlock(lkctx.Cancel) | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	if err := checkGetObjArgs(ctx, 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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	if _, err := fs.statBucketDir(ctx, bucket); err != nil { | 
					
						
							| 
									
										
										
										
											2018-06-26 09:24:00 +08:00
										 |  |  | 		return oi, err | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-07 03:08:58 +08:00
										 |  |  | 	if strings.HasSuffix(object, SlashSeparator) && !fs.isObjectDir(bucket, object) { | 
					
						
							| 
									
										
										
										
											2018-05-09 10:08:21 +08:00
										 |  |  | 		return oi, errFileNotFound | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	return fs.getObjectInfo(ctx, bucket, object) | 
					
						
							| 
									
										
										
										
											2016-09-03 03:18:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | // GetObjectInfo - reads object metadata and replies back ObjectInfo.
 | 
					
						
							| 
									
										
										
										
											2018-09-11 00:42:43 +08:00
										 |  |  | func (fs *FSObjects) GetObjectInfo(ctx context.Context, bucket, object string, opts ObjectOptions) (oi ObjectInfo, e error) { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if opts.VersionID != "" && opts.VersionID != nullVersionID { | 
					
						
							|  |  |  | 		return oi, VersionNotFound{ | 
					
						
							|  |  |  | 			Bucket:    bucket, | 
					
						
							|  |  |  | 			Object:    object, | 
					
						
							|  |  |  | 			VersionID: opts.VersionID, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | 	oi, err := fs.getObjectInfoWithLock(ctx, bucket, object) | 
					
						
							|  |  |  | 	if err == errCorruptedFormat || err == io.EOF { | 
					
						
							| 
									
										
										
										
											2020-11-05 00:25:42 +08:00
										 |  |  | 		lk := fs.NewNSLock(bucket, object) | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 		lkctx, err := lk.GetLock(ctx, globalOperationTimeout) | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | 			return oi, toObjectErr(err, bucket, object) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, object, fs.metaJSONFile) | 
					
						
							|  |  |  | 		err = fs.createFsJSON(object, fsMetaPath) | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 		lk.Unlock(lkctx.Cancel) | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return oi, toObjectErr(err, bucket, object) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		oi, err = fs.getObjectInfoWithLock(ctx, bucket, object) | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 		return oi, toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2018-04-25 08:36:43 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return oi, toObjectErr(err, bucket, object) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | func (fs *FSObjects) PutObject(ctx context.Context, bucket string, object string, r *PutObjReader, opts ObjectOptions) (objInfo ObjectInfo, err error) { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if opts.Versioned { | 
					
						
							|  |  |  | 		return objInfo, NotImplemented{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-07 03:03:57 +08:00
										 |  |  | 	if err := checkPutObjectArgs(ctx, bucket, object, fs); err != nil { | 
					
						
							| 
									
										
										
										
											2018-02-10 07:19:30 +08:00
										 |  |  | 		return ObjectInfo{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-28 01:06:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 23:25:48 +08:00
										 |  |  | 	defer NSUpdated(bucket, object) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | 	// Lock the object.
 | 
					
						
							| 
									
										
										
										
											2020-11-05 00:25:42 +08:00
										 |  |  | 	lk := fs.NewNSLock(bucket, object) | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 	lkctx, err := lk.GetLock(ctx, globalOperationTimeout) | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | 		return objInfo, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 	ctx = lkctx.Context() | 
					
						
							|  |  |  | 	defer lk.Unlock(lkctx.Cancel) | 
					
						
							| 
									
										
										
										
											2019-05-14 01:12:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-09 13:31:06 +08:00
										 |  |  | 	return fs.putObject(ctx, bucket, object, r, opts) | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // putObject - wrapper for PutObject
 | 
					
						
							| 
									
										
										
										
											2019-02-09 13:31:06 +08:00
										 |  |  | func (fs *FSObjects) putObject(ctx context.Context, bucket string, object string, r *PutObjReader, opts ObjectOptions) (objInfo ObjectInfo, retErr error) { | 
					
						
							| 
									
										
										
										
											2018-11-15 09:36:41 +08:00
										 |  |  | 	data := r.Reader | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	// No metadata is set, allocate a new one.
 | 
					
						
							| 
									
										
										
										
											2020-09-11 02:37:22 +08:00
										 |  |  | 	meta := cloneMSS(opts.UserDefined) | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	if _, err = fs.statBucketDir(ctx, bucket); err != nil { | 
					
						
							| 
									
										
										
										
											2017-05-26 00:22:43 +08:00
										 |  |  | 		return ObjectInfo{}, toObjectErr(err, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 	fsMeta := newFSMetaV1() | 
					
						
							| 
									
										
										
										
											2018-03-29 05:14:06 +08:00
										 |  |  | 	fsMeta.Meta = meta | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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()) { | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 		if err = mkdirAll(pathJoin(fs.fsPath, bucket, object), 0o777); err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 			return ObjectInfo{}, toObjectErr(err, bucket, object) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var fi os.FileInfo | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		if fi, err = fsStatDir(ctx, pathJoin(fs.fsPath, bucket, object)); err != nil { | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 			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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-07 00:38:01 +08:00
										 |  |  | 	// Validate input data size and it can never be less than zero.
 | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  | 	if data.Size() < -1 { | 
					
						
							| 
									
										
										
										
											2019-10-12 09:50:54 +08:00
										 |  |  | 		logger.LogIf(ctx, errInvalidArgument, logger.Application) | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		return ObjectInfo{}, 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) | 
					
						
							| 
									
										
										
										
											2018-03-29 05:14:06 +08:00
										 |  |  | 		fsMetaPath := pathJoin(bucketMetaDir, bucket, object, fs.metaJSONFile) | 
					
						
							| 
									
										
										
										
											2020-09-18 15:16:16 +08:00
										 |  |  | 		wlk, err = fs.rwPool.Write(fsMetaPath) | 
					
						
							|  |  |  | 		var freshFile bool | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-09-18 15:16:16 +08:00
										 |  |  | 			wlk, err = fs.rwPool.Create(fsMetaPath) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 				return ObjectInfo{}, toObjectErr(err, bucket, object) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			freshFile = true | 
					
						
							| 
									
										
										
										
											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() { | 
					
						
							| 
									
										
										
										
											2020-09-18 15:16:16 +08:00
										 |  |  | 			// Remove meta file when PutObject encounters
 | 
					
						
							|  |  |  | 			// any error and it is a fresh file.
 | 
					
						
							|  |  |  | 			//
 | 
					
						
							|  |  |  | 			// We should preserve the `fs.json` of any
 | 
					
						
							|  |  |  | 			// existing object
 | 
					
						
							|  |  |  | 			if retErr != nil && freshFile { | 
					
						
							| 
									
										
										
										
											2017-04-15 03:06:24 +08:00
										 |  |  | 				tmpDir := pathJoin(fs.fsPath, minioMetaTmpBucket, fs.fsUUID) | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 				fsRemoveMeta(ctx, bucketMetaDir, fsMetaPath, tmpDir) | 
					
						
							| 
									
										
										
										
											2017-04-15 03:06:24 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}() | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	fsTmpObjPath := pathJoin(fs.fsPath, minioMetaTmpBucket, fs.fsUUID, tempObj) | 
					
						
							| 
									
										
											  
											
												fix: use buffers only when necessary for io.Copy() (#11229)
Use separate sync.Pool for writes/reads
Avoid passing buffers for io.CopyBuffer()
if the writer or reader implement io.WriteTo or io.ReadFrom
respectively then its useless for sync.Pool to allocate
buffers on its own since that will be completely ignored
by the io.CopyBuffer Go implementation.
Improve this wherever we see this to be optimal.
This allows us to be more efficient on memory usage.
```
   385  // copyBuffer is the actual implementation of Copy and CopyBuffer.
   386  // if buf is nil, one is allocated.
   387  func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
   388  	// If the reader has a WriteTo method, use it to do the copy.
   389  	// Avoids an allocation and a copy.
   390  	if wt, ok := src.(WriterTo); ok {
   391  		return wt.WriteTo(dst)
   392  	}
   393  	// Similarly, if the writer has a ReadFrom method, use it to do the copy.
   394  	if rt, ok := dst.(ReaderFrom); ok {
   395  		return rt.ReadFrom(src)
   396  	}
```
From readahead package
```
// WriteTo writes data to w until there's no more data to write or when an error occurs.
// The return value n is the number of bytes written.
// Any error encountered during the write is also returned.
func (a *reader) WriteTo(w io.Writer) (n int64, err error) {
	if a.err != nil {
		return 0, a.err
	}
	n = 0
	for {
		err = a.fill()
		if err != nil {
			return n, err
		}
		n2, err := w.Write(a.cur.buffer())
		a.cur.inc(n2)
		n += int64(n2)
		if err != nil {
			return n, err
		}
```
											
										 
											2021-01-07 01:36:55 +08:00
										 |  |  | 	bytesWritten, err := fsCreateFile(ctx, fsTmpObjPath, data, data.Size()) | 
					
						
							| 
									
										
										
										
											2020-09-01 03:35:40 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Delete the temporary object in the case of a
 | 
					
						
							|  |  |  | 	// failure. If PutObject succeeds, then there would be
 | 
					
						
							|  |  |  | 	// nothing to delete.
 | 
					
						
							|  |  |  | 	defer fsRemoveFile(ctx, fsTmpObjPath) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-04 03:53:12 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return ObjectInfo{}, toObjectErr(err, bucket, object) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-15 09:36:41 +08:00
										 |  |  | 	fsMeta.Meta["etag"] = r.MD5CurrentHexString() | 
					
						
							| 
									
										
										
										
											2017-10-25 03:25:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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() { | 
					
						
							| 
									
										
										
										
											2020-09-09 05:22:04 +08:00
										 |  |  | 		return ObjectInfo{}, IncompleteBody{Bucket: bucket, Object: object} | 
					
						
							| 
									
										
										
										
											2016-05-14 02:52:36 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-04 03:53:12 +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) | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	if err = fsRenameFile(ctx, fsTmpObjPath, fsNSObjPath); err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		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.
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	fi, err := fsStatFile(ctx, pathJoin(fs.fsPath, bucket, object)) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | // DeleteObjects - deletes an object from a bucket, this operation is destructive
 | 
					
						
							|  |  |  | // and there are no rollbacks supported.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | func (fs *FSObjects) DeleteObjects(ctx context.Context, bucket string, objects []ObjectToDelete, opts ObjectOptions) ([]DeletedObject, []error) { | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | 	errs := make([]error, len(objects)) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	dobjects := make([]DeletedObject, len(objects)) | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | 	for idx, object := range objects { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		if object.VersionID != "" { | 
					
						
							| 
									
										
										
										
											2020-06-19 01:25:07 +08:00
										 |  |  | 			errs[idx] = VersionNotFound{ | 
					
						
							|  |  |  | 				Bucket:    bucket, | 
					
						
							|  |  |  | 				Object:    object.ObjectName, | 
					
						
							|  |  |  | 				VersionID: object.VersionID, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		_, errs[idx] = fs.DeleteObject(ctx, bucket, object.ObjectName, opts) | 
					
						
							|  |  |  | 		if errs[idx] == nil || isErrObjectNotFound(errs[idx]) { | 
					
						
							|  |  |  | 			dobjects[idx] = DeletedObject{ | 
					
						
							|  |  |  | 				ObjectName: object.ObjectName, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			errs[idx] = nil | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	return dobjects, errs | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +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.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | func (fs *FSObjects) DeleteObject(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error) { | 
					
						
							|  |  |  | 	if opts.VersionID != "" && opts.VersionID != nullVersionID { | 
					
						
							|  |  |  | 		return objInfo, VersionNotFound{ | 
					
						
							|  |  |  | 			Bucket:    bucket, | 
					
						
							|  |  |  | 			Object:    object, | 
					
						
							|  |  |  | 			VersionID: opts.VersionID, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 23:25:48 +08:00
										 |  |  | 	defer NSUpdated(bucket, object) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | 	// Acquire a write lock before deleting the object.
 | 
					
						
							| 
									
										
										
										
											2020-11-05 00:25:42 +08:00
										 |  |  | 	lk := fs.NewNSLock(bucket, object) | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 	lkctx, err := lk.GetLock(ctx, globalOperationTimeout) | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		return objInfo, err | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-30 11:55:21 +08:00
										 |  |  | 	ctx = lkctx.Context() | 
					
						
							|  |  |  | 	defer lk.Unlock(lkctx.Cancel) | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if err = checkDelObjArgs(ctx, bucket, object); err != nil { | 
					
						
							|  |  |  | 		return objInfo, err | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-11 01:20:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if _, err = fs.statBucketDir(ctx, bucket); err != nil { | 
					
						
							|  |  |  | 		return objInfo, toObjectErr(err, bucket) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-20 23:52:24 +08:00
										 |  |  | 	var rwlk *lock.LockedFile | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	minioMetaBucketDir := pathJoin(fs.fsPath, minioMetaBucket) | 
					
						
							| 
									
										
										
										
											2018-03-29 05:14:06 +08:00
										 |  |  | 	fsMetaPath := pathJoin(minioMetaBucketDir, bucketMetaPrefix, bucket, object, fs.metaJSONFile) | 
					
						
							| 
									
										
										
										
											2016-11-28 03:33:08 +08:00
										 |  |  | 	if bucket != minioMetaBucket { | 
					
						
							| 
									
										
										
										
											2020-07-20 23:52:24 +08:00
										 |  |  | 		rwlk, err = fs.rwPool.Write(fsMetaPath) | 
					
						
							|  |  |  | 		if err != nil && err != errFileNotFound { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 			return objInfo, toObjectErr(err, 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.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if err = fsDeleteFile(ctx, pathJoin(fs.fsPath, bucket), pathJoin(fs.fsPath, bucket, object)); err != nil { | 
					
						
							| 
									
										
										
										
											2020-07-20 23:52:24 +08:00
										 |  |  | 		if rwlk != nil { | 
					
						
							|  |  |  | 			rwlk.Close() | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		return objInfo, toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-20 23:52:24 +08:00
										 |  |  | 	// Close fsMetaPath before deletion
 | 
					
						
							|  |  |  | 	if rwlk != nil { | 
					
						
							|  |  |  | 		rwlk.Close() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	if bucket != minioMetaBucket { | 
					
						
							|  |  |  | 		// Delete the metadata object.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		err = fsDeleteFile(ctx, minioMetaBucketDir, fsMetaPath) | 
					
						
							| 
									
										
										
										
											2018-04-11 00:36:37 +08:00
										 |  |  | 		if err != nil && err != errFileNotFound { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			return objInfo, toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	return ObjectInfo{Bucket: bucket, Name: object}, nil | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 03:26:48 +08:00
										 |  |  | func (fs *FSObjects) isLeafDir(bucket string, leafPath string) bool { | 
					
						
							|  |  |  | 	return fs.isObjectDir(bucket, leafPath) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (fs *FSObjects) isLeaf(bucket string, leafPath string) bool { | 
					
						
							|  |  |  | 	return !strings.HasSuffix(leafPath, slashSeparator) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | // 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.
 | 
					
						
							| 
									
										
										
										
											2019-05-02 13:06:57 +08:00
										 |  |  | func (fs *FSObjects) listDirFactory() ListDirFunc { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// listDir - lists all the entries at a given prefix and given entry in the prefix.
 | 
					
						
							| 
									
										
										
										
											2020-08-26 03:26:48 +08:00
										 |  |  | 	listDir := func(bucket, prefixDir, prefixEntry string) (emptyDir bool, entries []string, delayIsLeaf bool) { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		var err error | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		entries, err = readDir(pathJoin(fs.fsPath, bucket, prefixDir)) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		if err != nil && err != errFileNotFound { | 
					
						
							| 
									
										
										
										
											2020-04-10 00:30:02 +08:00
										 |  |  | 			logger.LogIf(GlobalContext, err) | 
					
						
							| 
									
										
										
										
											2020-08-26 03:26:48 +08:00
										 |  |  | 			return false, nil, false | 
					
						
							| 
									
										
										
										
											2020-03-14 08:43:00 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if len(entries) == 0 { | 
					
						
							| 
									
										
										
										
											2020-08-26 03:26:48 +08:00
										 |  |  | 			return true, nil, false | 
					
						
							| 
									
										
										
										
											2017-01-27 07:39:22 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-26 03:26:48 +08:00
										 |  |  | 		entries, delayIsLeaf = filterListEntries(bucket, prefixDir, entries, prefixEntry, fs.isLeaf) | 
					
						
							|  |  |  | 		return false, entries, delayIsLeaf | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Return list factory instance.
 | 
					
						
							|  |  |  | 	return listDir | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-09 10:08:21 +08:00
										 |  |  | // isObjectDir returns true if the specified bucket & prefix exists
 | 
					
						
							|  |  |  | // and the prefix represents an empty directory. An S3 empty directory
 | 
					
						
							|  |  |  | // is also an empty directory in the FS backend.
 | 
					
						
							|  |  |  | func (fs *FSObjects) isObjectDir(bucket, prefix string) bool { | 
					
						
							|  |  |  | 	entries, err := readDirN(pathJoin(fs.fsPath, bucket, prefix), 1) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return len(entries) == 0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // ListObjectVersions not implemented for FS mode.
 | 
					
						
							|  |  |  | func (fs *FSObjects) ListObjectVersions(ctx context.Context, bucket, prefix, marker, versionMarker, delimiter string, maxKeys int) (loi ListObjectVersionsInfo, e error) { | 
					
						
							|  |  |  | 	return loi, NotImplemented{} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2022-02-02 02:00:14 +08:00
										 |  |  | func (fs *FSObjects) ListObjects(ctx context.Context, bucket, prefix, marker, delimiter string, maxKeys int) (loi ListObjectsInfo, err error) { | 
					
						
							|  |  |  | 	// listObjects may in rare cases not be able to find any valid results.
 | 
					
						
							|  |  |  | 	// Therefore, it cannot set a NextMarker.
 | 
					
						
							|  |  |  | 	// In that case we retry the operation, but we add a
 | 
					
						
							|  |  |  | 	// max limit, so we never end up in an infinite loop.
 | 
					
						
							|  |  |  | 	tries := 50 | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		loi, err = listObjects(ctx, fs, bucket, prefix, marker, delimiter, maxKeys, fs.listPool, | 
					
						
							|  |  |  | 			fs.listDirFactory(), fs.isLeaf, fs.isLeafDir, fs.getObjectInfoNoFSLock, fs.getObjectInfoNoFSLock) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return loi, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !loi.IsTruncated || loi.NextMarker != "" || tries == 0 { | 
					
						
							|  |  |  | 			return loi, nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		tries-- | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-24 02:09:35 +08:00
										 |  |  | // GetObjectTags - get object tags from an existing object
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | func (fs *FSObjects) GetObjectTags(ctx context.Context, bucket, object string, opts ObjectOptions) (*tags.Tags, error) { | 
					
						
							|  |  |  | 	if opts.VersionID != "" && opts.VersionID != nullVersionID { | 
					
						
							|  |  |  | 		return nil, VersionNotFound{ | 
					
						
							|  |  |  | 			Bucket:    bucket, | 
					
						
							|  |  |  | 			Object:    object, | 
					
						
							|  |  |  | 			VersionID: opts.VersionID, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  | 	oi, err := fs.GetObjectInfo(ctx, bucket, object, ObjectOptions{}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-05-06 05:18:13 +08:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-06 05:18:13 +08:00
										 |  |  | 	return tags.ParseObjectTags(oi.UserTags) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-24 02:09:35 +08:00
										 |  |  | // PutObjectTags - replace or add tags to an existing object
 | 
					
						
							| 
									
										
										
										
											2021-02-02 05:52:51 +08:00
										 |  |  | func (fs *FSObjects) PutObjectTags(ctx context.Context, bucket, object string, tags string, opts ObjectOptions) (ObjectInfo, error) { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if opts.VersionID != "" && opts.VersionID != nullVersionID { | 
					
						
							| 
									
										
										
										
											2021-02-02 05:52:51 +08:00
										 |  |  | 		return ObjectInfo{}, VersionNotFound{ | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			Bucket:    bucket, | 
					
						
							|  |  |  | 			Object:    object, | 
					
						
							|  |  |  | 			VersionID: opts.VersionID, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  | 	fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, object, fs.metaJSONFile) | 
					
						
							|  |  |  | 	fsMeta := fsMetaV1{} | 
					
						
							|  |  |  | 	wlk, err := fs.rwPool.Write(fsMetaPath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-09-16 00:18:41 +08:00
										 |  |  | 		wlk, err = fs.rwPool.Create(fsMetaPath) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2021-02-02 05:52:51 +08:00
										 |  |  | 			return ObjectInfo{}, toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2020-09-16 00:18:41 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// This close will allow for locks to be synchronized on `fs.json`.
 | 
					
						
							|  |  |  | 	defer wlk.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Read objects' metadata in `fs.json`.
 | 
					
						
							|  |  |  | 	if _, err = fsMeta.ReadFrom(ctx, wlk); err != nil { | 
					
						
							|  |  |  | 		// For any error to read fsMeta, set default ETag and proceed.
 | 
					
						
							|  |  |  | 		fsMeta = fs.defaultFsJSON(object) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// clean fsMeta.Meta of tag key, before updating the new tags
 | 
					
						
							|  |  |  | 	delete(fsMeta.Meta, xhttp.AmzObjectTagging) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Do not update for empty tags
 | 
					
						
							|  |  |  | 	if tags != "" { | 
					
						
							|  |  |  | 		fsMeta.Meta[xhttp.AmzObjectTagging] = tags | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, err = fsMeta.WriteTo(wlk); err != nil { | 
					
						
							| 
									
										
										
										
											2021-02-02 05:52:51 +08:00
										 |  |  | 		return ObjectInfo{}, toObjectErr(err, bucket, object) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-02-02 05:52:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Stat the file to get file size.
 | 
					
						
							|  |  |  | 	fi, err := fsStatFile(ctx, pathJoin(fs.fsPath, bucket, object)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return ObjectInfo{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fsMeta.ToObjectInfo(bucket, object, fi), nil | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-24 02:09:35 +08:00
										 |  |  | // DeleteObjectTags - delete object tags from an existing object
 | 
					
						
							| 
									
										
										
										
											2021-02-02 05:52:51 +08:00
										 |  |  | func (fs *FSObjects) DeleteObjectTags(ctx context.Context, bucket, object string, opts ObjectOptions) (ObjectInfo, error) { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	return fs.PutObjectTags(ctx, bucket, object, "", opts) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // HealFormat - no-op for fs, Valid only for Erasure.
 | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  | func (fs *FSObjects) HealFormat(ctx context.Context, dryRun bool) (madmin.HealResultItem, error) { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	return madmin.HealResultItem{}, NotImplemented{} | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // HealObject - no-op for fs. Valid only for Erasure.
 | 
					
						
							|  |  |  | func (fs *FSObjects) HealObject(ctx context.Context, bucket, object, versionID string, opts madmin.HealOpts) ( | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	res madmin.HealResultItem, err error) { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	return res, NotImplemented{} | 
					
						
							| 
									
										
										
										
											2016-08-18 02:36:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // HealBucket - no-op for fs, Valid only for Erasure.
 | 
					
						
							| 
									
										
										
										
											2020-12-14 03:57:08 +08:00
										 |  |  | func (fs *FSObjects) HealBucket(ctx context.Context, bucket string, opts madmin.HealOpts) (madmin.HealResultItem, | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	error) { | 
					
						
							| 
									
										
										
										
											2019-02-11 11:53:13 +08:00
										 |  |  | 	return madmin.HealResultItem{}, NotImplemented{} | 
					
						
							| 
									
										
										
										
											2016-10-15 10:57:40 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-25 23:52:28 +08:00
										 |  |  | // Walk a bucket, optionally prefix recursively, until we have returned
 | 
					
						
							|  |  |  | // all the content to objectInfo channel, it is callers responsibility
 | 
					
						
							|  |  |  | // to allocate a receive channel for ObjectInfo, upon any unhandled
 | 
					
						
							|  |  |  | // error walker returns error. Optionally if context.Done() is received
 | 
					
						
							|  |  |  | // then Walk() stops the walker.
 | 
					
						
							| 
									
										
										
										
											2020-07-11 13:21:04 +08:00
										 |  |  | func (fs *FSObjects) Walk(ctx context.Context, bucket, prefix string, results chan<- ObjectInfo, opts ObjectOptions) error { | 
					
						
							| 
									
										
										
										
											2020-08-26 03:26:48 +08:00
										 |  |  | 	return fsWalk(ctx, fs, bucket, prefix, fs.listDirFactory(), fs.isLeaf, fs.isLeafDir, results, fs.getObjectInfoNoFSLock, fs.getObjectInfoNoFSLock) | 
					
						
							| 
									
										
										
										
											2020-02-25 23:52:28 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // HealObjects - no-op for fs. Valid only for Erasure.
 | 
					
						
							|  |  |  | func (fs *FSObjects) HealObjects(ctx context.Context, bucket, prefix string, opts madmin.HealOpts, fn HealObjectFn) (e error) { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	logger.LogIf(ctx, NotImplemented{}) | 
					
						
							| 
									
										
										
										
											2019-03-14 08:35:09 +08:00
										 |  |  | 	return NotImplemented{} | 
					
						
							| 
									
										
										
										
											2016-08-18 02:36:33 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-01-20 01:34:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-06 15:16:06 +08:00
										 |  |  | // GetMetrics - no op
 | 
					
						
							| 
									
										
										
										
											2021-01-19 12:35:38 +08:00
										 |  |  | func (fs *FSObjects) GetMetrics(ctx context.Context) (*BackendMetrics, error) { | 
					
						
							| 
									
										
										
										
											2019-12-06 15:16:06 +08:00
										 |  |  | 	logger.LogIf(ctx, NotImplemented{}) | 
					
						
							| 
									
										
										
										
											2021-01-19 12:35:38 +08:00
										 |  |  | 	return &BackendMetrics{}, NotImplemented{} | 
					
						
							| 
									
										
										
										
											2019-12-06 15:16:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-10 07:19:30 +08:00
										 |  |  | // ListObjectsV2 lists all blobs in bucket filtered by prefix
 | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  | func (fs *FSObjects) ListObjectsV2(ctx context.Context, bucket, prefix, continuationToken, delimiter string, maxKeys int, fetchOwner bool, startAfter string) (result ListObjectsV2Info, err error) { | 
					
						
							| 
									
										
										
										
											2018-07-01 12:22:45 +08:00
										 |  |  | 	marker := continuationToken | 
					
						
							|  |  |  | 	if marker == "" { | 
					
						
							|  |  |  | 		marker = startAfter | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	loi, err := fs.ListObjects(ctx, bucket, prefix, marker, delimiter, maxKeys) | 
					
						
							| 
									
										
										
										
											2018-02-10 07:19:30 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return result, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	listObjectsV2Info := ListObjectsV2Info{ | 
					
						
							|  |  |  | 		IsTruncated:           loi.IsTruncated, | 
					
						
							|  |  |  | 		ContinuationToken:     continuationToken, | 
					
						
							|  |  |  | 		NextContinuationToken: loi.NextMarker, | 
					
						
							|  |  |  | 		Objects:               loi.Objects, | 
					
						
							|  |  |  | 		Prefixes:              loi.Prefixes, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return listObjectsV2Info, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsNotificationSupported returns whether bucket notification is applicable for this layer.
 | 
					
						
							| 
									
										
										
										
											2018-02-21 04:21:12 +08:00
										 |  |  | func (fs *FSObjects) IsNotificationSupported() bool { | 
					
						
							| 
									
										
										
										
											2018-02-10 07:19:30 +08:00
										 |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-21 03:52:49 +08:00
										 |  |  | // IsListenSupported returns whether listen bucket notification is applicable for this layer.
 | 
					
						
							|  |  |  | func (fs *FSObjects) IsListenSupported() bool { | 
					
						
							| 
									
										
										
										
											2018-12-06 06:03:42 +08:00
										 |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  | // IsEncryptionSupported returns whether server side encryption is implemented for this layer.
 | 
					
						
							| 
									
										
										
										
											2018-02-21 04:21:12 +08:00
										 |  |  | func (fs *FSObjects) IsEncryptionSupported() bool { | 
					
						
							| 
									
										
										
										
											2018-02-10 07:19:30 +08:00
										 |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // IsCompressionSupported returns whether compression is applicable for this layer.
 | 
					
						
							|  |  |  | func (fs *FSObjects) IsCompressionSupported() bool { | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-12-29 00:54:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-24 02:09:35 +08:00
										 |  |  | // IsTaggingSupported returns true, object tagging is supported in fs object layer.
 | 
					
						
							|  |  |  | func (fs *FSObjects) IsTaggingSupported() bool { | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-21 09:31:22 +08:00
										 |  |  | // Health returns health of the object layer
 | 
					
						
							|  |  |  | func (fs *FSObjects) Health(ctx context.Context, opts HealthOptions) HealthResult { | 
					
						
							| 
									
										
										
										
											2020-04-08 23:53:20 +08:00
										 |  |  | 	if _, err := os.Stat(fs.fsPath); err != nil { | 
					
						
							| 
									
										
										
										
											2020-07-21 09:31:22 +08:00
										 |  |  | 		return HealthResult{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return HealthResult{ | 
					
						
							|  |  |  | 		Healthy: newObjectLayerFn() != nil, | 
					
						
							| 
									
										
										
										
											2020-04-08 23:53:20 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-12-29 00:54:43 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-09 17:00:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // ReadHealth returns "read" health of the object layer
 | 
					
						
							|  |  |  | func (fs *FSObjects) ReadHealth(ctx context.Context) bool { | 
					
						
							|  |  |  | 	_, err := os.Stat(fs.fsPath) | 
					
						
							|  |  |  | 	return err == nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // TransitionObject - transition object content to target tier.
 | 
					
						
							|  |  |  | func (fs *FSObjects) TransitionObject(ctx context.Context, bucket, object string, opts ObjectOptions) error { | 
					
						
							|  |  |  | 	return NotImplemented{} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RestoreTransitionedObject - restore transitioned object content locally on this cluster.
 | 
					
						
							|  |  |  | func (fs *FSObjects) RestoreTransitionedObject(ctx context.Context, bucket, object string, opts ObjectOptions) error { | 
					
						
							|  |  |  | 	return NotImplemented{} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // GetRawData returns raw file data to the callback.
 | 
					
						
							|  |  |  | // Errors are ignored, only errors from the callback are returned.
 | 
					
						
							|  |  |  | // For now only direct file paths are supported.
 | 
					
						
							| 
									
										
										
										
											2021-10-02 02:50:00 +08:00
										 |  |  | func (fs *FSObjects) GetRawData(ctx context.Context, volume, file string, fn func(r io.Reader, host string, disk string, filename string, size int64, modtime time.Time, isDir bool) error) error { | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 	f, err := os.Open(filepath.Join(fs.fsPath, volume, file)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer f.Close() | 
					
						
							|  |  |  | 	st, err := f.Stat() | 
					
						
							|  |  |  | 	if err != nil || st.IsDir() { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-10-02 02:50:00 +08:00
										 |  |  | 	return fn(f, "fs", fs.fsUUID, file, st.Size(), st.ModTime(), st.IsDir()) | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | } |