| 
									
										
										
										
											2016-07-22 08:31:14 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  |  * Minio Cloud Storage, (C) 2016, 2017, 2017 Minio, Inc. | 
					
						
							| 
									
										
										
										
											2016-07-22 08:31:14 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	"io" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	pathutil "path" | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/minio/minio/pkg/lock" | 
					
						
							|  |  |  | 	"github.com/minio/minio/pkg/mimedb" | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-25 12:24:20 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2016-06-14 16:39:40 +08:00
										 |  |  | 	fsMetaJSONFile   = "fs.json" | 
					
						
							|  |  |  | 	fsFormatJSONFile = "format.json" | 
					
						
							| 
									
										
										
										
											2016-05-25 12:24:20 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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"` | 
					
						
							|  |  |  | 	Format  string `json:"format"` | 
					
						
							|  |  |  | 	Minio   struct { | 
					
						
							|  |  |  | 		Release string `json:"release"` | 
					
						
							|  |  |  | 	} `json:"minio"` | 
					
						
							| 
									
										
										
										
											2016-07-22 08:31:14 +08:00
										 |  |  | 	// Metadata map for current object `fs.json`.
 | 
					
						
							|  |  |  | 	Meta  map[string]string `json:"meta,omitempty"` | 
					
						
							|  |  |  | 	Parts []objectPartInfo  `json:"parts,omitempty"` | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +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"] == "" { | 
					
						
							|  |  |  | 		if objectExt := pathutil.Ext(object); objectExt != "" { | 
					
						
							|  |  |  | 			if content, ok := mimedb.DB[strings.ToLower(strings.TrimPrefix(objectExt, "."))]; ok { | 
					
						
							|  |  |  | 				m.Meta["content-type"] = content.ContentType | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	objInfo := ObjectInfo{ | 
					
						
							|  |  |  | 		Bucket: bucket, | 
					
						
							|  |  |  | 		Name:   object, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// We set file into only if its valid.
 | 
					
						
							|  |  |  | 	objInfo.ModTime = timeSentinel | 
					
						
							|  |  |  | 	if fi != nil { | 
					
						
							|  |  |  | 		objInfo.ModTime = fi.ModTime() | 
					
						
							|  |  |  | 		objInfo.Size = fi.Size() | 
					
						
							|  |  |  | 		objInfo.IsDir = fi.IsDir() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	objInfo.MD5Sum = m.Meta["md5Sum"] | 
					
						
							|  |  |  | 	objInfo.ContentType = m.Meta["content-type"] | 
					
						
							|  |  |  | 	objInfo.ContentEncoding = m.Meta["content-encoding"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// md5Sum has already been extracted into objInfo.MD5Sum.  We
 | 
					
						
							|  |  |  | 	// need to remove it from m.Meta to avoid it from appearing as
 | 
					
						
							|  |  |  | 	// part of response headers. e.g, X-Minio-* or X-Amz-*.
 | 
					
						
							|  |  |  | 	delete(m.Meta, "md5Sum") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Save all the other userdefined API.
 | 
					
						
							|  |  |  | 	objInfo.UserDefined = m.Meta | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Success..
 | 
					
						
							|  |  |  | 	return objInfo | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | // ObjectPartIndex - returns the index of matching object part number.
 | 
					
						
							|  |  |  | func (m fsMetaV1) ObjectPartIndex(partNumber int) (partIndex int) { | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	for i, part := range m.Parts { | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | 		if partNumber == part.Number { | 
					
						
							|  |  |  | 			partIndex = i | 
					
						
							|  |  |  | 			return partIndex | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return -1 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AddObjectPart - add a new object part in order.
 | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | func (m *fsMetaV1) AddObjectPart(partNumber int, partName string, partETag string, partSize int64) { | 
					
						
							|  |  |  | 	partInfo := objectPartInfo{ | 
					
						
							|  |  |  | 		Number: partNumber, | 
					
						
							|  |  |  | 		Name:   partName, | 
					
						
							|  |  |  | 		ETag:   partETag, | 
					
						
							|  |  |  | 		Size:   partSize, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Update part info if it already exists.
 | 
					
						
							|  |  |  | 	for i, part := range m.Parts { | 
					
						
							|  |  |  | 		if partNumber == part.Number { | 
					
						
							|  |  |  | 			m.Parts[i] = partInfo | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Proceed to include new part info.
 | 
					
						
							|  |  |  | 	m.Parts = append(m.Parts, partInfo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Parts in fsMeta should be in sorted order by part number.
 | 
					
						
							| 
									
										
										
										
											2016-06-01 11:23:31 +08:00
										 |  |  | 	sort.Sort(byObjectPartNumber(m.Parts)) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-26 04:29:06 +08:00
										 |  |  | func (m *fsMetaV1) WriteTo(lk *lock.LockedFile) (n int64, err error) { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	var metadataBytes []byte | 
					
						
							|  |  |  | 	metadataBytes, err = json.Marshal(m) | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		return 0, traceError(err) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-24 17:06:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-26 04:29:06 +08:00
										 |  |  | 	if err = lk.Truncate(0); err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		return 0, traceError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-26 04:29:06 +08:00
										 |  |  | 	if _, err = lk.Write(metadataBytes); err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		return 0, traceError(err) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-24 17:06:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Success.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	return int64(len(metadataBytes)), nil | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-26 04:29:06 +08:00
										 |  |  | func (m *fsMetaV1) ReadFrom(lk *lock.LockedFile) (n int64, err error) { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	var metadataBytes []byte | 
					
						
							| 
									
										
										
										
											2017-01-26 04:29:06 +08:00
										 |  |  | 	fi, err := lk.Stat() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0, traceError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	metadataBytes, err = ioutil.ReadAll(io.NewSectionReader(lk, 0, fi.Size())) | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 		return 0, traceError(err) | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if len(metadataBytes) == 0 { | 
					
						
							|  |  |  | 		return 0, traceError(io.EOF) | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Decode `fs.json` into fsMeta structure.
 | 
					
						
							|  |  |  | 	if err = json.Unmarshal(metadataBytes, m); err != nil { | 
					
						
							|  |  |  | 		return 0, traceError(err) | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Success.
 | 
					
						
							|  |  |  | 	return int64(len(metadataBytes)), nil | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | // FS metadata constants.
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	// FS backend meta version.
 | 
					
						
							|  |  |  | 	fsMetaVersion = "1.0.0" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// FS backend meta format.
 | 
					
						
							|  |  |  | 	fsMetaFormat = "fs" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Add more constants here.
 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	fsMeta.Format = fsMetaFormat | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | 	fsMeta.Minio.Release = ReleaseTag | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | 	return fsMeta | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-14 16:39:40 +08:00
										 |  |  | // newFSFormatV1 - initializes new formatConfigV1 with FS format info.
 | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | func newFSFormatV1() (format *formatConfigV1) { | 
					
						
							|  |  |  | 	return &formatConfigV1{ | 
					
						
							| 
									
										
										
										
											2016-06-14 16:39:40 +08:00
										 |  |  | 		Version: "1", | 
					
						
							|  |  |  | 		Format:  "fs", | 
					
						
							|  |  |  | 		FS: &fsFormat{ | 
					
						
							|  |  |  | 			Version: "1", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | // loads format.json from minioMetaBucket if it exists.
 | 
					
						
							|  |  |  | func loadFormatFS(fsPath string) (*formatConfigV1, error) { | 
					
						
							|  |  |  | 	rlk, err := lock.RLockedOpenFile(pathJoin(fsPath, minioMetaBucket, fsFormatJSONFile)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if os.IsNotExist(err) { | 
					
						
							|  |  |  | 			return nil, errUnformattedDisk | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer rlk.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	formatBytes, err := ioutil.ReadAll(rlk) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	format := &formatConfigV1{} | 
					
						
							|  |  |  | 	if err = json.Unmarshal(formatBytes, format); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return format, nil | 
					
						
							| 
									
										
										
										
											2016-07-04 11:01:40 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-14 16:39:40 +08:00
										 |  |  | // writes FS format (format.json) into minioMetaBucket.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | func saveFormatFS(formatPath string, fsFormat *formatConfigV1) error { | 
					
						
							| 
									
										
										
										
											2016-06-14 16:39:40 +08:00
										 |  |  | 	metadataBytes, err := json.Marshal(fsFormat) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// fsFormatJSONFile - format.json file stored in minioMetaBucket(.minio.sys) directory.
 | 
					
						
							|  |  |  | 	lk, err := lock.LockedOpenFile(preparePath(formatPath), os.O_CREATE|os.O_WRONLY, 0600) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-14 16:39:40 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	defer lk.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, err = lk.Write(metadataBytes); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Success.
 | 
					
						
							| 
									
										
										
										
											2016-06-14 16:39:40 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | // Return if the part info in uploadedParts and completeParts are same.
 | 
					
						
							|  |  |  | func isPartsSame(uploadedParts []objectPartInfo, completeParts []completePart) bool { | 
					
						
							|  |  |  | 	if len(uploadedParts) != len(completeParts) { | 
					
						
							|  |  |  | 		return false | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | 	for i := range completeParts { | 
					
						
							|  |  |  | 		if uploadedParts[i].Number != completeParts[i].PartNumber || | 
					
						
							|  |  |  | 			uploadedParts[i].ETag != completeParts[i].ETag { | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | 	return true | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } |