| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * MinIO Cloud Storage, (C) 2020 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2020-06-24 01:20:31 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2020-09-10 09:11:24 +08:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/google/uuid" | 
					
						
							| 
									
										
										
										
											2020-08-11 23:29:29 +08:00
										 |  |  | 	xhttp "github.com/minio/minio/cmd/http" | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	// XL header specifies the format
 | 
					
						
							|  |  |  | 	xlHeader = [4]byte{'X', 'L', '2', ' '} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XLv2 version 1
 | 
					
						
							|  |  |  | 	xlVersionV1 = [4]byte{'1', ' ', ' ', ' '} | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func checkXL2V1(buf []byte) error { | 
					
						
							|  |  |  | 	if len(buf) <= 8 { | 
					
						
							|  |  |  | 		return fmt.Errorf("xlMeta: no data") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !bytes.Equal(buf[:4], xlHeader[:]) { | 
					
						
							| 
									
										
										
										
											2020-06-23 08:27:54 +08:00
										 |  |  | 		return fmt.Errorf("xlMeta: unknown XLv2 header, expected %v, got %v", xlHeader[:4], buf[:4]) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !bytes.Equal(buf[4:8], xlVersionV1[:]) { | 
					
						
							| 
									
										
										
										
											2020-06-23 08:27:54 +08:00
										 |  |  | 		return fmt.Errorf("xlMeta: unknown XLv2 version, expected %v, got %v", xlVersionV1[:4], buf[4:8]) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func isXL2V1Format(buf []byte) bool { | 
					
						
							|  |  |  | 	return checkXL2V1(buf) == nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // The []journal contains all the different versions of the object.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This array can have 3 kinds of objects:
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // ``object``: If the object is uploaded the usual way: putobject, multipart-put, copyobject
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // ``delete``: This is the delete-marker
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // ``legacyObject``: This is the legacy object in xlV1 format, preserved until its overwritten
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // The most recently updated element in the array is considered the latest version.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Backend directory tree structure:
 | 
					
						
							|  |  |  | // disk1/
 | 
					
						
							|  |  |  | // └── bucket
 | 
					
						
							|  |  |  | //     └── object
 | 
					
						
							|  |  |  | //         ├── a192c1d5-9bd5-41fd-9a90-ab10e165398d
 | 
					
						
							|  |  |  | //         │   └── part.1
 | 
					
						
							|  |  |  | //         ├── c06e0436-f813-447e-ae5e-f2564df9dfd4
 | 
					
						
							|  |  |  | //         │   └── part.1
 | 
					
						
							|  |  |  | //         ├── df433928-2dcf-47b1-a786-43efa0f6b424
 | 
					
						
							|  |  |  | //         │   └── part.1
 | 
					
						
							|  |  |  | //         ├── legacy
 | 
					
						
							|  |  |  | //         │   └── part.1
 | 
					
						
							|  |  |  | //         └── xl.meta
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //go:generate msgp -file=$GOFILE -unexported
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // VersionType defines the type of journal type of the current entry.
 | 
					
						
							|  |  |  | type VersionType uint8 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // List of different types of journal type
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	invalidVersionType VersionType = 0 | 
					
						
							|  |  |  | 	ObjectType         VersionType = 1 | 
					
						
							|  |  |  | 	DeleteType         VersionType = 2 | 
					
						
							|  |  |  | 	LegacyType         VersionType = 3 | 
					
						
							|  |  |  | 	lastVersionType    VersionType = 4 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e VersionType) valid() bool { | 
					
						
							|  |  |  | 	return e > invalidVersionType && e < lastVersionType | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ErasureAlgo defines common type of different erasure algorithms
 | 
					
						
							|  |  |  | type ErasureAlgo uint8 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // List of currently supported erasure coding algorithms
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	invalidErasureAlgo ErasureAlgo = 0 | 
					
						
							|  |  |  | 	ReedSolomon        ErasureAlgo = 1 | 
					
						
							|  |  |  | 	lastErasureAlgo    ErasureAlgo = 2 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e ErasureAlgo) valid() bool { | 
					
						
							|  |  |  | 	return e > invalidErasureAlgo && e < lastErasureAlgo | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e ErasureAlgo) String() string { | 
					
						
							|  |  |  | 	switch e { | 
					
						
							|  |  |  | 	case ReedSolomon: | 
					
						
							|  |  |  | 		return "reedsolomon" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return "" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ChecksumAlgo defines common type of different checksum algorithms
 | 
					
						
							|  |  |  | type ChecksumAlgo uint8 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // List of currently supported checksum algorithms
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	invalidChecksumAlgo ChecksumAlgo = 0 | 
					
						
							|  |  |  | 	HighwayHash         ChecksumAlgo = 1 | 
					
						
							|  |  |  | 	lastChecksumAlgo    ChecksumAlgo = 2 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e ChecksumAlgo) valid() bool { | 
					
						
							|  |  |  | 	return e > invalidChecksumAlgo && e < lastChecksumAlgo | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // xlMetaV2DeleteMarker defines the data struct for the delete marker journal type
 | 
					
						
							|  |  |  | type xlMetaV2DeleteMarker struct { | 
					
						
							|  |  |  | 	VersionID [16]byte `json:"ID" msg:"ID"`       // Version ID for delete marker
 | 
					
						
							|  |  |  | 	ModTime   int64    `json:"MTime" msg:"MTime"` // Object delete marker modified time
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // xlMetaV2Object defines the data struct for object journal type
 | 
					
						
							|  |  |  | type xlMetaV2Object struct { | 
					
						
							|  |  |  | 	VersionID          [16]byte          `json:"ID" msg:"ID"`                                     // Version ID
 | 
					
						
							|  |  |  | 	DataDir            [16]byte          `json:"DDir" msg:"DDir"`                                 // Data dir ID
 | 
					
						
							|  |  |  | 	ErasureAlgorithm   ErasureAlgo       `json:"EcAlgo" msg:"EcAlgo"`                             // Erasure coding algorithm
 | 
					
						
							|  |  |  | 	ErasureM           int               `json:"EcM" msg:"EcM"`                                   // Erasure data blocks
 | 
					
						
							|  |  |  | 	ErasureN           int               `json:"EcN" msg:"EcN"`                                   // Erasure parity blocks
 | 
					
						
							|  |  |  | 	ErasureBlockSize   int64             `json:"EcBSize" msg:"EcBSize"`                           // Erasure block size
 | 
					
						
							|  |  |  | 	ErasureIndex       int               `json:"EcIndex" msg:"EcIndex"`                           // Erasure disk index
 | 
					
						
							|  |  |  | 	ErasureDist        []uint8           `json:"EcDist" msg:"EcDist"`                             // Erasure distribution
 | 
					
						
							|  |  |  | 	BitrotChecksumAlgo ChecksumAlgo      `json:"CSumAlgo" msg:"CSumAlgo"`                         // Bitrot checksum algo
 | 
					
						
							|  |  |  | 	PartNumbers        []int             `json:"PartNums" msg:"PartNums"`                         // Part Numbers
 | 
					
						
							|  |  |  | 	PartETags          []string          `json:"PartETags" msg:"PartETags"`                       // Part ETags
 | 
					
						
							|  |  |  | 	PartSizes          []int64           `json:"PartSizes" msg:"PartSizes"`                       // Part Sizes
 | 
					
						
							|  |  |  | 	PartActualSizes    []int64           `json:"PartASizes,omitempty" msg:"PartASizes,omitempty"` // Part ActualSizes (compression)
 | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | 	Size               int64             `json:"Size" msg:"Size"`                                 // Object version size
 | 
					
						
							|  |  |  | 	ModTime            int64             `json:"MTime" msg:"MTime"`                               // Object version modified time
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	MetaSys            map[string][]byte `json:"MetaSys,omitempty" msg:"MetaSys,omitempty"`       // Object version internal metadata
 | 
					
						
							|  |  |  | 	MetaUser           map[string]string `json:"MetaUsr,omitempty" msg:"MetaUsr,omitempty"`       // Object version metadata set by user
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // xlMetaV2Version describes the jouranal entry, Type defines
 | 
					
						
							|  |  |  | // the current journal entry type other types might be nil based
 | 
					
						
							|  |  |  | // on what Type field carries, it is imperative for the caller
 | 
					
						
							|  |  |  | // to verify which journal type first before accessing rest of the fields.
 | 
					
						
							|  |  |  | type xlMetaV2Version struct { | 
					
						
							|  |  |  | 	Type         VersionType           `json:"Type" msg:"Type"` | 
					
						
							|  |  |  | 	ObjectV1     *xlMetaV1Object       `json:"V1Obj,omitempty" msg:"V1Obj,omitempty"` | 
					
						
							|  |  |  | 	ObjectV2     *xlMetaV2Object       `json:"V2Obj,omitempty" msg:"V2Obj,omitempty"` | 
					
						
							|  |  |  | 	DeleteMarker *xlMetaV2DeleteMarker `json:"DelObj,omitempty" msg:"DelObj,omitempty"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Valid xl meta xlMetaV2Version is valid
 | 
					
						
							|  |  |  | func (j xlMetaV2Version) Valid() bool { | 
					
						
							|  |  |  | 	switch j.Type { | 
					
						
							|  |  |  | 	case LegacyType: | 
					
						
							| 
									
										
										
										
											2020-06-23 05:33:58 +08:00
										 |  |  | 		return j.ObjectV1 != nil && | 
					
						
							|  |  |  | 			j.ObjectV1.valid() | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	case ObjectType: | 
					
						
							|  |  |  | 		return j.ObjectV2 != nil && | 
					
						
							|  |  |  | 			j.ObjectV2.ErasureAlgorithm.valid() && | 
					
						
							|  |  |  | 			j.ObjectV2.BitrotChecksumAlgo.valid() && | 
					
						
							| 
									
										
										
										
											2020-06-23 05:33:58 +08:00
										 |  |  | 			isXLMetaErasureInfoValid(j.ObjectV2.ErasureM, j.ObjectV2.ErasureN) && | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | 			j.ObjectV2.ModTime > 0 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	case DeleteType: | 
					
						
							| 
									
										
										
										
											2020-06-23 05:33:58 +08:00
										 |  |  | 		return j.DeleteMarker != nil && | 
					
						
							|  |  |  | 			j.DeleteMarker.ModTime > 0 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // xlMetaV2 - object meta structure defines the format and list of
 | 
					
						
							|  |  |  | // the journals for the object.
 | 
					
						
							|  |  |  | type xlMetaV2 struct { | 
					
						
							|  |  |  | 	Versions []xlMetaV2Version `json:"Versions" msg:"Versions"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AddLegacy adds a legacy version, is only called when no prior
 | 
					
						
							|  |  |  | // versions exist, safe to use it by only one function in xl-storage(RenameData)
 | 
					
						
							|  |  |  | func (z *xlMetaV2) AddLegacy(m *xlMetaV1Object) error { | 
					
						
							|  |  |  | 	if !m.valid() { | 
					
						
							|  |  |  | 		return errFileCorrupt | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	m.VersionID = nullVersionID | 
					
						
							|  |  |  | 	m.DataDir = legacyDataDir | 
					
						
							|  |  |  | 	z.Versions = []xlMetaV2Version{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Type:     LegacyType, | 
					
						
							|  |  |  | 			ObjectV1: m, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Load unmarshal and load the entire message pack.
 | 
					
						
							|  |  |  | func (z *xlMetaV2) Load(buf []byte) error { | 
					
						
							|  |  |  | 	if err := checkXL2V1(buf); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	_, err := z.UnmarshalMsg(buf[8:]) | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AddVersion adds a new version
 | 
					
						
							|  |  |  | func (z *xlMetaV2) AddVersion(fi FileInfo) error { | 
					
						
							| 
									
										
										
										
											2020-06-18 02:13:41 +08:00
										 |  |  | 	if fi.VersionID == "" { | 
					
						
							|  |  |  | 		// this means versioning is not yet
 | 
					
						
							| 
									
										
										
										
											2020-09-02 15:19:03 +08:00
										 |  |  | 		// enabled or suspend i.e all versions
 | 
					
						
							|  |  |  | 		// are basically default value i.e "null"
 | 
					
						
							| 
									
										
										
										
											2020-06-18 02:13:41 +08:00
										 |  |  | 		fi.VersionID = nullVersionID | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-18 02:13:41 +08:00
										 |  |  | 	var uv uuid.UUID | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	if fi.VersionID != "" && fi.VersionID != nullVersionID { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		uv, err = uuid.Parse(fi.VersionID) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dd, err := uuid.Parse(fi.DataDir) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ventry := xlMetaV2Version{ | 
					
						
							|  |  |  | 		Type: ObjectType, | 
					
						
							|  |  |  | 		ObjectV2: &xlMetaV2Object{ | 
					
						
							|  |  |  | 			VersionID:          uv, | 
					
						
							|  |  |  | 			DataDir:            dd, | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | 			Size:               fi.Size, | 
					
						
							|  |  |  | 			ModTime:            fi.ModTime.UnixNano(), | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			ErasureAlgorithm:   ReedSolomon, | 
					
						
							|  |  |  | 			ErasureM:           fi.Erasure.DataBlocks, | 
					
						
							|  |  |  | 			ErasureN:           fi.Erasure.ParityBlocks, | 
					
						
							|  |  |  | 			ErasureBlockSize:   fi.Erasure.BlockSize, | 
					
						
							|  |  |  | 			ErasureIndex:       fi.Erasure.Index, | 
					
						
							|  |  |  | 			BitrotChecksumAlgo: HighwayHash, | 
					
						
							|  |  |  | 			ErasureDist:        make([]uint8, len(fi.Erasure.Distribution)), | 
					
						
							|  |  |  | 			PartNumbers:        make([]int, len(fi.Parts)), | 
					
						
							|  |  |  | 			PartETags:          make([]string, len(fi.Parts)), | 
					
						
							|  |  |  | 			PartSizes:          make([]int64, len(fi.Parts)), | 
					
						
							|  |  |  | 			PartActualSizes:    make([]int64, len(fi.Parts)), | 
					
						
							|  |  |  | 			MetaSys:            make(map[string][]byte), | 
					
						
							| 
									
										
										
										
											2020-09-11 02:37:22 +08:00
										 |  |  | 			MetaUser:           make(map[string]string, len(fi.Metadata)), | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := range fi.Erasure.Distribution { | 
					
						
							|  |  |  | 		ventry.ObjectV2.ErasureDist[i] = uint8(fi.Erasure.Distribution[i]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := range fi.Parts { | 
					
						
							|  |  |  | 		ventry.ObjectV2.PartSizes[i] = fi.Parts[i].Size | 
					
						
							|  |  |  | 		if fi.Parts[i].ETag != "" { | 
					
						
							|  |  |  | 			ventry.ObjectV2.PartETags[i] = fi.Parts[i].ETag | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ventry.ObjectV2.PartNumbers[i] = fi.Parts[i].Number | 
					
						
							|  |  |  | 		ventry.ObjectV2.PartActualSizes[i] = fi.Parts[i].ActualSize | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for k, v := range fi.Metadata { | 
					
						
							|  |  |  | 		if strings.HasPrefix(strings.ToLower(k), ReservedMetadataPrefixLower) { | 
					
						
							|  |  |  | 			ventry.ObjectV2.MetaSys[k] = []byte(v) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			ventry.ObjectV2.MetaUser[k] = v | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-24 01:20:31 +08:00
										 |  |  | 	if !ventry.Valid() { | 
					
						
							|  |  |  | 		return errors.New("internal error: invalid version entry generated") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	for i, version := range z.Versions { | 
					
						
							|  |  |  | 		if !version.Valid() { | 
					
						
							|  |  |  | 			return errFileCorrupt | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		switch version.Type { | 
					
						
							|  |  |  | 		case LegacyType: | 
					
						
							|  |  |  | 			// This would convert legacy type into new ObjectType
 | 
					
						
							|  |  |  | 			// this means that we are basically purging the `null`
 | 
					
						
							|  |  |  | 			// version of the object.
 | 
					
						
							|  |  |  | 			if version.ObjectV1.VersionID == fi.VersionID { | 
					
						
							|  |  |  | 				z.Versions[i] = ventry | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case ObjectType: | 
					
						
							|  |  |  | 			if bytes.Equal(version.ObjectV2.VersionID[:], uv[:]) { | 
					
						
							|  |  |  | 				z.Versions[i] = ventry | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case DeleteType: | 
					
						
							|  |  |  | 			// Allowing delete marker to replaced with an proper
 | 
					
						
							|  |  |  | 			// object data type as well, this is not S3 complaint
 | 
					
						
							|  |  |  | 			// behavior but kept here for future flexibility.
 | 
					
						
							|  |  |  | 			if bytes.Equal(version.DeleteMarker.VersionID[:], uv[:]) { | 
					
						
							|  |  |  | 				z.Versions[i] = ventry | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	z.Versions = append(z.Versions, ventry) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newXLMetaV2(fi FileInfo) (xlMetaV2, error) { | 
					
						
							|  |  |  | 	xlMeta := xlMetaV2{} | 
					
						
							|  |  |  | 	return xlMeta, xlMeta.AddVersion(fi) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (j xlMetaV2DeleteMarker) ToFileInfo(volume, path string) (FileInfo, error) { | 
					
						
							| 
									
										
										
										
											2020-09-02 15:19:03 +08:00
										 |  |  | 	versionID := "" | 
					
						
							|  |  |  | 	var uv uuid.UUID | 
					
						
							|  |  |  | 	// check if the version is not "null"
 | 
					
						
							|  |  |  | 	if !bytes.Equal(j.VersionID[:], uv[:]) { | 
					
						
							|  |  |  | 		versionID = uuid.UUID(j.VersionID).String() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	fi := FileInfo{ | 
					
						
							|  |  |  | 		Volume:    volume, | 
					
						
							|  |  |  | 		Name:      path, | 
					
						
							| 
									
										
										
										
											2020-06-16 22:54:27 +08:00
										 |  |  | 		ModTime:   time.Unix(0, j.ModTime).UTC(), | 
					
						
							| 
									
										
										
										
											2020-09-02 15:19:03 +08:00
										 |  |  | 		VersionID: versionID, | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		Deleted:   true, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return fi, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (j xlMetaV2Object) ToFileInfo(volume, path string) (FileInfo, error) { | 
					
						
							|  |  |  | 	versionID := "" | 
					
						
							|  |  |  | 	var uv uuid.UUID | 
					
						
							|  |  |  | 	// check if the version is not "null"
 | 
					
						
							|  |  |  | 	if !bytes.Equal(j.VersionID[:], uv[:]) { | 
					
						
							|  |  |  | 		versionID = uuid.UUID(j.VersionID).String() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	fi := FileInfo{ | 
					
						
							|  |  |  | 		Volume:    volume, | 
					
						
							|  |  |  | 		Name:      path, | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | 		Size:      j.Size, | 
					
						
							|  |  |  | 		ModTime:   time.Unix(0, j.ModTime).UTC(), | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		VersionID: versionID, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	fi.Parts = make([]ObjectPartInfo, len(j.PartNumbers)) | 
					
						
							|  |  |  | 	for i := range fi.Parts { | 
					
						
							| 
									
										
										
										
											2020-08-25 03:11:20 +08:00
										 |  |  | 		fi.Parts[i].Number = j.PartNumbers[i] | 
					
						
							|  |  |  | 		fi.Parts[i].Size = j.PartSizes[i] | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		fi.Parts[i].ETag = j.PartETags[i] | 
					
						
							| 
									
										
										
										
											2020-08-25 03:11:20 +08:00
										 |  |  | 		fi.Parts[i].ActualSize = j.PartActualSizes[i] | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	fi.Erasure.Checksums = make([]ChecksumInfo, len(j.PartSizes)) | 
					
						
							|  |  |  | 	for i := range fi.Parts { | 
					
						
							|  |  |  | 		fi.Erasure.Checksums[i].PartNumber = fi.Parts[i].Number | 
					
						
							|  |  |  | 		switch j.BitrotChecksumAlgo { | 
					
						
							|  |  |  | 		case HighwayHash: | 
					
						
							|  |  |  | 			fi.Erasure.Checksums[i].Algorithm = HighwayHash256S | 
					
						
							|  |  |  | 			fi.Erasure.Checksums[i].Hash = []byte{} | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return FileInfo{}, fmt.Errorf("unknown BitrotChecksumAlgo: %v", j.BitrotChecksumAlgo) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	fi.Metadata = make(map[string]string, len(j.MetaUser)+len(j.MetaSys)) | 
					
						
							|  |  |  | 	for k, v := range j.MetaUser { | 
					
						
							| 
									
										
										
										
											2020-08-11 23:29:29 +08:00
										 |  |  | 		// https://github.com/google/security-research/security/advisories/GHSA-76wf-9vgp-pj7w
 | 
					
						
							|  |  |  | 		if strings.EqualFold(k, xhttp.AmzMetaUnencryptedContentLength) || strings.EqualFold(k, xhttp.AmzMetaUnencryptedContentMD5) { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		fi.Metadata[k] = v | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for k, v := range j.MetaSys { | 
					
						
							|  |  |  | 		if strings.HasPrefix(strings.ToLower(k), ReservedMetadataPrefixLower) { | 
					
						
							|  |  |  | 			fi.Metadata[k] = string(v) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	fi.Erasure.Algorithm = j.ErasureAlgorithm.String() | 
					
						
							|  |  |  | 	fi.Erasure.Index = j.ErasureIndex | 
					
						
							|  |  |  | 	fi.Erasure.BlockSize = j.ErasureBlockSize | 
					
						
							|  |  |  | 	fi.Erasure.DataBlocks = j.ErasureM | 
					
						
							|  |  |  | 	fi.Erasure.ParityBlocks = j.ErasureN | 
					
						
							|  |  |  | 	fi.Erasure.Distribution = make([]int, len(j.ErasureDist)) | 
					
						
							|  |  |  | 	for i := range j.ErasureDist { | 
					
						
							|  |  |  | 		fi.Erasure.Distribution[i] = int(j.ErasureDist[i]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	fi.DataDir = uuid.UUID(j.DataDir).String() | 
					
						
							|  |  |  | 	return fi, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DeleteVersion deletes the version specified by version id.
 | 
					
						
							|  |  |  | // returns to the caller which dataDir to delete, also
 | 
					
						
							|  |  |  | // indicates if this is the last version.
 | 
					
						
							|  |  |  | func (z *xlMetaV2) DeleteVersion(fi FileInfo) (string, bool, error) { | 
					
						
							|  |  |  | 	// This is a situation where versionId is explicitly
 | 
					
						
							|  |  |  | 	// specified as "null", as we do not save "null"
 | 
					
						
							|  |  |  | 	// string it is considered empty. But empty also
 | 
					
						
							|  |  |  | 	// means the version which matches will be purged.
 | 
					
						
							|  |  |  | 	if fi.VersionID == nullVersionID { | 
					
						
							|  |  |  | 		fi.VersionID = "" | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-02 15:19:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	var uv uuid.UUID | 
					
						
							|  |  |  | 	if fi.VersionID != "" { | 
					
						
							|  |  |  | 		uv, _ = uuid.Parse(fi.VersionID) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-02 15:19:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var ventry xlMetaV2Version | 
					
						
							|  |  |  | 	if fi.Deleted { | 
					
						
							|  |  |  | 		ventry = xlMetaV2Version{ | 
					
						
							|  |  |  | 			Type: DeleteType, | 
					
						
							|  |  |  | 			DeleteMarker: &xlMetaV2DeleteMarker{ | 
					
						
							|  |  |  | 				VersionID: uv, | 
					
						
							|  |  |  | 				ModTime:   fi.ModTime.UnixNano(), | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !ventry.Valid() { | 
					
						
							|  |  |  | 			return "", false, errors.New("internal error: invalid version entry generated") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	for i, version := range z.Versions { | 
					
						
							|  |  |  | 		if !version.Valid() { | 
					
						
							|  |  |  | 			return "", false, errFileCorrupt | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		switch version.Type { | 
					
						
							|  |  |  | 		case LegacyType: | 
					
						
							|  |  |  | 			if version.ObjectV1.VersionID == fi.VersionID { | 
					
						
							|  |  |  | 				z.Versions = append(z.Versions[:i], z.Versions[i+1:]...) | 
					
						
							| 
									
										
										
										
											2020-09-02 15:19:03 +08:00
										 |  |  | 				if fi.Deleted { | 
					
						
							|  |  |  | 					z.Versions = append(z.Versions, ventry) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				return version.ObjectV1.DataDir, len(z.Versions) == 0, nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case DeleteType: | 
					
						
							|  |  |  | 			if bytes.Equal(version.DeleteMarker.VersionID[:], uv[:]) { | 
					
						
							|  |  |  | 				z.Versions = append(z.Versions[:i], z.Versions[i+1:]...) | 
					
						
							| 
									
										
										
										
											2020-09-02 15:19:03 +08:00
										 |  |  | 				if fi.Deleted { | 
					
						
							|  |  |  | 					z.Versions = append(z.Versions, ventry) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				return "", len(z.Versions) == 0, nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-04 07:21:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	findDataDir := func(dataDir [16]byte, versions []xlMetaV2Version) int { | 
					
						
							|  |  |  | 		var sameDataDirCount int | 
					
						
							|  |  |  | 		for _, version := range versions { | 
					
						
							|  |  |  | 			switch version.Type { | 
					
						
							|  |  |  | 			case ObjectType: | 
					
						
							|  |  |  | 				if bytes.Equal(version.ObjectV2.DataDir[:], dataDir[:]) { | 
					
						
							|  |  |  | 					sameDataDirCount++ | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return sameDataDirCount | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, version := range z.Versions { | 
					
						
							|  |  |  | 		if !version.Valid() { | 
					
						
							|  |  |  | 			return "", false, errFileCorrupt | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		switch version.Type { | 
					
						
							|  |  |  | 		case ObjectType: | 
					
						
							|  |  |  | 			if bytes.Equal(version.ObjectV2.VersionID[:], uv[:]) { | 
					
						
							|  |  |  | 				z.Versions = append(z.Versions[:i], z.Versions[i+1:]...) | 
					
						
							|  |  |  | 				if findDataDir(version.ObjectV2.DataDir, z.Versions) > 0 { | 
					
						
							| 
									
										
										
										
											2020-09-02 15:19:03 +08:00
										 |  |  | 					if fi.Deleted { | 
					
						
							|  |  |  | 						z.Versions = append(z.Versions, ventry) | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-08-04 07:21:10 +08:00
										 |  |  | 					// Found that another version references the same dataDir
 | 
					
						
							|  |  |  | 					// we shouldn't remove it, and only remove the version instead
 | 
					
						
							|  |  |  | 					return "", len(z.Versions) == 0, nil | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-09-02 15:19:03 +08:00
										 |  |  | 				if fi.Deleted { | 
					
						
							|  |  |  | 					z.Versions = append(z.Versions, ventry) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-08-04 07:21:10 +08:00
										 |  |  | 				return uuid.UUID(version.ObjectV2.DataDir).String(), len(z.Versions) == 0, nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-02 15:19:03 +08:00
										 |  |  | 	if fi.Deleted { | 
					
						
							|  |  |  | 		z.Versions = append(z.Versions, ventry) | 
					
						
							|  |  |  | 		return "", false, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	return "", false, errFileVersionNotFound | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TotalSize returns the total size of all versions.
 | 
					
						
							|  |  |  | func (z xlMetaV2) TotalSize() int64 { | 
					
						
							|  |  |  | 	var total int64 | 
					
						
							|  |  |  | 	for i := range z.Versions { | 
					
						
							|  |  |  | 		switch z.Versions[i].Type { | 
					
						
							|  |  |  | 		case ObjectType: | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | 			total += z.Versions[i].ObjectV2.Size | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		case LegacyType: | 
					
						
							|  |  |  | 			total += z.Versions[i].ObjectV1.Stat.Size | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return total | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-10 09:11:24 +08:00
										 |  |  | type versionsSorter []FileInfo | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (v versionsSorter) Len() int      { return len(v) } | 
					
						
							|  |  |  | func (v versionsSorter) Swap(i, j int) { v[i], v[j] = v[j], v[i] } | 
					
						
							|  |  |  | func (v versionsSorter) Less(i, j int) bool { | 
					
						
							|  |  |  | 	if v[i].IsLatest { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if v[j].IsLatest { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return v[i].ModTime.After(v[j].ModTime) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // ListVersions lists current versions, and current deleted
 | 
					
						
							|  |  |  | // versions returns error for unexpected entries.
 | 
					
						
							| 
									
										
										
										
											2020-07-04 00:15:44 +08:00
										 |  |  | func (z xlMetaV2) ListVersions(volume, path string) (versions []FileInfo, modTime time.Time, err error) { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	var latestModTime time.Time | 
					
						
							|  |  |  | 	var latestVersionID string | 
					
						
							|  |  |  | 	for _, version := range z.Versions { | 
					
						
							|  |  |  | 		if !version.Valid() { | 
					
						
							| 
									
										
										
										
											2020-07-04 00:15:44 +08:00
										 |  |  | 			return nil, latestModTime, errFileCorrupt | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		var fi FileInfo | 
					
						
							|  |  |  | 		switch version.Type { | 
					
						
							|  |  |  | 		case ObjectType: | 
					
						
							|  |  |  | 			fi, err = version.ObjectV2.ToFileInfo(volume, path) | 
					
						
							|  |  |  | 		case DeleteType: | 
					
						
							|  |  |  | 			fi, err = version.DeleteMarker.ToFileInfo(volume, path) | 
					
						
							|  |  |  | 		case LegacyType: | 
					
						
							|  |  |  | 			fi, err = version.ObjectV1.ToFileInfo(volume, path) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-07-04 00:15:44 +08:00
										 |  |  | 			return nil, latestModTime, err | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if fi.ModTime.After(latestModTime) { | 
					
						
							|  |  |  | 			latestModTime = fi.ModTime | 
					
						
							|  |  |  | 			latestVersionID = fi.VersionID | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | 		versions = append(versions, fi) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// We didn't find the version in delete markers so latest version
 | 
					
						
							|  |  |  | 	// is indeed one of the actual version of the object with data.
 | 
					
						
							|  |  |  | 	for i := range versions { | 
					
						
							|  |  |  | 		if versions[i].VersionID != latestVersionID { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		versions[i].IsLatest = true | 
					
						
							|  |  |  | 		break | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-04 00:15:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-10 09:11:24 +08:00
										 |  |  | 	sort.Sort(versionsSorter(versions)) | 
					
						
							| 
									
										
										
										
											2020-07-04 00:15:44 +08:00
										 |  |  | 	return versions, latestModTime, nil | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ToFileInfo converts xlMetaV2 into a common FileInfo datastructure
 | 
					
						
							|  |  |  | // for consumption across callers.
 | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | func (z xlMetaV2) ToFileInfo(volume, path, versionID string) (fi FileInfo, err error) { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	var uv uuid.UUID | 
					
						
							|  |  |  | 	if versionID != "" { | 
					
						
							|  |  |  | 		uv, _ = uuid.Parse(versionID) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | 	var latestModTime time.Time | 
					
						
							|  |  |  | 	var latestIndex int | 
					
						
							|  |  |  | 	for i, version := range z.Versions { | 
					
						
							|  |  |  | 		if !version.Valid() { | 
					
						
							|  |  |  | 			logger.LogIf(GlobalContext, fmt.Errorf("invalid version detected %#v", version)) | 
					
						
							|  |  |  | 			return FileInfo{}, errFileNotFound | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var modTime time.Time | 
					
						
							|  |  |  | 		switch version.Type { | 
					
						
							|  |  |  | 		case ObjectType: | 
					
						
							|  |  |  | 			modTime = time.Unix(0, version.ObjectV2.ModTime) | 
					
						
							|  |  |  | 		case DeleteType: | 
					
						
							|  |  |  | 			modTime = time.Unix(0, version.DeleteMarker.ModTime) | 
					
						
							|  |  |  | 		case LegacyType: | 
					
						
							|  |  |  | 			modTime = version.ObjectV1.Stat.ModTime | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if modTime.After(latestModTime) { | 
					
						
							|  |  |  | 			latestModTime = modTime | 
					
						
							|  |  |  | 			latestIndex = i | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if versionID == "" { | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | 		if len(z.Versions) >= 1 { | 
					
						
							|  |  |  | 			if !z.Versions[latestIndex].Valid() { | 
					
						
							|  |  |  | 				logger.LogIf(GlobalContext, fmt.Errorf("invalid version detected %#v", z.Versions[latestIndex])) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				return FileInfo{}, errFileNotFound | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			switch z.Versions[latestIndex].Type { | 
					
						
							|  |  |  | 			case ObjectType: | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | 				fi, err = z.Versions[latestIndex].ObjectV2.ToFileInfo(volume, path) | 
					
						
							|  |  |  | 				fi.IsLatest = true | 
					
						
							|  |  |  | 				return fi, err | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			case DeleteType: | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | 				fi, err = z.Versions[latestIndex].DeleteMarker.ToFileInfo(volume, path) | 
					
						
							|  |  |  | 				fi.IsLatest = true | 
					
						
							|  |  |  | 				return fi, err | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			case LegacyType: | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | 				fi, err = z.Versions[latestIndex].ObjectV1.ToFileInfo(volume, path) | 
					
						
							|  |  |  | 				fi.IsLatest = true | 
					
						
							|  |  |  | 				return fi, err | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return FileInfo{}, errFileNotFound | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, version := range z.Versions { | 
					
						
							|  |  |  | 		if !version.Valid() { | 
					
						
							|  |  |  | 			logger.LogIf(GlobalContext, fmt.Errorf("invalid version detected %#v", version)) | 
					
						
							|  |  |  | 			if versionID == "" { | 
					
						
							|  |  |  | 				return FileInfo{}, errFileNotFound | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return FileInfo{}, errFileVersionNotFound | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		switch version.Type { | 
					
						
							|  |  |  | 		case ObjectType: | 
					
						
							|  |  |  | 			if bytes.Equal(version.ObjectV2.VersionID[:], uv[:]) { | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | 				fi, err = version.ObjectV2.ToFileInfo(volume, path) | 
					
						
							|  |  |  | 				fi.IsLatest = latestModTime.Equal(fi.ModTime) | 
					
						
							|  |  |  | 				return fi, err | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		case LegacyType: | 
					
						
							|  |  |  | 			if version.ObjectV1.VersionID == versionID { | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | 				fi, err = version.ObjectV1.ToFileInfo(volume, path) | 
					
						
							|  |  |  | 				fi.IsLatest = latestModTime.Equal(fi.ModTime) | 
					
						
							|  |  |  | 				return fi, err | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		case DeleteType: | 
					
						
							|  |  |  | 			if bytes.Equal(version.DeleteMarker.VersionID[:], uv[:]) { | 
					
						
							| 
									
										
										
										
											2020-07-05 03:25:53 +08:00
										 |  |  | 				fi, err = version.DeleteMarker.ToFileInfo(volume, path) | 
					
						
							|  |  |  | 				fi.IsLatest = latestModTime.Equal(fi.ModTime) | 
					
						
							|  |  |  | 				return fi, err | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if versionID == "" { | 
					
						
							|  |  |  | 		return FileInfo{}, errFileNotFound | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return FileInfo{}, errFileVersionNotFound | 
					
						
							|  |  |  | } |