2016-07-21 20:31:14 -04: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-18 19:23:42 -04:00
|
|
|
package cmd
|
2016-05-20 23:48:47 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2016-08-15 18:20:06 -04:00
|
|
|
"os"
|
2016-05-20 23:48:47 -04:00
|
|
|
"path"
|
|
|
|
"sort"
|
2016-07-21 20:31:14 -04:00
|
|
|
"strings"
|
2016-05-20 23:48:47 -04:00
|
|
|
)
|
|
|
|
|
2016-05-25 00:24:20 -04:00
|
|
|
const (
|
2016-06-14 04:39:40 -04:00
|
|
|
fsMetaJSONFile = "fs.json"
|
|
|
|
fsFormatJSONFile = "format.json"
|
2016-05-25 00:24:20 -04:00
|
|
|
)
|
|
|
|
|
2016-05-20 23:48:47 -04: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-21 20:31:14 -04:00
|
|
|
// Metadata map for current object `fs.json`.
|
|
|
|
Meta map[string]string `json:"meta,omitempty"`
|
|
|
|
Parts []objectPartInfo `json:"parts,omitempty"`
|
2016-05-20 23:48:47 -04:00
|
|
|
}
|
|
|
|
|
2016-05-26 06:15:01 -04:00
|
|
|
// ObjectPartIndex - returns the index of matching object part number.
|
|
|
|
func (m fsMetaV1) ObjectPartIndex(partNumber int) (partIndex int) {
|
2016-05-20 23:48:47 -04:00
|
|
|
for i, part := range m.Parts {
|
2016-05-26 06:15:01 -04:00
|
|
|
if partNumber == part.Number {
|
|
|
|
partIndex = i
|
|
|
|
return partIndex
|
2016-05-20 23:48:47 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddObjectPart - add a new object part in order.
|
2016-05-26 06:15:01 -04: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-05-31 23:23:31 -04:00
|
|
|
sort.Sort(byObjectPartNumber(m.Parts))
|
2016-05-20 23:48:47 -04:00
|
|
|
}
|
|
|
|
|
2016-05-26 06:15:01 -04:00
|
|
|
// readFSMetadata - returns the object metadata `fs.json` content.
|
2016-08-31 16:42:57 -04:00
|
|
|
func readFSMetadata(disk StorageAPI, bucket, filePath string) (fsMeta fsMetaV1, err error) {
|
2016-06-25 17:51:06 -04:00
|
|
|
// Read all `fs.json`.
|
2016-08-31 16:42:57 -04:00
|
|
|
buf, err := disk.ReadAll(bucket, filePath)
|
2016-06-25 17:51:06 -04:00
|
|
|
if err != nil {
|
2016-05-20 23:48:47 -04:00
|
|
|
return fsMetaV1{}, err
|
|
|
|
}
|
2016-06-24 05:06:23 -04:00
|
|
|
|
2016-06-25 17:51:06 -04:00
|
|
|
// Decode `fs.json` into fsMeta structure.
|
|
|
|
if err = json.Unmarshal(buf, &fsMeta); err != nil {
|
2016-05-20 23:48:47 -04:00
|
|
|
return fsMetaV1{}, err
|
|
|
|
}
|
2016-06-24 05:06:23 -04:00
|
|
|
|
|
|
|
// Success.
|
2016-05-20 23:48:47 -04:00
|
|
|
return fsMeta, nil
|
|
|
|
}
|
|
|
|
|
2016-08-31 16:42:57 -04:00
|
|
|
// Write fsMeta to fs.json or fs-append.json.
|
|
|
|
func writeFSMetadata(disk StorageAPI, bucket, filePath string, fsMeta fsMetaV1) (err error) {
|
|
|
|
tmpPath := path.Join(tmpMetaPrefix, getUUID())
|
|
|
|
metadataBytes, err := json.Marshal(fsMeta)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = disk.AppendFile(minioMetaBucket, tmpPath, metadataBytes); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return disk.RenameFile(minioMetaBucket, tmpPath, bucket, filePath)
|
|
|
|
}
|
|
|
|
|
2016-05-26 06:15:01 -04:00
|
|
|
// newFSMetaV1 - initializes new fsMetaV1.
|
|
|
|
func newFSMetaV1() (fsMeta fsMetaV1) {
|
|
|
|
fsMeta = fsMetaV1{}
|
2016-06-29 05:10:40 -04:00
|
|
|
fsMeta.Version = "1.0.0"
|
2016-05-26 06:15:01 -04:00
|
|
|
fsMeta.Format = "fs"
|
2016-08-18 19:23:42 -04:00
|
|
|
fsMeta.Minio.Release = ReleaseTag
|
2016-05-26 06:15:01 -04:00
|
|
|
return fsMeta
|
|
|
|
}
|
|
|
|
|
2016-06-14 04:39:40 -04: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-03 23:01:40 -04:00
|
|
|
// isFSFormat - returns whether given formatConfigV1 is FS type or not.
|
|
|
|
func isFSFormat(format formatConfigV1) bool {
|
|
|
|
return format.Format == "fs"
|
|
|
|
}
|
|
|
|
|
2016-06-14 04:39:40 -04: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-19 18:31:13 -04:00
|
|
|
if err = storage.AppendFile(minioMetaBucket, fsFormatJSONFile, metadataBytes); err != nil {
|
2016-06-14 04:39:40 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-08-31 16:42:57 -04: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-20 23:48:47 -04:00
|
|
|
}
|
2016-08-31 16:42:57 -04:00
|
|
|
for i := range completeParts {
|
|
|
|
if uploadedParts[i].Number != completeParts[i].PartNumber ||
|
|
|
|
uploadedParts[i].ETag != completeParts[i].ETag {
|
|
|
|
return false
|
|
|
|
}
|
2016-05-20 23:48:47 -04:00
|
|
|
}
|
2016-08-31 16:42:57 -04:00
|
|
|
return true
|
2016-05-20 23:48:47 -04:00
|
|
|
}
|
2016-07-21 20:31:14 -04: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-15 18:20:06 -04:00
|
|
|
if os.Getenv("MINIO_ENABLE_FSMETA") == "1" {
|
|
|
|
return true
|
|
|
|
}
|
2016-07-21 20:31:14 -04:00
|
|
|
for k := range metadata {
|
|
|
|
if isExtendedHeader(k) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|