| 
									
										
										
										
											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-07-22 08:31:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2018-03-28 08:23:10 +08:00
										 |  |  | 	"encoding/hex" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	"io" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2019-03-01 03:01:25 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	"os" | 
					
						
							|  |  |  | 	pathutil "path" | 
					
						
							| 
									
										
										
										
											2019-03-01 03:01:25 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-06 06:51:27 +08:00
										 |  |  | 	jsoniter "github.com/json-iterator/go" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	xhttp "github.com/minio/minio/internal/http" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/lock" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							| 
									
										
										
										
											2021-05-29 06:17:01 +08:00
										 |  |  | 	"github.com/minio/pkg/mimedb" | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | // FS format, and object metadata.
 | 
					
						
							| 
									
										
										
										
											2016-05-25 12:24:20 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 	// fs.json object metadata.
 | 
					
						
							|  |  |  | 	fsMetaJSONFile = "fs.json" | 
					
						
							| 
									
										
										
										
											2016-05-25 12:24:20 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | // FS metadata constants.
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	// FS backend meta 1.0.0 version.
 | 
					
						
							|  |  |  | 	fsMetaVersion100 = "1.0.0" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// FS backend meta 1.0.1 version.
 | 
					
						
							| 
									
										
										
										
											2018-03-28 08:23:10 +08:00
										 |  |  | 	fsMetaVersion101 = "1.0.1" | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-28 08:23:10 +08:00
										 |  |  | 	// FS backend meta 1.0.2
 | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  | 	// Removed the fields "Format" and "MinIO" from fsMetaV1 as they were unused. Added "Checksum" field - to be used in future for bit-rot protection.
 | 
					
						
							| 
									
										
										
										
											2018-03-28 08:23:10 +08:00
										 |  |  | 	fsMetaVersion = "1.0.2" | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Add more constants here.
 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-28 08:23:10 +08:00
										 |  |  | // FSChecksumInfoV1 - carries checksums of individual blocks on disk.
 | 
					
						
							|  |  |  | type FSChecksumInfoV1 struct { | 
					
						
							|  |  |  | 	Algorithm string | 
					
						
							|  |  |  | 	Blocksize int64 | 
					
						
							|  |  |  | 	Hashes    [][]byte | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MarshalJSON marshals the FSChecksumInfoV1 struct
 | 
					
						
							|  |  |  | func (c FSChecksumInfoV1) MarshalJSON() ([]byte, error) { | 
					
						
							|  |  |  | 	type checksuminfo struct { | 
					
						
							|  |  |  | 		Algorithm string   `json:"algorithm"` | 
					
						
							|  |  |  | 		Blocksize int64    `json:"blocksize"` | 
					
						
							|  |  |  | 		Hashes    []string `json:"hashes"` | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var hashes []string | 
					
						
							|  |  |  | 	for _, h := range c.Hashes { | 
					
						
							|  |  |  | 		hashes = append(hashes, hex.EncodeToString(h)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	info := checksuminfo{ | 
					
						
							|  |  |  | 		Algorithm: c.Algorithm, | 
					
						
							|  |  |  | 		Hashes:    hashes, | 
					
						
							|  |  |  | 		Blocksize: c.Blocksize, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return json.Marshal(info) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UnmarshalJSON unmarshals the the given data into the FSChecksumInfoV1 struct
 | 
					
						
							|  |  |  | func (c *FSChecksumInfoV1) UnmarshalJSON(data []byte) error { | 
					
						
							|  |  |  | 	type checksuminfo struct { | 
					
						
							|  |  |  | 		Algorithm string   `json:"algorithm"` | 
					
						
							|  |  |  | 		Blocksize int64    `json:"blocksize"` | 
					
						
							|  |  |  | 		Hashes    []string `json:"hashes"` | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var info checksuminfo | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 	json := jsoniter.ConfigCompatibleWithStandardLibrary | 
					
						
							| 
									
										
										
										
											2018-03-28 08:23:10 +08:00
										 |  |  | 	err := json.Unmarshal(data, &info) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	c.Algorithm = info.Algorithm | 
					
						
							|  |  |  | 	c.Blocksize = info.Blocksize | 
					
						
							|  |  |  | 	var hashes [][]byte | 
					
						
							|  |  |  | 	for _, hashStr := range info.Hashes { | 
					
						
							|  |  |  | 		h, err := hex.DecodeString(hashStr) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		hashes = append(hashes, h) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	c.Hashes = hashes | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | // A fsMetaV1 represents a metadata header mapping keys to sets of values.
 | 
					
						
							|  |  |  | type fsMetaV1 struct { | 
					
						
							|  |  |  | 	Version string `json:"version"` | 
					
						
							| 
									
										
										
										
											2018-03-28 08:23:10 +08:00
										 |  |  | 	// checksums of blocks on disk.
 | 
					
						
							|  |  |  | 	Checksum FSChecksumInfoV1 `json:"checksum,omitempty"` | 
					
						
							|  |  |  | 	// Metadata map for current object.
 | 
					
						
							|  |  |  | 	Meta map[string]string `json:"meta,omitempty"` | 
					
						
							|  |  |  | 	// parts info for current object - used in encryption.
 | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  | 	Parts []ObjectPartInfo `json:"parts,omitempty"` | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | // IsValid - tells if the format is sane by validating the version
 | 
					
						
							|  |  |  | // string and format style.
 | 
					
						
							|  |  |  | func (m fsMetaV1) IsValid() bool { | 
					
						
							| 
									
										
										
										
											2018-03-28 08:23:10 +08:00
										 |  |  | 	return isFSMetaValid(m.Version) | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-28 08:23:10 +08:00
										 |  |  | // Verifies if the backend format metadata is same by validating
 | 
					
						
							|  |  |  | // the version string.
 | 
					
						
							|  |  |  | func isFSMetaValid(version string) bool { | 
					
						
							|  |  |  | 	return (version == fsMetaVersion || version == fsMetaVersion100 || version == fsMetaVersion101) | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | // Converts metadata to object info.
 | 
					
						
							|  |  |  | func (m fsMetaV1) ToObjectInfo(bucket, object string, fi os.FileInfo) ObjectInfo { | 
					
						
							|  |  |  | 	if len(m.Meta) == 0 { | 
					
						
							|  |  |  | 		m.Meta = make(map[string]string) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Guess content-type from the extension if possible.
 | 
					
						
							|  |  |  | 	if m.Meta["content-type"] == "" { | 
					
						
							| 
									
										
										
										
											2018-10-02 14:18:17 +08:00
										 |  |  | 		m.Meta["content-type"] = mimedb.TypeByExtension(pathutil.Ext(object)) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-06 15:16:06 +08:00
										 |  |  | 	if HasSuffix(object, SlashSeparator) { | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 		m.Meta["etag"] = emptyETag // For directories etag is d41d8cd98f00b204e9800998ecf8427e
 | 
					
						
							|  |  |  | 		m.Meta["content-type"] = "application/octet-stream" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	objInfo := ObjectInfo{ | 
					
						
							|  |  |  | 		Bucket: bucket, | 
					
						
							|  |  |  | 		Name:   object, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 	// We set file info only if its valid.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	objInfo.ModTime = timeSentinel | 
					
						
							|  |  |  | 	if fi != nil { | 
					
						
							|  |  |  | 		objInfo.ModTime = fi.ModTime() | 
					
						
							|  |  |  | 		objInfo.Size = fi.Size() | 
					
						
							| 
									
										
										
										
											2017-10-17 08:20:54 +08:00
										 |  |  | 		if fi.IsDir() { | 
					
						
							|  |  |  | 			// Directory is always 0 bytes in S3 API, treat it as such.
 | 
					
						
							|  |  |  | 			objInfo.Size = 0 | 
					
						
							|  |  |  | 			objInfo.IsDir = fi.IsDir() | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 	objInfo.ETag = extractETag(m.Meta) | 
					
						
							| 
									
										
										
										
											2021-07-29 13:11:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	objInfo.ContentType = m.Meta["content-type"] | 
					
						
							|  |  |  | 	objInfo.ContentEncoding = m.Meta["content-encoding"] | 
					
						
							| 
									
										
										
										
											2019-10-07 13:50:24 +08:00
										 |  |  | 	if storageClass, ok := m.Meta[xhttp.AmzStorageClass]; ok { | 
					
						
							| 
									
										
										
										
											2018-04-06 23:20:02 +08:00
										 |  |  | 		objInfo.StorageClass = storageClass | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		objInfo.StorageClass = globalMinioDefaultStorageClass | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-01 03:01:25 +08:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		t time.Time | 
					
						
							| 
									
										
										
										
											2021-09-09 13:25:23 +08:00
										 |  |  | 		e error | 
					
						
							| 
									
										
										
										
											2019-03-01 03:01:25 +08:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	if exp, ok := m.Meta["expires"]; ok { | 
					
						
							|  |  |  | 		if t, e = time.Parse(http.TimeFormat, exp); e == nil { | 
					
						
							|  |  |  | 			objInfo.Expires = t.UTC() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Add user tags to the object info
 | 
					
						
							|  |  |  | 	objInfo.UserTags = m.Meta[xhttp.AmzObjectTagging] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 	// etag/md5Sum has already been extracted. We need to
 | 
					
						
							|  |  |  | 	// remove to avoid it from appearing as part of
 | 
					
						
							|  |  |  | 	// response headers. e.g, X-Minio-* or X-Amz-*.
 | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  | 	// Tags have also been extracted, we remove that as well.
 | 
					
						
							| 
									
										
										
										
											2018-01-04 14:14:45 +08:00
										 |  |  | 	objInfo.UserDefined = cleanMetadata(m.Meta) | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	// All the parts per object.
 | 
					
						
							|  |  |  | 	objInfo.Parts = m.Parts | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Success..
 | 
					
						
							|  |  |  | 	return objInfo | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-26 04:29:06 +08:00
										 |  |  | func (m *fsMetaV1) WriteTo(lk *lock.LockedFile) (n int64, err error) { | 
					
						
							| 
									
										
										
										
											2018-02-07 07:37:48 +08:00
										 |  |  | 	if err = jsonSave(lk, m); err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-07 07:37:48 +08:00
										 |  |  | 	fi, err := lk.Stat() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-07 07:37:48 +08:00
										 |  |  | 	return fi.Size(), nil | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | func (m *fsMetaV1) ReadFrom(ctx context.Context, lk *lock.LockedFile) (n int64, err error) { | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 	var fsMetaBuf []byte | 
					
						
							| 
									
										
										
										
											2017-01-26 04:29:06 +08:00
										 |  |  | 	fi, err := lk.Stat() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							| 
									
										
										
										
											2017-01-26 04:29:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 	fsMetaBuf, err = ioutil.ReadAll(io.NewSectionReader(lk, 0, fi.Size())) | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 	if len(fsMetaBuf) == 0 { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		return 0, io.EOF | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 	json := jsoniter.ConfigCompatibleWithStandardLibrary | 
					
						
							| 
									
										
										
										
											2019-09-06 06:51:27 +08:00
										 |  |  | 	if err = json.Unmarshal(fsMetaBuf, m); err != nil { | 
					
						
							| 
									
										
										
										
											2019-08-20 02:35:52 +08:00
										 |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 	// Verify if the format is valid, return corrupted format
 | 
					
						
							|  |  |  | 	// for unrecognized formats.
 | 
					
						
							| 
									
										
										
										
											2018-03-28 08:23:10 +08:00
										 |  |  | 	if !isFSMetaValid(m.Version) { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		logger.GetReqInfo(ctx).AppendTags("file", lk.Name()) | 
					
						
							|  |  |  | 		logger.LogIf(ctx, errCorruptedFormat) | 
					
						
							|  |  |  | 		return 0, errCorruptedFormat | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Success.
 | 
					
						
							| 
									
										
										
										
											2017-04-05 00:14:03 +08:00
										 |  |  | 	return int64(len(fsMetaBuf)), nil | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | // newFSMetaV1 - initializes new fsMetaV1.
 | 
					
						
							|  |  |  | func newFSMetaV1() (fsMeta fsMetaV1) { | 
					
						
							|  |  |  | 	fsMeta = fsMetaV1{} | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | 	fsMeta.Version = fsMetaVersion | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | 	return fsMeta | 
					
						
							|  |  |  | } |