| 
									
										
										
										
											2016-07-22 08:31:14 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * Minio Cloud Storage, (C) 2016 Minio, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2016-08-16 06:20:06 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	"path" | 
					
						
							|  |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2016-07-22 08:31:14 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | // readFSMetadata - returns the object metadata `fs.json` content.
 | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | func readFSMetadata(disk StorageAPI, bucket, filePath string) (fsMeta fsMetaV1, err error) { | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 	// Read all `fs.json`.
 | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | 	buf, err := disk.ReadAll(bucket, filePath) | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | 		return fsMetaV1{}, traceError(err) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-24 17:06:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 	// Decode `fs.json` into fsMeta structure.
 | 
					
						
							|  |  |  | 	if err = json.Unmarshal(buf, &fsMeta); err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | 		return fsMetaV1{}, traceError(err) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-24 17:06:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Success.
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	return fsMeta, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | // Write fsMeta to fs.json or fs-append.json.
 | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | func writeFSMetadata(disk StorageAPI, bucket, filePath string, fsMeta fsMetaV1) error { | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | 	tmpPath := path.Join(tmpMetaPrefix, getUUID()) | 
					
						
							|  |  |  | 	metadataBytes, err := json.Marshal(fsMeta) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | 		return traceError(err) | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if err = disk.AppendFile(minioMetaBucket, tmpPath, metadataBytes); err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | 		return traceError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err = disk.RenameFile(minioMetaBucket, tmpPath, bucket, filePath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		err = disk.DeleteFile(minioMetaBucket, tmpPath) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return traceError(err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-08-26 00:39:01 +08:00
										 |  |  | 	return 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{} | 
					
						
							| 
									
										
										
										
											2016-06-29 17:10:40 +08:00
										 |  |  | 	fsMeta.Version = "1.0.0" | 
					
						
							| 
									
										
										
										
											2016-05-26 18:15:01 +08:00
										 |  |  | 	fsMeta.Format = "fs" | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							|  |  |  | func newFSFormatV1() (format formatConfigV1) { | 
					
						
							|  |  |  | 	return formatConfigV1{ | 
					
						
							|  |  |  | 		Version: "1", | 
					
						
							|  |  |  | 		Format:  "fs", | 
					
						
							|  |  |  | 		FS: &fsFormat{ | 
					
						
							|  |  |  | 			Version: "1", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-04 11:01:40 +08:00
										 |  |  | // isFSFormat - returns whether given formatConfigV1 is FS type or not.
 | 
					
						
							|  |  |  | func isFSFormat(format formatConfigV1) bool { | 
					
						
							|  |  |  | 	return format.Format == "fs" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-14 16:39:40 +08:00
										 |  |  | // writes FS format (format.json) into minioMetaBucket.
 | 
					
						
							|  |  |  | func writeFSFormatData(storage StorageAPI, fsFormat formatConfigV1) error { | 
					
						
							|  |  |  | 	metadataBytes, err := json.Marshal(fsFormat) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// fsFormatJSONFile - format.json file stored in minioMetaBucket(.minio) directory.
 | 
					
						
							| 
									
										
										
										
											2016-06-20 06:31:13 +08:00
										 |  |  | 	if err = storage.AppendFile(minioMetaBucket, fsFormatJSONFile, metadataBytes); err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-14 16:39:40 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	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
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-09-01 04:42:57 +08:00
										 |  |  | 	return true | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-07-22 08:31:14 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | var extendedHeaders = []string{ | 
					
						
							|  |  |  | 	"X-Amz-Meta-", | 
					
						
							|  |  |  | 	"X-Minio-Meta-", | 
					
						
							|  |  |  | 	// Add new extended headers.
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // isExtendedHeader validates if input string matches extended headers.
 | 
					
						
							|  |  |  | func isExtendedHeader(header string) bool { | 
					
						
							|  |  |  | 	for _, extendedHeader := range extendedHeaders { | 
					
						
							|  |  |  | 		if strings.HasPrefix(header, extendedHeader) { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Return true if extended HTTP headers are set, false otherwise.
 | 
					
						
							|  |  |  | func hasExtendedHeader(metadata map[string]string) bool { | 
					
						
							| 
									
										
										
										
											2016-08-16 06:20:06 +08:00
										 |  |  | 	if os.Getenv("MINIO_ENABLE_FSMETA") == "1" { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-22 08:31:14 +08:00
										 |  |  | 	for k := range metadata { | 
					
						
							|  |  |  | 		if isExtendedHeader(k) { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } |