github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/xl-storage-format-v2.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/binary"
    23  	"encoding/hex"
    24  	"errors"
    25  	"fmt"
    26  	"io"
    27  	"sort"
    28  	"strings"
    29  	"sync"
    30  	"time"
    31  
    32  	"github.com/cespare/xxhash/v2"
    33  	"github.com/google/uuid"
    34  	jsoniter "github.com/json-iterator/go"
    35  	"github.com/minio/minio/internal/bucket/lifecycle"
    36  	"github.com/minio/minio/internal/bucket/replication"
    37  	"github.com/minio/minio/internal/config/storageclass"
    38  	xhttp "github.com/minio/minio/internal/http"
    39  	"github.com/minio/minio/internal/logger"
    40  	"github.com/tinylib/msgp/msgp"
    41  )
    42  
    43  var (
    44  	// XL header specifies the format
    45  	xlHeader = [4]byte{'X', 'L', '2', ' '}
    46  
    47  	// Current version being written.
    48  	xlVersionCurrent [4]byte
    49  )
    50  
    51  //go:generate msgp -file=$GOFILE -unexported
    52  //go:generate stringer -type VersionType,ErasureAlgo -output=xl-storage-format-v2_string.go $GOFILE
    53  
    54  const (
    55  	// Breaking changes.
    56  	// Newer versions cannot be read by older software.
    57  	// This will prevent downgrades to incompatible versions.
    58  	xlVersionMajor = 1
    59  
    60  	// Non breaking changes.
    61  	// Bumping this is informational, but should be done
    62  	// if any change is made to the data stored, bumping this
    63  	// will allow to detect the exact version later.
    64  	xlVersionMinor = 3
    65  )
    66  
    67  func init() {
    68  	binary.LittleEndian.PutUint16(xlVersionCurrent[0:2], xlVersionMajor)
    69  	binary.LittleEndian.PutUint16(xlVersionCurrent[2:4], xlVersionMinor)
    70  }
    71  
    72  // The []journal contains all the different versions of the object.
    73  //
    74  // This array can have 3 kinds of objects:
    75  //
    76  // ``object``: If the object is uploaded the usual way: putobject, multipart-put, copyobject
    77  //
    78  // ``delete``: This is the delete-marker
    79  //
    80  // ``legacyObject``: This is the legacy object in xlV1 format, preserved until its overwritten
    81  //
    82  // The most recently updated element in the array is considered the latest version.
    83  
    84  // In addition to these we have a special kind called free-version. This is represented
    85  // using a delete-marker and MetaSys entries. It's used to track tiered content of a
    86  // deleted/overwritten version. This version is visible _only_to the scanner routine, for subsequent deletion.
    87  // This kind of tracking is necessary since a version's tiered content is deleted asynchronously.
    88  
    89  // Backend directory tree structure:
    90  // disk1/
    91  // └── bucket
    92  //     └── object
    93  //         ├── a192c1d5-9bd5-41fd-9a90-ab10e165398d
    94  //         │   └── part.1
    95  //         ├── c06e0436-f813-447e-ae5e-f2564df9dfd4
    96  //         │   └── part.1
    97  //         ├── df433928-2dcf-47b1-a786-43efa0f6b424
    98  //         │   └── part.1
    99  //         ├── legacy
   100  //         │   └── part.1
   101  //         └── xl.meta
   102  
   103  // VersionType defines the type of journal type of the current entry.
   104  type VersionType uint8
   105  
   106  // List of different types of journal type
   107  const (
   108  	invalidVersionType VersionType = 0
   109  	ObjectType         VersionType = 1
   110  	DeleteType         VersionType = 2
   111  	LegacyType         VersionType = 3
   112  	lastVersionType    VersionType = 4
   113  )
   114  
   115  func (e VersionType) valid() bool {
   116  	return e > invalidVersionType && e < lastVersionType
   117  }
   118  
   119  // ErasureAlgo defines common type of different erasure algorithms
   120  type ErasureAlgo uint8
   121  
   122  // List of currently supported erasure coding algorithms
   123  const (
   124  	invalidErasureAlgo ErasureAlgo = 0
   125  	ReedSolomon        ErasureAlgo = 1
   126  	lastErasureAlgo    ErasureAlgo = 2
   127  )
   128  
   129  func (e ErasureAlgo) valid() bool {
   130  	return e > invalidErasureAlgo && e < lastErasureAlgo
   131  }
   132  
   133  // ChecksumAlgo defines common type of different checksum algorithms
   134  type ChecksumAlgo uint8
   135  
   136  // List of currently supported checksum algorithms
   137  const (
   138  	invalidChecksumAlgo ChecksumAlgo = 0
   139  	HighwayHash         ChecksumAlgo = 1
   140  	lastChecksumAlgo    ChecksumAlgo = 2
   141  )
   142  
   143  func (e ChecksumAlgo) valid() bool {
   144  	return e > invalidChecksumAlgo && e < lastChecksumAlgo
   145  }
   146  
   147  // xlMetaV2DeleteMarker defines the data struct for the delete marker journal type
   148  type xlMetaV2DeleteMarker struct {
   149  	VersionID [16]byte          `json:"ID" msg:"ID"`                               // Version ID for delete marker
   150  	ModTime   int64             `json:"MTime" msg:"MTime"`                         // Object delete marker modified time
   151  	MetaSys   map[string][]byte `json:"MetaSys,omitempty" msg:"MetaSys,omitempty"` // Delete marker internal metadata
   152  }
   153  
   154  // xlMetaV2Object defines the data struct for object journal type
   155  type xlMetaV2Object struct {
   156  	VersionID          [16]byte          `json:"ID" msg:"ID"`                                    // Version ID
   157  	DataDir            [16]byte          `json:"DDir" msg:"DDir"`                                // Data dir ID
   158  	ErasureAlgorithm   ErasureAlgo       `json:"EcAlgo" msg:"EcAlgo"`                            // Erasure coding algorithm
   159  	ErasureM           int               `json:"EcM" msg:"EcM"`                                  // Erasure data blocks
   160  	ErasureN           int               `json:"EcN" msg:"EcN"`                                  // Erasure parity blocks
   161  	ErasureBlockSize   int64             `json:"EcBSize" msg:"EcBSize"`                          // Erasure block size
   162  	ErasureIndex       int               `json:"EcIndex" msg:"EcIndex"`                          // Erasure disk index
   163  	ErasureDist        []uint8           `json:"EcDist" msg:"EcDist"`                            // Erasure distribution
   164  	BitrotChecksumAlgo ChecksumAlgo      `json:"CSumAlgo" msg:"CSumAlgo"`                        // Bitrot checksum algo
   165  	PartNumbers        []int             `json:"PartNums" msg:"PartNums"`                        // Part Numbers
   166  	PartETags          []string          `json:"PartETags" msg:"PartETags,allownil"`             // Part ETags
   167  	PartSizes          []int64           `json:"PartSizes" msg:"PartSizes"`                      // Part Sizes
   168  	PartActualSizes    []int64           `json:"PartASizes,omitempty" msg:"PartASizes,allownil"` // Part ActualSizes (compression)
   169  	PartIndices        [][]byte          `json:"PartIndices,omitempty" msg:"PartIdx,omitempty"`  // Part Indexes (compression)
   170  	Size               int64             `json:"Size" msg:"Size"`                                // Object version size
   171  	ModTime            int64             `json:"MTime" msg:"MTime"`                              // Object version modified time
   172  	MetaSys            map[string][]byte `json:"MetaSys,omitempty" msg:"MetaSys,allownil"`       // Object version internal metadata
   173  	MetaUser           map[string]string `json:"MetaUsr,omitempty" msg:"MetaUsr,allownil"`       // Object version metadata set by user
   174  }
   175  
   176  // xlMetaV2Version describes the journal entry, Type defines
   177  // the current journal entry type other types might be nil based
   178  // on what Type field carries, it is imperative for the caller
   179  // to verify which journal type first before accessing rest of the fields.
   180  type xlMetaV2Version struct {
   181  	Type             VersionType           `json:"Type" msg:"Type"`
   182  	ObjectV1         *xlMetaV1Object       `json:"V1Obj,omitempty" msg:"V1Obj,omitempty"`
   183  	ObjectV2         *xlMetaV2Object       `json:"V2Obj,omitempty" msg:"V2Obj,omitempty"`
   184  	DeleteMarker     *xlMetaV2DeleteMarker `json:"DelObj,omitempty" msg:"DelObj,omitempty"`
   185  	WrittenByVersion uint64                `msg:"v"` // Tracks written by MinIO version
   186  }
   187  
   188  // xlFlags contains flags on the object.
   189  // This can be extended up to 64 bits without breaking compatibility.
   190  type xlFlags uint8
   191  
   192  const (
   193  	xlFlagFreeVersion xlFlags = 1 << iota
   194  	xlFlagUsesDataDir
   195  	xlFlagInlineData
   196  )
   197  
   198  func (x xlFlags) String() string {
   199  	var s strings.Builder
   200  	if x&xlFlagFreeVersion != 0 {
   201  		s.WriteString("FreeVersion")
   202  	}
   203  	if x&xlFlagUsesDataDir != 0 {
   204  		if s.Len() > 0 {
   205  			s.WriteByte(',')
   206  		}
   207  		s.WriteString("UsesDD")
   208  	}
   209  	if x&xlFlagInlineData != 0 {
   210  		if s.Len() > 0 {
   211  			s.WriteByte(',')
   212  		}
   213  		s.WriteString("Inline")
   214  	}
   215  	return s.String()
   216  }
   217  
   218  // checkXL2V1 will check if the metadata has correct header and is a known major version.
   219  // The remaining payload and versions are returned.
   220  func checkXL2V1(buf []byte) (payload []byte, major, minor uint16, err error) {
   221  	if len(buf) <= 8 {
   222  		return payload, 0, 0, fmt.Errorf("xlMeta: no data")
   223  	}
   224  
   225  	if !bytes.Equal(buf[:4], xlHeader[:]) {
   226  		return payload, 0, 0, fmt.Errorf("xlMeta: unknown XLv2 header, expected %v, got %v", xlHeader[:4], buf[:4])
   227  	}
   228  
   229  	if bytes.Equal(buf[4:8], []byte("1   ")) {
   230  		// Set as 1,0.
   231  		major, minor = 1, 0
   232  	} else {
   233  		major, minor = binary.LittleEndian.Uint16(buf[4:6]), binary.LittleEndian.Uint16(buf[6:8])
   234  	}
   235  	if major > xlVersionMajor {
   236  		return buf[8:], major, minor, fmt.Errorf("xlMeta: unknown major version %d found", major)
   237  	}
   238  
   239  	return buf[8:], major, minor, nil
   240  }
   241  
   242  func isXL2V1Format(buf []byte) bool {
   243  	_, _, _, err := checkXL2V1(buf)
   244  	return err == nil
   245  }
   246  
   247  //msgp:tuple xlMetaV2VersionHeader
   248  type xlMetaV2VersionHeader struct {
   249  	VersionID [16]byte
   250  	ModTime   int64
   251  	Signature [4]byte
   252  	Type      VersionType
   253  	Flags     xlFlags
   254  }
   255  
   256  func (x xlMetaV2VersionHeader) String() string {
   257  	return fmt.Sprintf("Type: %s, VersionID: %s, Signature: %s, ModTime: %s, Flags: %s",
   258  		x.Type.String(),
   259  		hex.EncodeToString(x.VersionID[:]),
   260  		hex.EncodeToString(x.Signature[:]),
   261  		time.Unix(0, x.ModTime),
   262  		x.Flags.String(),
   263  	)
   264  }
   265  
   266  // matchesNotStrict returns whether x and o have both have non-zero version,
   267  // their versions match and their type match.
   268  // If they have zero version, modtime must match.
   269  func (x xlMetaV2VersionHeader) matchesNotStrict(o xlMetaV2VersionHeader) bool {
   270  	if x.VersionID == [16]byte{} {
   271  		return x.VersionID == o.VersionID &&
   272  			x.Type == o.Type && o.ModTime == x.ModTime
   273  	}
   274  	return x.VersionID == o.VersionID &&
   275  		x.Type == o.Type
   276  }
   277  
   278  // sortsBefore can be used as a tiebreaker for stable sorting/selecting.
   279  // Returns false on ties.
   280  func (x xlMetaV2VersionHeader) sortsBefore(o xlMetaV2VersionHeader) bool {
   281  	if x == o {
   282  		return false
   283  	}
   284  	// Prefer newest modtime.
   285  	if x.ModTime != o.ModTime {
   286  		return x.ModTime > o.ModTime
   287  	}
   288  
   289  	// The following doesn't make too much sense, but we want sort to be consistent nonetheless.
   290  	// Prefer lower types
   291  	if x.Type != o.Type {
   292  		return x.Type < o.Type
   293  	}
   294  	// Consistent sort on signature
   295  	if v := bytes.Compare(x.Signature[:], o.Signature[:]); v != 0 {
   296  		return v > 0
   297  	}
   298  	// On ID mismatch
   299  	if v := bytes.Compare(x.VersionID[:], o.VersionID[:]); v != 0 {
   300  		return v > 0
   301  	}
   302  	// Flags
   303  	if x.Flags != o.Flags {
   304  		return x.Flags > o.Flags
   305  	}
   306  	return false
   307  }
   308  
   309  func (j xlMetaV2Version) getDataDir() string {
   310  	if j.Valid() {
   311  		switch j.Type {
   312  		case LegacyType:
   313  			return j.ObjectV1.DataDir
   314  		case ObjectType:
   315  			return uuid.UUID(j.ObjectV2.DataDir).String()
   316  		}
   317  	}
   318  	return ""
   319  }
   320  
   321  // Valid xl meta xlMetaV2Version is valid
   322  func (j xlMetaV2Version) Valid() bool {
   323  	if !j.Type.valid() {
   324  		return false
   325  	}
   326  	switch j.Type {
   327  	case LegacyType:
   328  		return j.ObjectV1 != nil &&
   329  			j.ObjectV1.valid()
   330  	case ObjectType:
   331  		return j.ObjectV2 != nil &&
   332  			j.ObjectV2.ErasureAlgorithm.valid() &&
   333  			j.ObjectV2.BitrotChecksumAlgo.valid() &&
   334  			isXLMetaErasureInfoValid(j.ObjectV2.ErasureM, j.ObjectV2.ErasureN) &&
   335  			j.ObjectV2.ModTime > 0
   336  	case DeleteType:
   337  		return j.DeleteMarker != nil &&
   338  			j.DeleteMarker.ModTime > 0
   339  	}
   340  	return false
   341  }
   342  
   343  // header will return a shallow header of the version.
   344  func (j *xlMetaV2Version) header() xlMetaV2VersionHeader {
   345  	var flags xlFlags
   346  	if j.FreeVersion() {
   347  		flags |= xlFlagFreeVersion
   348  	}
   349  	if j.Type == ObjectType && j.ObjectV2.UsesDataDir() {
   350  		flags |= xlFlagUsesDataDir
   351  	}
   352  	if j.Type == ObjectType && j.ObjectV2.InlineData() {
   353  		flags |= xlFlagInlineData
   354  	}
   355  	return xlMetaV2VersionHeader{
   356  		VersionID: j.getVersionID(),
   357  		ModTime:   j.getModTime().UnixNano(),
   358  		Signature: j.getSignature(),
   359  		Type:      j.Type,
   360  		Flags:     flags,
   361  	}
   362  }
   363  
   364  // FreeVersion returns true if x represents a free-version, false otherwise.
   365  func (x xlMetaV2VersionHeader) FreeVersion() bool {
   366  	return x.Flags&xlFlagFreeVersion != 0
   367  }
   368  
   369  // UsesDataDir returns true if this object version uses its data directory for
   370  // its contents and false otherwise.
   371  func (x xlMetaV2VersionHeader) UsesDataDir() bool {
   372  	return x.Flags&xlFlagUsesDataDir != 0
   373  }
   374  
   375  // InlineData returns whether inline data has been set.
   376  // Note that false does not mean there is no inline data,
   377  // only that it is unlikely.
   378  func (x xlMetaV2VersionHeader) InlineData() bool {
   379  	return x.Flags&xlFlagInlineData != 0
   380  }
   381  
   382  // signatureErr is a signature returned when an error occurs.
   383  var signatureErr = [4]byte{'e', 'r', 'r', 0}
   384  
   385  // getSignature will return a signature that is expected to be the same across all disks.
   386  func (j xlMetaV2Version) getSignature() [4]byte {
   387  	switch j.Type {
   388  	case ObjectType:
   389  		return j.ObjectV2.Signature()
   390  	case DeleteType:
   391  		return j.DeleteMarker.Signature()
   392  	case LegacyType:
   393  		return j.ObjectV1.Signature()
   394  	}
   395  	return signatureErr
   396  }
   397  
   398  // getModTime will return the ModTime of the underlying version.
   399  func (j xlMetaV2Version) getModTime() time.Time {
   400  	switch j.Type {
   401  	case ObjectType:
   402  		return time.Unix(0, j.ObjectV2.ModTime)
   403  	case DeleteType:
   404  		return time.Unix(0, j.DeleteMarker.ModTime)
   405  	case LegacyType:
   406  		return j.ObjectV1.Stat.ModTime
   407  	}
   408  	return time.Time{}
   409  }
   410  
   411  // getVersionID will return the versionID of the underlying version.
   412  func (j xlMetaV2Version) getVersionID() [16]byte {
   413  	switch j.Type {
   414  	case ObjectType:
   415  		return j.ObjectV2.VersionID
   416  	case DeleteType:
   417  		return j.DeleteMarker.VersionID
   418  	case LegacyType:
   419  		return [16]byte{}
   420  	}
   421  	return [16]byte{}
   422  }
   423  
   424  // ToFileInfo returns FileInfo of the underlying type.
   425  func (j *xlMetaV2Version) ToFileInfo(volume, path string, allParts bool) (fi FileInfo, err error) {
   426  	if j == nil {
   427  		return fi, errFileNotFound
   428  	}
   429  	switch j.Type {
   430  	case ObjectType:
   431  		fi, err = j.ObjectV2.ToFileInfo(volume, path, allParts)
   432  	case DeleteType:
   433  		fi, err = j.DeleteMarker.ToFileInfo(volume, path)
   434  	case LegacyType:
   435  		fi, err = j.ObjectV1.ToFileInfo(volume, path)
   436  	default:
   437  		return fi, errFileNotFound
   438  	}
   439  	fi.WrittenByVersion = j.WrittenByVersion
   440  	return fi, err
   441  }
   442  
   443  const (
   444  	xlHeaderVersion = 2
   445  	xlMetaVersion   = 2
   446  )
   447  
   448  func (j xlMetaV2DeleteMarker) ToFileInfo(volume, path string) (FileInfo, error) {
   449  	versionID := ""
   450  	var uv uuid.UUID
   451  	// check if the version is not "null"
   452  	if j.VersionID != uv {
   453  		versionID = uuid.UUID(j.VersionID).String()
   454  	}
   455  	fi := FileInfo{
   456  		Volume:    volume,
   457  		Name:      path,
   458  		ModTime:   time.Unix(0, j.ModTime).UTC(),
   459  		VersionID: versionID,
   460  		Deleted:   true,
   461  	}
   462  	fi.Metadata = make(map[string]string, len(j.MetaSys))
   463  	for k, v := range j.MetaSys {
   464  		fi.Metadata[k] = string(v)
   465  	}
   466  
   467  	fi.ReplicationState = GetInternalReplicationState(j.MetaSys)
   468  	if j.FreeVersion() {
   469  		fi.SetTierFreeVersion()
   470  		fi.TransitionTier = string(j.MetaSys[metaTierName])
   471  		fi.TransitionedObjName = string(j.MetaSys[metaTierObjName])
   472  		fi.TransitionVersionID = string(j.MetaSys[metaTierVersionID])
   473  	}
   474  
   475  	return fi, nil
   476  }
   477  
   478  // Signature will return a signature that is expected to be the same across all disks.
   479  func (j *xlMetaV2DeleteMarker) Signature() [4]byte {
   480  	// Shallow copy
   481  	c := *j
   482  
   483  	// Marshal metadata
   484  	crc := hashDeterministicBytes(c.MetaSys)
   485  	c.MetaSys = nil
   486  	if bts, err := c.MarshalMsg(metaDataPoolGet()); err == nil {
   487  		crc ^= xxhash.Sum64(bts)
   488  		metaDataPoolPut(bts)
   489  	}
   490  
   491  	// Combine upper and lower part
   492  	var tmp [4]byte
   493  	binary.LittleEndian.PutUint32(tmp[:], uint32(crc^(crc>>32)))
   494  	return tmp
   495  }
   496  
   497  // UsesDataDir returns true if this object version uses its data directory for
   498  // its contents and false otherwise.
   499  func (j xlMetaV2Object) UsesDataDir() bool {
   500  	// Skip if this version is not transitioned, i.e it uses its data directory.
   501  	if !bytes.Equal(j.MetaSys[metaTierStatus], []byte(lifecycle.TransitionComplete)) {
   502  		return true
   503  	}
   504  
   505  	// Check if this transitioned object has been restored on disk.
   506  	return isRestoredObjectOnDisk(j.MetaUser)
   507  }
   508  
   509  // InlineData returns whether inline data has been set.
   510  // Note that false does not mean there is no inline data,
   511  // only that it is unlikely.
   512  func (j xlMetaV2Object) InlineData() bool {
   513  	_, ok := j.MetaSys[ReservedMetadataPrefixLower+"inline-data"]
   514  	return ok
   515  }
   516  
   517  func (j *xlMetaV2Object) ResetInlineData() {
   518  	delete(j.MetaSys, ReservedMetadataPrefixLower+"inline-data")
   519  }
   520  
   521  const (
   522  	metaTierStatus    = ReservedMetadataPrefixLower + TransitionStatus
   523  	metaTierObjName   = ReservedMetadataPrefixLower + TransitionedObjectName
   524  	metaTierVersionID = ReservedMetadataPrefixLower + TransitionedVersionID
   525  	metaTierName      = ReservedMetadataPrefixLower + TransitionTier
   526  )
   527  
   528  func (j *xlMetaV2Object) SetTransition(fi FileInfo) {
   529  	j.MetaSys[metaTierStatus] = []byte(fi.TransitionStatus)
   530  	j.MetaSys[metaTierObjName] = []byte(fi.TransitionedObjName)
   531  	j.MetaSys[metaTierVersionID] = []byte(fi.TransitionVersionID)
   532  	j.MetaSys[metaTierName] = []byte(fi.TransitionTier)
   533  }
   534  
   535  func (j *xlMetaV2Object) RemoveRestoreHdrs() {
   536  	delete(j.MetaUser, xhttp.AmzRestore)
   537  	delete(j.MetaUser, xhttp.AmzRestoreExpiryDays)
   538  	delete(j.MetaUser, xhttp.AmzRestoreRequestDate)
   539  }
   540  
   541  // Signature will return a signature that is expected to be the same across all disks.
   542  func (j *xlMetaV2Object) Signature() [4]byte {
   543  	// Shallow copy
   544  	c := *j
   545  	// Zero fields that will vary across disks
   546  	c.ErasureIndex = 0
   547  
   548  	// Nil 0 size allownil, so we don't differentiate between nil and 0 len.
   549  	allEmpty := true
   550  	for _, tag := range c.PartETags {
   551  		if len(tag) != 0 {
   552  			allEmpty = false
   553  			break
   554  		}
   555  	}
   556  	if allEmpty {
   557  		c.PartETags = nil
   558  	}
   559  	if len(c.PartActualSizes) == 0 {
   560  		c.PartActualSizes = nil
   561  	}
   562  
   563  	// Get a 64 bit CRC
   564  	crc := hashDeterministicString(c.MetaUser)
   565  	crc ^= hashDeterministicBytes(c.MetaSys)
   566  
   567  	// Nil fields.
   568  	c.MetaSys = nil
   569  	c.MetaUser = nil
   570  
   571  	if bts, err := c.MarshalMsg(metaDataPoolGet()); err == nil {
   572  		crc ^= xxhash.Sum64(bts)
   573  		metaDataPoolPut(bts)
   574  	}
   575  
   576  	// Combine upper and lower part
   577  	var tmp [4]byte
   578  	binary.LittleEndian.PutUint32(tmp[:], uint32(crc^(crc>>32)))
   579  	return tmp
   580  }
   581  
   582  func (j xlMetaV2Object) ToFileInfo(volume, path string, allParts bool) (FileInfo, error) {
   583  	versionID := ""
   584  	var uv uuid.UUID
   585  	// check if the version is not "null"
   586  	if j.VersionID != uv {
   587  		versionID = uuid.UUID(j.VersionID).String()
   588  	}
   589  	fi := FileInfo{
   590  		Volume:    volume,
   591  		Name:      path,
   592  		Size:      j.Size,
   593  		ModTime:   time.Unix(0, j.ModTime).UTC(),
   594  		VersionID: versionID,
   595  	}
   596  	if allParts {
   597  		fi.Parts = make([]ObjectPartInfo, len(j.PartNumbers))
   598  		for i := range fi.Parts {
   599  			fi.Parts[i].Number = j.PartNumbers[i]
   600  			fi.Parts[i].Size = j.PartSizes[i]
   601  			if len(j.PartETags) == len(fi.Parts) {
   602  				fi.Parts[i].ETag = j.PartETags[i]
   603  			}
   604  			fi.Parts[i].ActualSize = j.PartActualSizes[i]
   605  			if len(j.PartIndices) == len(fi.Parts) {
   606  				fi.Parts[i].Index = j.PartIndices[i]
   607  			}
   608  		}
   609  	}
   610  
   611  	// fi.Erasure.Checksums - is left empty since we do not have any
   612  	// whole checksums for many years now, no need to allocate.
   613  
   614  	fi.Metadata = make(map[string]string, len(j.MetaUser)+len(j.MetaSys))
   615  	for k, v := range j.MetaUser {
   616  		// https://github.com/google/security-research/security/advisories/GHSA-76wf-9vgp-pj7w
   617  		if equals(k, xhttp.AmzMetaUnencryptedContentLength, xhttp.AmzMetaUnencryptedContentMD5) {
   618  			continue
   619  		}
   620  		if equals(k, "x-amz-storage-class") && v == storageclass.STANDARD {
   621  			continue
   622  		}
   623  
   624  		fi.Metadata[k] = v
   625  	}
   626  
   627  	tierFVIDKey := ReservedMetadataPrefixLower + tierFVID
   628  	tierFVMarkerKey := ReservedMetadataPrefixLower + tierFVMarker
   629  	for k, v := range j.MetaSys {
   630  		// Make sure we skip free-version-id, similar to AddVersion()
   631  		if len(k) > len(ReservedMetadataPrefixLower) && strings.EqualFold(k[:len(ReservedMetadataPrefixLower)], ReservedMetadataPrefixLower) {
   632  			// Skip tierFVID, tierFVMarker keys; it's used
   633  			// only for creating free-version.
   634  			switch k {
   635  			case tierFVIDKey, tierFVMarkerKey:
   636  				continue
   637  			}
   638  		}
   639  		if equals(k, "x-amz-storage-class") && string(v) == storageclass.STANDARD {
   640  			continue
   641  		}
   642  		switch {
   643  		case strings.HasPrefix(strings.ToLower(k), ReservedMetadataPrefixLower), equals(k, VersionPurgeStatusKey):
   644  			fi.Metadata[k] = string(v)
   645  		}
   646  	}
   647  	fi.ReplicationState = getInternalReplicationState(fi.Metadata)
   648  	fi.Deleted = !fi.VersionPurgeStatus().Empty()
   649  	replStatus := fi.ReplicationState.CompositeReplicationStatus()
   650  	if replStatus != "" {
   651  		fi.Metadata[xhttp.AmzBucketReplicationStatus] = string(replStatus)
   652  	}
   653  	fi.Erasure.Algorithm = j.ErasureAlgorithm.String()
   654  	fi.Erasure.Index = j.ErasureIndex
   655  	fi.Erasure.BlockSize = j.ErasureBlockSize
   656  	fi.Erasure.DataBlocks = j.ErasureM
   657  	fi.Erasure.ParityBlocks = j.ErasureN
   658  	fi.Erasure.Distribution = make([]int, len(j.ErasureDist))
   659  	for i := range j.ErasureDist {
   660  		fi.Erasure.Distribution[i] = int(j.ErasureDist[i])
   661  	}
   662  	fi.DataDir = uuid.UUID(j.DataDir).String()
   663  
   664  	if st, ok := j.MetaSys[metaTierStatus]; ok {
   665  		fi.TransitionStatus = string(st)
   666  	}
   667  	if o, ok := j.MetaSys[metaTierObjName]; ok {
   668  		fi.TransitionedObjName = string(o)
   669  	}
   670  	if rv, ok := j.MetaSys[metaTierVersionID]; ok {
   671  		fi.TransitionVersionID = string(rv)
   672  	}
   673  	if sc, ok := j.MetaSys[metaTierName]; ok {
   674  		fi.TransitionTier = string(sc)
   675  	}
   676  	if crcs := j.MetaSys[ReservedMetadataPrefixLower+"crc"]; len(crcs) > 0 {
   677  		fi.Checksum = crcs
   678  	}
   679  	return fi, nil
   680  }
   681  
   682  // Read at most this much on initial read.
   683  const metaDataReadDefault = 4 << 10
   684  
   685  // Return used metadata byte slices here.
   686  var metaDataPool = sync.Pool{New: func() interface{} { return make([]byte, 0, metaDataReadDefault) }}
   687  
   688  // metaDataPoolGet will return a byte slice with capacity at least metaDataReadDefault.
   689  // It will be length 0.
   690  func metaDataPoolGet() []byte {
   691  	return metaDataPool.Get().([]byte)[:0]
   692  }
   693  
   694  // metaDataPoolPut will put an unused small buffer back into the pool.
   695  func metaDataPoolPut(buf []byte) {
   696  	if cap(buf) >= metaDataReadDefault && cap(buf) < metaDataReadDefault*4 {
   697  		//nolint:staticcheck // SA6002 we are fine with the tiny alloc
   698  		metaDataPool.Put(buf)
   699  	}
   700  }
   701  
   702  // readXLMetaNoData will load the metadata, but skip data segments.
   703  // This should only be used when data is never interesting.
   704  // If data is not xlv2, it is returned in full.
   705  func readXLMetaNoData(r io.Reader, size int64) ([]byte, error) {
   706  	initial := size
   707  	hasFull := true
   708  	if initial > metaDataReadDefault {
   709  		initial = metaDataReadDefault
   710  		hasFull = false
   711  	}
   712  
   713  	buf := metaDataPoolGet()[:initial]
   714  	_, err := io.ReadFull(r, buf)
   715  	if err != nil {
   716  		return nil, fmt.Errorf("readXLMetaNoData(io.ReadFull): %w", err)
   717  	}
   718  	readMore := func(n int64) error {
   719  		has := int64(len(buf))
   720  		if has >= n {
   721  			return nil
   722  		}
   723  		if hasFull || n > size {
   724  			return io.ErrUnexpectedEOF
   725  		}
   726  		extra := n - has
   727  		if int64(cap(buf)) >= n {
   728  			// Extend since we have enough space.
   729  			buf = buf[:n]
   730  		} else {
   731  			buf = append(buf, make([]byte, extra)...)
   732  		}
   733  		_, err := io.ReadFull(r, buf[has:])
   734  		if err != nil {
   735  			if errors.Is(err, io.EOF) {
   736  				// Returned if we read nothing.
   737  				err = io.ErrUnexpectedEOF
   738  			}
   739  			return fmt.Errorf("readXLMetaNoData(readMore): %w", err)
   740  		}
   741  		return nil
   742  	}
   743  	tmp, major, minor, err := checkXL2V1(buf)
   744  	if err != nil {
   745  		err = readMore(size)
   746  		return buf, err
   747  	}
   748  	switch major {
   749  	case 1:
   750  		switch minor {
   751  		case 0:
   752  			err = readMore(size)
   753  			return buf, err
   754  		case 1, 2, 3:
   755  			sz, tmp, err := msgp.ReadBytesHeader(tmp)
   756  			if err != nil {
   757  				return nil, fmt.Errorf("readXLMetaNoData(read_meta): unknown metadata version %w", err)
   758  			}
   759  			want := int64(sz) + int64(len(buf)-len(tmp))
   760  
   761  			// v1.1 does not have CRC.
   762  			if minor < 2 {
   763  				if err := readMore(want); err != nil {
   764  					return nil, err
   765  				}
   766  				return buf[:want], nil
   767  			}
   768  
   769  			// CRC is variable length, so we need to truncate exactly that.
   770  			wantMax := want + msgp.Uint32Size
   771  			if wantMax > size {
   772  				wantMax = size
   773  			}
   774  			if err := readMore(wantMax); err != nil {
   775  				return nil, err
   776  			}
   777  
   778  			if int64(len(buf)) < want {
   779  				return nil, fmt.Errorf("buffer shorter than expected (buflen: %d, want: %d): %w", len(buf), want, errFileCorrupt)
   780  			}
   781  
   782  			tmp = buf[want:]
   783  			_, after, err := msgp.ReadUint32Bytes(tmp)
   784  			if err != nil {
   785  				return nil, fmt.Errorf("readXLMetaNoData(read_meta): unknown metadata version %w", err)
   786  			}
   787  			want += int64(len(tmp) - len(after))
   788  
   789  			return buf[:want], err
   790  
   791  		default:
   792  			return nil, errors.New("unknown minor metadata version")
   793  		}
   794  	default:
   795  		return nil, errors.New("unknown major metadata version")
   796  	}
   797  }
   798  
   799  func decodeXLHeaders(buf []byte) (versions int, headerV, metaV uint8, b []byte, err error) {
   800  	hdrVer, buf, err := msgp.ReadUint8Bytes(buf)
   801  	if err != nil {
   802  		return 0, 0, 0, buf, err
   803  	}
   804  	metaVer, buf, err := msgp.ReadUint8Bytes(buf)
   805  	if err != nil {
   806  		return 0, 0, 0, buf, err
   807  	}
   808  	if hdrVer > xlHeaderVersion {
   809  		return 0, 0, 0, buf, fmt.Errorf("decodeXLHeaders: Unknown xl header version %d", metaVer)
   810  	}
   811  	if metaVer > xlMetaVersion {
   812  		return 0, 0, 0, buf, fmt.Errorf("decodeXLHeaders: Unknown xl meta version %d", metaVer)
   813  	}
   814  	versions, buf, err = msgp.ReadIntBytes(buf)
   815  	if err != nil {
   816  		return 0, 0, 0, buf, err
   817  	}
   818  	if versions < 0 {
   819  		return 0, 0, 0, buf, fmt.Errorf("decodeXLHeaders: Negative version count %d", versions)
   820  	}
   821  	return versions, hdrVer, metaVer, buf, nil
   822  }
   823  
   824  // decodeVersions will decode a number of versions from a buffer
   825  // and perform a callback for each version in order, newest first.
   826  // Return errDoneForNow to stop processing and return nil.
   827  // Any non-nil error is returned.
   828  func decodeVersions(buf []byte, versions int, fn func(idx int, hdr, meta []byte) error) (err error) {
   829  	var tHdr, tMeta []byte // Zero copy bytes
   830  	for i := 0; i < versions; i++ {
   831  		tHdr, buf, err = msgp.ReadBytesZC(buf)
   832  		if err != nil {
   833  			return err
   834  		}
   835  		tMeta, buf, err = msgp.ReadBytesZC(buf)
   836  		if err != nil {
   837  			return err
   838  		}
   839  		if err = fn(i, tHdr, tMeta); err != nil {
   840  			if err == errDoneForNow {
   841  				err = nil
   842  			}
   843  			return err
   844  		}
   845  	}
   846  	return nil
   847  }
   848  
   849  // isIndexedMetaV2 returns non-nil result if metadata is indexed.
   850  // Returns 3x nil if not XLV2 or not indexed.
   851  // If indexed and unable to parse an error will be returned.
   852  func isIndexedMetaV2(buf []byte) (meta xlMetaBuf, data xlMetaInlineData, err error) {
   853  	buf, major, minor, err := checkXL2V1(buf)
   854  	if err != nil || major != 1 || minor < 3 {
   855  		return nil, nil, nil
   856  	}
   857  	meta, buf, err = msgp.ReadBytesZC(buf)
   858  	if err != nil {
   859  		return nil, nil, err
   860  	}
   861  	if crc, nbuf, err := msgp.ReadUint32Bytes(buf); err == nil {
   862  		// Read metadata CRC
   863  		buf = nbuf
   864  		if got := uint32(xxhash.Sum64(meta)); got != crc {
   865  			return nil, nil, fmt.Errorf("xlMetaV2.Load version(%d), CRC mismatch, want 0x%x, got 0x%x", minor, crc, got)
   866  		}
   867  	} else {
   868  		return nil, nil, err
   869  	}
   870  	data = buf
   871  	if data.validate() != nil {
   872  		data.repair()
   873  	}
   874  
   875  	return meta, data, nil
   876  }
   877  
   878  type xlMetaV2ShallowVersion struct {
   879  	header xlMetaV2VersionHeader
   880  	meta   []byte
   881  }
   882  
   883  //msgp:ignore xlMetaV2 xlMetaV2ShallowVersion
   884  
   885  type xlMetaV2 struct {
   886  	versions []xlMetaV2ShallowVersion
   887  
   888  	// data will contain raw data if any.
   889  	// data will be one or more versions indexed by versionID.
   890  	// To remove all data set to nil.
   891  	data xlMetaInlineData
   892  
   893  	// metadata version.
   894  	metaV uint8
   895  }
   896  
   897  // LoadOrConvert will load the metadata in the buffer.
   898  // If this is a legacy format, it will automatically be converted to XLV2.
   899  func (x *xlMetaV2) LoadOrConvert(buf []byte) error {
   900  	if isXL2V1Format(buf) {
   901  		return x.Load(buf)
   902  	}
   903  
   904  	xlMeta := &xlMetaV1Object{}
   905  	json := jsoniter.ConfigCompatibleWithStandardLibrary
   906  	if err := json.Unmarshal(buf, xlMeta); err != nil {
   907  		return errFileCorrupt
   908  	}
   909  	if len(x.versions) > 0 {
   910  		x.versions = x.versions[:0]
   911  	}
   912  	x.data = nil
   913  	x.metaV = xlMetaVersion
   914  	return x.AddLegacy(xlMeta)
   915  }
   916  
   917  // Load all versions of the stored data.
   918  // Note that references to the incoming buffer will be kept.
   919  func (x *xlMetaV2) Load(buf []byte) error {
   920  	if meta, data, err := isIndexedMetaV2(buf); err != nil {
   921  		return err
   922  	} else if meta != nil {
   923  		return x.loadIndexed(meta, data)
   924  	}
   925  	// Convert older format.
   926  	return x.loadLegacy(buf)
   927  }
   928  
   929  func (x *xlMetaV2) loadIndexed(buf xlMetaBuf, data xlMetaInlineData) error {
   930  	versions, headerV, metaV, buf, err := decodeXLHeaders(buf)
   931  	if err != nil {
   932  		return err
   933  	}
   934  	if cap(x.versions) < versions {
   935  		x.versions = make([]xlMetaV2ShallowVersion, 0, versions+1)
   936  	}
   937  	x.versions = x.versions[:versions]
   938  	x.data = data
   939  	x.metaV = metaV
   940  	if err = x.data.validate(); err != nil {
   941  		x.data.repair()
   942  		logger.LogIf(GlobalContext, fmt.Errorf("xlMetaV2.loadIndexed: data validation failed: %v. %d entries after repair", err, x.data.entries()))
   943  	}
   944  	return decodeVersions(buf, versions, func(i int, hdr, meta []byte) error {
   945  		ver := &x.versions[i]
   946  		_, err = ver.header.unmarshalV(headerV, hdr)
   947  		if err != nil {
   948  			return err
   949  		}
   950  		ver.meta = meta
   951  
   952  		// Fix inconsistent x-minio-internal-replication-timestamp by loading and reindexing.
   953  		if metaV < 2 && ver.header.Type == DeleteType {
   954  			// load (and convert) version.
   955  			version, err := x.getIdx(i)
   956  			if err == nil {
   957  				// Only reindex if set.
   958  				_, ok1 := version.DeleteMarker.MetaSys[ReservedMetadataPrefixLower+ReplicationTimestamp]
   959  				_, ok2 := version.DeleteMarker.MetaSys[ReservedMetadataPrefixLower+ReplicaTimestamp]
   960  				if ok1 || ok2 {
   961  					meta, err := version.MarshalMsg(make([]byte, 0, len(ver.meta)+10))
   962  					if err == nil {
   963  						// Override both if fine.
   964  						ver.header = version.header()
   965  						ver.meta = meta
   966  					}
   967  				}
   968  			}
   969  		}
   970  		return nil
   971  	})
   972  }
   973  
   974  // loadLegacy will load content prior to v1.3
   975  // Note that references to the incoming buffer will be kept.
   976  func (x *xlMetaV2) loadLegacy(buf []byte) error {
   977  	buf, major, minor, err := checkXL2V1(buf)
   978  	if err != nil {
   979  		return fmt.Errorf("xlMetaV2.Load %w", err)
   980  	}
   981  	var allMeta []byte
   982  	switch major {
   983  	case 1:
   984  		switch minor {
   985  		case 0:
   986  			allMeta = buf
   987  		case 1, 2:
   988  			v, buf, err := msgp.ReadBytesZC(buf)
   989  			if err != nil {
   990  				return fmt.Errorf("xlMetaV2.Load version(%d), bufLen(%d) %w", minor, len(buf), err)
   991  			}
   992  			if minor >= 2 {
   993  				if crc, nbuf, err := msgp.ReadUint32Bytes(buf); err == nil {
   994  					// Read metadata CRC (added in v2)
   995  					buf = nbuf
   996  					if got := uint32(xxhash.Sum64(v)); got != crc {
   997  						return fmt.Errorf("xlMetaV2.Load version(%d), CRC mismatch, want 0x%x, got 0x%x", minor, crc, got)
   998  					}
   999  				} else {
  1000  					return fmt.Errorf("xlMetaV2.Load version(%d), loading CRC: %w", minor, err)
  1001  				}
  1002  			}
  1003  
  1004  			allMeta = v
  1005  			// Add remaining data.
  1006  			x.data = buf
  1007  			if err = x.data.validate(); err != nil {
  1008  				x.data.repair()
  1009  				logger.LogIf(GlobalContext, fmt.Errorf("xlMetaV2.Load: data validation failed: %v. %d entries after repair", err, x.data.entries()))
  1010  			}
  1011  		default:
  1012  			return errors.New("unknown minor metadata version")
  1013  		}
  1014  	default:
  1015  		return errors.New("unknown major metadata version")
  1016  	}
  1017  	if allMeta == nil {
  1018  		return errFileCorrupt
  1019  	}
  1020  	// bts will shrink as we decode.
  1021  	bts := allMeta
  1022  	var field []byte
  1023  	var zb0001 uint32
  1024  	zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
  1025  	if err != nil {
  1026  		return msgp.WrapError(err, "loadLegacy.ReadMapHeader")
  1027  	}
  1028  
  1029  	var tmp xlMetaV2Version
  1030  	for zb0001 > 0 {
  1031  		zb0001--
  1032  		field, bts, err = msgp.ReadMapKeyZC(bts)
  1033  		if err != nil {
  1034  			return msgp.WrapError(err, "loadLegacy.ReadMapKey")
  1035  		}
  1036  		switch msgp.UnsafeString(field) {
  1037  		case "Versions":
  1038  			var zb0002 uint32
  1039  			zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
  1040  			if err != nil {
  1041  				return msgp.WrapError(err, "Versions")
  1042  			}
  1043  			if cap(x.versions) >= int(zb0002) {
  1044  				x.versions = (x.versions)[:zb0002]
  1045  			} else {
  1046  				x.versions = make([]xlMetaV2ShallowVersion, zb0002, zb0002+1)
  1047  			}
  1048  			for za0001 := range x.versions {
  1049  				start := len(allMeta) - len(bts)
  1050  				bts, err = tmp.unmarshalV(1, bts)
  1051  				if err != nil {
  1052  					return msgp.WrapError(err, "Versions", za0001)
  1053  				}
  1054  				end := len(allMeta) - len(bts)
  1055  				// We reference the marshaled data, so we don't have to re-marshal.
  1056  				x.versions[za0001] = xlMetaV2ShallowVersion{
  1057  					header: tmp.header(),
  1058  					meta:   allMeta[start:end],
  1059  				}
  1060  			}
  1061  		default:
  1062  			bts, err = msgp.Skip(bts)
  1063  			if err != nil {
  1064  				return msgp.WrapError(err, "loadLegacy.Skip")
  1065  			}
  1066  		}
  1067  	}
  1068  	x.metaV = 1 // Fixed for legacy conversions.
  1069  	x.sortByModTime()
  1070  	return nil
  1071  }
  1072  
  1073  // latestModtime returns the modtime of the latest version.
  1074  func (x *xlMetaV2) latestModtime() time.Time {
  1075  	if x == nil || len(x.versions) == 0 {
  1076  		return time.Time{}
  1077  	}
  1078  	return time.Unix(0, x.versions[0].header.ModTime)
  1079  }
  1080  
  1081  func (x *xlMetaV2) addVersion(ver xlMetaV2Version) error {
  1082  	modTime := ver.getModTime().UnixNano()
  1083  	if !ver.Valid() {
  1084  		return errors.New("attempted to add invalid version")
  1085  	}
  1086  	encoded, err := ver.MarshalMsg(nil)
  1087  	if err != nil {
  1088  		return err
  1089  	}
  1090  
  1091  	// returns error if we have exceeded configured object max versions
  1092  	if int64(len(x.versions)+1) > globalAPIConfig.getObjectMaxVersions() {
  1093  		return errMaxVersionsExceeded
  1094  	}
  1095  
  1096  	// Add space at the end.
  1097  	// Will have -1 modtime, so it will be inserted there.
  1098  	x.versions = append(x.versions, xlMetaV2ShallowVersion{header: xlMetaV2VersionHeader{ModTime: -1}})
  1099  
  1100  	// Linear search, we likely have to insert at front.
  1101  	for i, existing := range x.versions {
  1102  		if existing.header.ModTime <= modTime {
  1103  			// Insert at current idx. First move current back.
  1104  			copy(x.versions[i+1:], x.versions[i:])
  1105  			x.versions[i] = xlMetaV2ShallowVersion{
  1106  				header: ver.header(),
  1107  				meta:   encoded,
  1108  			}
  1109  			return nil
  1110  		}
  1111  	}
  1112  	return fmt.Errorf("addVersion: Internal error, unable to add version")
  1113  }
  1114  
  1115  // AppendTo will marshal the data in z and append it to the provided slice.
  1116  func (x *xlMetaV2) AppendTo(dst []byte) ([]byte, error) {
  1117  	// Header...
  1118  	sz := len(xlHeader) + len(xlVersionCurrent) + msgp.ArrayHeaderSize + len(dst) + 3*msgp.Uint32Size
  1119  	// Existing + Inline data
  1120  	sz += len(dst) + len(x.data)
  1121  	// Versions...
  1122  	for _, ver := range x.versions {
  1123  		sz += 32 + len(ver.meta)
  1124  	}
  1125  	if cap(dst) < sz {
  1126  		buf := make([]byte, len(dst), sz)
  1127  		copy(buf, dst)
  1128  		dst = buf
  1129  	}
  1130  	if err := x.data.validate(); err != nil {
  1131  		return nil, err
  1132  	}
  1133  
  1134  	dst = append(dst, xlHeader[:]...)
  1135  	dst = append(dst, xlVersionCurrent[:]...)
  1136  	// Add "bin 32" type header to always have enough space.
  1137  	// We will fill out the correct size when we know it.
  1138  	dst = append(dst, 0xc6, 0, 0, 0, 0)
  1139  	dataOffset := len(dst)
  1140  
  1141  	dst = msgp.AppendUint(dst, xlHeaderVersion)
  1142  	dst = msgp.AppendUint(dst, xlMetaVersion)
  1143  	dst = msgp.AppendInt(dst, len(x.versions))
  1144  
  1145  	tmp := metaDataPoolGet()
  1146  	defer metaDataPoolPut(tmp)
  1147  	for _, ver := range x.versions {
  1148  		var err error
  1149  
  1150  		// Add header
  1151  		tmp, err = ver.header.MarshalMsg(tmp[:0])
  1152  		if err != nil {
  1153  			return nil, err
  1154  		}
  1155  		dst = msgp.AppendBytes(dst, tmp)
  1156  
  1157  		// Add full meta
  1158  		dst = msgp.AppendBytes(dst, ver.meta)
  1159  	}
  1160  
  1161  	// Update size...
  1162  	binary.BigEndian.PutUint32(dst[dataOffset-4:dataOffset], uint32(len(dst)-dataOffset))
  1163  
  1164  	// Add CRC of metadata as fixed size (5 bytes)
  1165  	// Prior to v1.3 this was variable sized.
  1166  	tmp = tmp[:5]
  1167  	tmp[0] = 0xce // muint32
  1168  	binary.BigEndian.PutUint32(tmp[1:], uint32(xxhash.Sum64(dst[dataOffset:])))
  1169  	dst = append(dst, tmp[:5]...)
  1170  	return append(dst, x.data...), nil
  1171  }
  1172  
  1173  const emptyUUID = "00000000-0000-0000-0000-000000000000"
  1174  
  1175  func (x *xlMetaV2) findVersionStr(key string) (idx int, ver *xlMetaV2Version, err error) {
  1176  	if key == nullVersionID {
  1177  		key = ""
  1178  	}
  1179  	var u uuid.UUID
  1180  	if key != "" {
  1181  		u, err = uuid.Parse(key)
  1182  		if err != nil {
  1183  			return -1, nil, errFileVersionNotFound
  1184  		}
  1185  	}
  1186  	return x.findVersion(u)
  1187  }
  1188  
  1189  func (x *xlMetaV2) findVersion(key [16]byte) (idx int, ver *xlMetaV2Version, err error) {
  1190  	for i, ver := range x.versions {
  1191  		if key == ver.header.VersionID {
  1192  			obj, err := x.getIdx(i)
  1193  			return i, obj, err
  1194  		}
  1195  	}
  1196  	return -1, nil, errFileVersionNotFound
  1197  }
  1198  
  1199  func (x *xlMetaV2) getIdx(idx int) (ver *xlMetaV2Version, err error) {
  1200  	if idx < 0 || idx >= len(x.versions) {
  1201  		return nil, errFileNotFound
  1202  	}
  1203  	var dst xlMetaV2Version
  1204  	_, err = dst.unmarshalV(x.metaV, x.versions[idx].meta)
  1205  	if false {
  1206  		if err == nil && x.versions[idx].header.VersionID != dst.getVersionID() {
  1207  			panic(fmt.Sprintf("header: %x != object id: %x", x.versions[idx].header.VersionID, dst.getVersionID()))
  1208  		}
  1209  	}
  1210  	return &dst, err
  1211  }
  1212  
  1213  // setIdx will replace a version at a given index.
  1214  // Note that versions may become re-sorted if modtime changes.
  1215  func (x *xlMetaV2) setIdx(idx int, ver xlMetaV2Version) (err error) {
  1216  	if idx < 0 || idx >= len(x.versions) {
  1217  		return errFileNotFound
  1218  	}
  1219  	update := &x.versions[idx]
  1220  	prevMod := update.header.ModTime
  1221  	update.meta, err = ver.MarshalMsg(update.meta[:0:len(update.meta)])
  1222  	if err != nil {
  1223  		update.meta = nil
  1224  		return err
  1225  	}
  1226  	update.header = ver.header()
  1227  	if prevMod != update.header.ModTime {
  1228  		x.sortByModTime()
  1229  	}
  1230  	return nil
  1231  }
  1232  
  1233  // getDataDirs will return all data directories in the metadata
  1234  // as well as all version ids used for inline data.
  1235  func (x *xlMetaV2) getDataDirs() ([]string, error) {
  1236  	dds := make([]string, len(x.versions)*2)
  1237  	for i, ver := range x.versions {
  1238  		if ver.header.Type == DeleteType {
  1239  			continue
  1240  		}
  1241  
  1242  		obj, err := x.getIdx(i)
  1243  		if err != nil {
  1244  			return nil, err
  1245  		}
  1246  		switch ver.header.Type {
  1247  		case ObjectType:
  1248  			if obj.ObjectV2 == nil {
  1249  				return nil, errors.New("obj.ObjectV2 unexpectedly nil")
  1250  			}
  1251  			dds = append(dds, uuid.UUID(obj.ObjectV2.DataDir).String())
  1252  			if obj.ObjectV2.VersionID == [16]byte{} {
  1253  				dds = append(dds, nullVersionID)
  1254  			} else {
  1255  				dds = append(dds, uuid.UUID(obj.ObjectV2.VersionID).String())
  1256  			}
  1257  		case LegacyType:
  1258  			if obj.ObjectV1 == nil {
  1259  				return nil, errors.New("obj.ObjectV1 unexpectedly nil")
  1260  			}
  1261  			dds = append(dds, obj.ObjectV1.DataDir)
  1262  		}
  1263  	}
  1264  	return dds, nil
  1265  }
  1266  
  1267  // sortByModTime will sort versions by modtime in descending order,
  1268  // meaning index 0 will be latest version.
  1269  func (x *xlMetaV2) sortByModTime() {
  1270  	// Quick check
  1271  	if len(x.versions) <= 1 || sort.SliceIsSorted(x.versions, func(i, j int) bool {
  1272  		return x.versions[i].header.sortsBefore(x.versions[j].header)
  1273  	}) {
  1274  		return
  1275  	}
  1276  
  1277  	// We should sort.
  1278  	sort.Slice(x.versions, func(i, j int) bool {
  1279  		return x.versions[i].header.sortsBefore(x.versions[j].header)
  1280  	})
  1281  }
  1282  
  1283  // DeleteVersion deletes the version specified by version id.
  1284  // returns to the caller which dataDir to delete, also
  1285  // indicates if this is the last version.
  1286  func (x *xlMetaV2) DeleteVersion(fi FileInfo) (string, error) {
  1287  	// This is a situation where versionId is explicitly
  1288  	// specified as "null", as we do not save "null"
  1289  	// string it is considered empty. But empty also
  1290  	// means the version which matches will be purged.
  1291  	if fi.VersionID == nullVersionID {
  1292  		fi.VersionID = ""
  1293  	}
  1294  
  1295  	var uv uuid.UUID
  1296  	var err error
  1297  	if fi.VersionID != "" {
  1298  		uv, err = uuid.Parse(fi.VersionID)
  1299  		if err != nil {
  1300  			return "", errFileVersionNotFound
  1301  		}
  1302  	}
  1303  
  1304  	var ventry xlMetaV2Version
  1305  	if fi.Deleted {
  1306  		ventry = xlMetaV2Version{
  1307  			Type: DeleteType,
  1308  			DeleteMarker: &xlMetaV2DeleteMarker{
  1309  				VersionID: uv,
  1310  				ModTime:   fi.ModTime.UnixNano(),
  1311  				MetaSys:   make(map[string][]byte),
  1312  			},
  1313  			WrittenByVersion: globalVersionUnix,
  1314  		}
  1315  		if !ventry.Valid() {
  1316  			return "", errors.New("internal error: invalid version entry generated")
  1317  		}
  1318  	}
  1319  	updateVersion := false
  1320  	if fi.VersionPurgeStatus().Empty() && (fi.DeleteMarkerReplicationStatus() == "REPLICA" || fi.DeleteMarkerReplicationStatus().Empty()) {
  1321  		updateVersion = fi.MarkDeleted
  1322  	} else {
  1323  		// for replication scenario
  1324  		if fi.Deleted && fi.VersionPurgeStatus() != Complete {
  1325  			if !fi.VersionPurgeStatus().Empty() || fi.DeleteMarkerReplicationStatus().Empty() {
  1326  				updateVersion = true
  1327  			}
  1328  		}
  1329  		// object or delete-marker versioned delete is not complete
  1330  		if !fi.VersionPurgeStatus().Empty() && fi.VersionPurgeStatus() != Complete {
  1331  			updateVersion = true
  1332  		}
  1333  	}
  1334  
  1335  	if fi.Deleted {
  1336  		if !fi.DeleteMarkerReplicationStatus().Empty() {
  1337  			switch fi.DeleteMarkerReplicationStatus() {
  1338  			case replication.Replica:
  1339  				ventry.DeleteMarker.MetaSys[ReservedMetadataPrefixLower+ReplicaStatus] = []byte(fi.ReplicationState.ReplicaStatus)
  1340  				ventry.DeleteMarker.MetaSys[ReservedMetadataPrefixLower+ReplicaTimestamp] = []byte(fi.ReplicationState.ReplicaTimeStamp.UTC().Format(time.RFC3339Nano))
  1341  			default:
  1342  				ventry.DeleteMarker.MetaSys[ReservedMetadataPrefixLower+ReplicationStatus] = []byte(fi.ReplicationState.ReplicationStatusInternal)
  1343  				ventry.DeleteMarker.MetaSys[ReservedMetadataPrefixLower+ReplicationTimestamp] = []byte(fi.ReplicationState.ReplicationTimeStamp.UTC().Format(time.RFC3339Nano))
  1344  			}
  1345  		}
  1346  		if !fi.VersionPurgeStatus().Empty() {
  1347  			ventry.DeleteMarker.MetaSys[VersionPurgeStatusKey] = []byte(fi.ReplicationState.VersionPurgeStatusInternal)
  1348  		}
  1349  		for k, v := range fi.ReplicationState.ResetStatusesMap {
  1350  			ventry.DeleteMarker.MetaSys[k] = []byte(v)
  1351  		}
  1352  	}
  1353  
  1354  	for i, ver := range x.versions {
  1355  		if ver.header.VersionID != uv {
  1356  			continue
  1357  		}
  1358  		switch ver.header.Type {
  1359  		case LegacyType:
  1360  			ver, err := x.getIdx(i)
  1361  			if err != nil {
  1362  				return "", err
  1363  			}
  1364  			x.versions = append(x.versions[:i], x.versions[i+1:]...)
  1365  			if fi.Deleted {
  1366  				err = x.addVersion(ventry)
  1367  			}
  1368  			return ver.ObjectV1.DataDir, err
  1369  		case DeleteType:
  1370  			if updateVersion {
  1371  				ver, err := x.getIdx(i)
  1372  				if err != nil {
  1373  					return "", err
  1374  				}
  1375  				if len(ver.DeleteMarker.MetaSys) == 0 {
  1376  					ver.DeleteMarker.MetaSys = make(map[string][]byte)
  1377  				}
  1378  				if !fi.DeleteMarkerReplicationStatus().Empty() {
  1379  					switch fi.DeleteMarkerReplicationStatus() {
  1380  					case replication.Replica:
  1381  						ver.DeleteMarker.MetaSys[ReservedMetadataPrefixLower+ReplicaStatus] = []byte(fi.ReplicationState.ReplicaStatus)
  1382  						ver.DeleteMarker.MetaSys[ReservedMetadataPrefixLower+ReplicaTimestamp] = []byte(fi.ReplicationState.ReplicaTimeStamp.UTC().Format(time.RFC3339Nano))
  1383  					default:
  1384  						ver.DeleteMarker.MetaSys[ReservedMetadataPrefixLower+ReplicationStatus] = []byte(fi.ReplicationState.ReplicationStatusInternal)
  1385  						ver.DeleteMarker.MetaSys[ReservedMetadataPrefixLower+ReplicationTimestamp] = []byte(fi.ReplicationState.ReplicationTimeStamp.UTC().Format(time.RFC3339Nano))
  1386  					}
  1387  				}
  1388  				if !fi.VersionPurgeStatus().Empty() {
  1389  					ver.DeleteMarker.MetaSys[VersionPurgeStatusKey] = []byte(fi.ReplicationState.VersionPurgeStatusInternal)
  1390  				}
  1391  				for k, v := range fi.ReplicationState.ResetStatusesMap {
  1392  					ver.DeleteMarker.MetaSys[k] = []byte(v)
  1393  				}
  1394  				err = x.setIdx(i, *ver)
  1395  				return "", err
  1396  			}
  1397  			x.versions = append(x.versions[:i], x.versions[i+1:]...)
  1398  			if fi.MarkDeleted && (fi.VersionPurgeStatus().Empty() || (fi.VersionPurgeStatus() != Complete)) {
  1399  				err = x.addVersion(ventry)
  1400  			} else if fi.Deleted && uv.String() == emptyUUID {
  1401  				return "", x.addVersion(ventry)
  1402  			}
  1403  			return "", err
  1404  		case ObjectType:
  1405  			if updateVersion && !fi.Deleted {
  1406  				ver, err := x.getIdx(i)
  1407  				if err != nil {
  1408  					return "", err
  1409  				}
  1410  				ver.ObjectV2.MetaSys[VersionPurgeStatusKey] = []byte(fi.ReplicationState.VersionPurgeStatusInternal)
  1411  				for k, v := range fi.ReplicationState.ResetStatusesMap {
  1412  					ver.ObjectV2.MetaSys[k] = []byte(v)
  1413  				}
  1414  				err = x.setIdx(i, *ver)
  1415  				return uuid.UUID(ver.ObjectV2.DataDir).String(), err
  1416  			}
  1417  		}
  1418  	}
  1419  
  1420  	for i, version := range x.versions {
  1421  		if version.header.Type != ObjectType || version.header.VersionID != uv {
  1422  			continue
  1423  		}
  1424  		ver, err := x.getIdx(i)
  1425  		if err != nil {
  1426  			return "", err
  1427  		}
  1428  		switch {
  1429  		case fi.ExpireRestored:
  1430  			ver.ObjectV2.RemoveRestoreHdrs()
  1431  			err = x.setIdx(i, *ver)
  1432  		case fi.TransitionStatus == lifecycle.TransitionComplete:
  1433  			ver.ObjectV2.SetTransition(fi)
  1434  			ver.ObjectV2.ResetInlineData()
  1435  			err = x.setIdx(i, *ver)
  1436  		default:
  1437  			x.versions = append(x.versions[:i], x.versions[i+1:]...)
  1438  			// if uv has tiered content we add a
  1439  			// free-version to track it for
  1440  			// asynchronous deletion via scanner.
  1441  			if freeVersion, toFree := ver.ObjectV2.InitFreeVersion(fi); toFree {
  1442  				err = x.addVersion(freeVersion)
  1443  			}
  1444  		}
  1445  
  1446  		if fi.Deleted {
  1447  			err = x.addVersion(ventry)
  1448  		}
  1449  		if x.SharedDataDirCount(ver.ObjectV2.VersionID, ver.ObjectV2.DataDir) > 0 {
  1450  			// Found that another version references the same dataDir
  1451  			// we shouldn't remove it, and only remove the version instead
  1452  			return "", nil
  1453  		}
  1454  		return uuid.UUID(ver.ObjectV2.DataDir).String(), err
  1455  	}
  1456  
  1457  	if fi.Deleted {
  1458  		err = x.addVersion(ventry)
  1459  		return "", err
  1460  	}
  1461  	return "", errFileVersionNotFound
  1462  }
  1463  
  1464  // xlMetaDataDirDecoder is a shallow decoder for decoding object datadir only.
  1465  type xlMetaDataDirDecoder struct {
  1466  	ObjectV2 *struct {
  1467  		DataDir [16]byte `msg:"DDir"` // Data dir ID
  1468  	} `msg:"V2Obj,omitempty"`
  1469  }
  1470  
  1471  // UpdateObjectVersion updates metadata and modTime for a given
  1472  // versionID, NOTE: versionID must be valid and should exist -
  1473  // and must not be a DeleteMarker or legacy object, if no
  1474  // versionID is specified 'null' versionID is updated instead.
  1475  //
  1476  // It is callers responsibility to set correct versionID, this
  1477  // function shouldn't be further extended to update immutable
  1478  // values such as ErasureInfo, ChecksumInfo.
  1479  //
  1480  // Metadata is only updated to new values, existing values
  1481  // stay as is, if you wish to update all values you should
  1482  // update all metadata freshly before calling this function
  1483  // in-case you wish to clear existing metadata.
  1484  func (x *xlMetaV2) UpdateObjectVersion(fi FileInfo) error {
  1485  	if fi.VersionID == "" {
  1486  		// this means versioning is not yet
  1487  		// enabled or suspend i.e all versions
  1488  		// are basically default value i.e "null"
  1489  		fi.VersionID = nullVersionID
  1490  	}
  1491  
  1492  	var uv uuid.UUID
  1493  	var err error
  1494  	if fi.VersionID != "" && fi.VersionID != nullVersionID {
  1495  		uv, err = uuid.Parse(fi.VersionID)
  1496  		if err != nil {
  1497  			return err
  1498  		}
  1499  	}
  1500  
  1501  	for i, version := range x.versions {
  1502  		switch version.header.Type {
  1503  		case LegacyType, DeleteType:
  1504  			if version.header.VersionID == uv {
  1505  				return errMethodNotAllowed
  1506  			}
  1507  		case ObjectType:
  1508  			if version.header.VersionID == uv {
  1509  				ver, err := x.getIdx(i)
  1510  				if err != nil {
  1511  					return err
  1512  				}
  1513  				for k, v := range fi.Metadata {
  1514  					if len(k) > len(ReservedMetadataPrefixLower) && strings.EqualFold(k[:len(ReservedMetadataPrefixLower)], ReservedMetadataPrefixLower) {
  1515  						ver.ObjectV2.MetaSys[k] = []byte(v)
  1516  					} else {
  1517  						ver.ObjectV2.MetaUser[k] = v
  1518  					}
  1519  				}
  1520  				if !fi.ModTime.IsZero() {
  1521  					ver.ObjectV2.ModTime = fi.ModTime.UnixNano()
  1522  				}
  1523  				return x.setIdx(i, *ver)
  1524  			}
  1525  		}
  1526  	}
  1527  
  1528  	return errFileVersionNotFound
  1529  }
  1530  
  1531  // AddVersion adds a new version
  1532  func (x *xlMetaV2) AddVersion(fi FileInfo) error {
  1533  	if fi.VersionID == "" {
  1534  		// this means versioning is not yet
  1535  		// enabled or suspend i.e all versions
  1536  		// are basically default value i.e "null"
  1537  		fi.VersionID = nullVersionID
  1538  	}
  1539  
  1540  	var uv uuid.UUID
  1541  	var err error
  1542  	if fi.VersionID != "" && fi.VersionID != nullVersionID {
  1543  		uv, err = uuid.Parse(fi.VersionID)
  1544  		if err != nil {
  1545  			return err
  1546  		}
  1547  	}
  1548  
  1549  	var dd uuid.UUID
  1550  	if fi.DataDir != "" {
  1551  		dd, err = uuid.Parse(fi.DataDir)
  1552  		if err != nil {
  1553  			return err
  1554  		}
  1555  	}
  1556  
  1557  	ventry := xlMetaV2Version{
  1558  		WrittenByVersion: globalVersionUnix,
  1559  	}
  1560  
  1561  	if fi.Deleted {
  1562  		ventry.Type = DeleteType
  1563  		ventry.DeleteMarker = &xlMetaV2DeleteMarker{
  1564  			VersionID: uv,
  1565  			ModTime:   fi.ModTime.UnixNano(),
  1566  			MetaSys:   make(map[string][]byte),
  1567  		}
  1568  	} else {
  1569  		ventry.Type = ObjectType
  1570  		ventry.ObjectV2 = &xlMetaV2Object{
  1571  			VersionID:          uv,
  1572  			DataDir:            dd,
  1573  			Size:               fi.Size,
  1574  			ModTime:            fi.ModTime.UnixNano(),
  1575  			ErasureAlgorithm:   ReedSolomon,
  1576  			ErasureM:           fi.Erasure.DataBlocks,
  1577  			ErasureN:           fi.Erasure.ParityBlocks,
  1578  			ErasureBlockSize:   fi.Erasure.BlockSize,
  1579  			ErasureIndex:       fi.Erasure.Index,
  1580  			BitrotChecksumAlgo: HighwayHash,
  1581  			ErasureDist:        make([]uint8, len(fi.Erasure.Distribution)),
  1582  			PartNumbers:        make([]int, len(fi.Parts)),
  1583  			PartETags:          nil,
  1584  			PartSizes:          make([]int64, len(fi.Parts)),
  1585  			PartActualSizes:    make([]int64, len(fi.Parts)),
  1586  			MetaSys:            make(map[string][]byte),
  1587  			MetaUser:           make(map[string]string, len(fi.Metadata)),
  1588  		}
  1589  		for i := range fi.Parts {
  1590  			// Only add etags if any.
  1591  			if fi.Parts[i].ETag != "" {
  1592  				ventry.ObjectV2.PartETags = make([]string, len(fi.Parts))
  1593  				break
  1594  			}
  1595  		}
  1596  		for i := range fi.Parts {
  1597  			// Only add indices if any.
  1598  			if len(fi.Parts[i].Index) > 0 {
  1599  				ventry.ObjectV2.PartIndices = make([][]byte, len(fi.Parts))
  1600  				break
  1601  			}
  1602  		}
  1603  		for i := range fi.Erasure.Distribution {
  1604  			ventry.ObjectV2.ErasureDist[i] = uint8(fi.Erasure.Distribution[i])
  1605  		}
  1606  
  1607  		for i := range fi.Parts {
  1608  			ventry.ObjectV2.PartSizes[i] = fi.Parts[i].Size
  1609  			if len(ventry.ObjectV2.PartETags) > 0 && fi.Parts[i].ETag != "" {
  1610  				ventry.ObjectV2.PartETags[i] = fi.Parts[i].ETag
  1611  			}
  1612  			ventry.ObjectV2.PartNumbers[i] = fi.Parts[i].Number
  1613  			ventry.ObjectV2.PartActualSizes[i] = fi.Parts[i].ActualSize
  1614  			if len(ventry.ObjectV2.PartIndices) > 0 {
  1615  				ventry.ObjectV2.PartIndices[i] = fi.Parts[i].Index
  1616  			}
  1617  		}
  1618  
  1619  		tierFVIDKey := ReservedMetadataPrefixLower + tierFVID
  1620  		tierFVMarkerKey := ReservedMetadataPrefixLower + tierFVMarker
  1621  		for k, v := range fi.Metadata {
  1622  			if len(k) > len(ReservedMetadataPrefixLower) && strings.EqualFold(k[:len(ReservedMetadataPrefixLower)], ReservedMetadataPrefixLower) {
  1623  				// Skip tierFVID, tierFVMarker keys; it's used
  1624  				// only for creating free-version.
  1625  				// Also skip xMinIOHealing, xMinIODataMov as used only in RenameData
  1626  				switch k {
  1627  				case tierFVIDKey, tierFVMarkerKey, xMinIOHealing, xMinIODataMov:
  1628  					continue
  1629  				}
  1630  
  1631  				ventry.ObjectV2.MetaSys[k] = []byte(v)
  1632  			} else {
  1633  				ventry.ObjectV2.MetaUser[k] = v
  1634  			}
  1635  		}
  1636  
  1637  		// If asked to save data.
  1638  		if len(fi.Data) > 0 || fi.Size == 0 {
  1639  			x.data.replace(fi.VersionID, fi.Data)
  1640  		}
  1641  
  1642  		if fi.TransitionStatus != "" {
  1643  			ventry.ObjectV2.MetaSys[metaTierStatus] = []byte(fi.TransitionStatus)
  1644  		}
  1645  		if fi.TransitionedObjName != "" {
  1646  			ventry.ObjectV2.MetaSys[metaTierObjName] = []byte(fi.TransitionedObjName)
  1647  		}
  1648  		if fi.TransitionVersionID != "" {
  1649  			ventry.ObjectV2.MetaSys[metaTierVersionID] = []byte(fi.TransitionVersionID)
  1650  		}
  1651  		if fi.TransitionTier != "" {
  1652  			ventry.ObjectV2.MetaSys[metaTierName] = []byte(fi.TransitionTier)
  1653  		}
  1654  		if len(fi.Checksum) > 0 {
  1655  			ventry.ObjectV2.MetaSys[ReservedMetadataPrefixLower+"crc"] = fi.Checksum
  1656  		}
  1657  	}
  1658  
  1659  	if !ventry.Valid() {
  1660  		return errors.New("internal error: invalid version entry generated")
  1661  	}
  1662  
  1663  	// Check if we should replace first.
  1664  	for i := range x.versions {
  1665  		if x.versions[i].header.VersionID != uv {
  1666  			continue
  1667  		}
  1668  		switch x.versions[i].header.Type {
  1669  		case LegacyType:
  1670  			// This would convert legacy type into new ObjectType
  1671  			// this means that we are basically purging the `null`
  1672  			// version of the object.
  1673  			return x.setIdx(i, ventry)
  1674  		case ObjectType:
  1675  			return x.setIdx(i, ventry)
  1676  		case DeleteType:
  1677  			// Allowing delete marker to replaced with proper
  1678  			// object data type as well, this is not S3 complaint
  1679  			// behavior but kept here for future flexibility.
  1680  			return x.setIdx(i, ventry)
  1681  		}
  1682  	}
  1683  
  1684  	// We did not find it, add it.
  1685  	return x.addVersion(ventry)
  1686  }
  1687  
  1688  func (x *xlMetaV2) SharedDataDirCount(versionID [16]byte, dataDir [16]byte) int {
  1689  	// v2 object is inlined, if it is skip dataDir share check.
  1690  	if x.data.entries() > 0 && x.data.find(uuid.UUID(versionID).String()) != nil {
  1691  		return 0
  1692  	}
  1693  	var sameDataDirCount int
  1694  	var decoded xlMetaDataDirDecoder
  1695  	for _, version := range x.versions {
  1696  		if version.header.Type != ObjectType || version.header.VersionID == versionID || !version.header.UsesDataDir() {
  1697  			continue
  1698  		}
  1699  		_, err := decoded.UnmarshalMsg(version.meta)
  1700  		if err != nil || decoded.ObjectV2 == nil || decoded.ObjectV2.DataDir != dataDir {
  1701  			continue
  1702  		}
  1703  		sameDataDirCount++
  1704  	}
  1705  	return sameDataDirCount
  1706  }
  1707  
  1708  func (x *xlMetaV2) SharedDataDirCountStr(versionID, dataDir string) int {
  1709  	var (
  1710  		uv   uuid.UUID
  1711  		ddir uuid.UUID
  1712  		err  error
  1713  	)
  1714  	if versionID == nullVersionID {
  1715  		versionID = ""
  1716  	}
  1717  	if versionID != "" {
  1718  		uv, err = uuid.Parse(versionID)
  1719  		if err != nil {
  1720  			return 0
  1721  		}
  1722  	}
  1723  	ddir, err = uuid.Parse(dataDir)
  1724  	if err != nil {
  1725  		return 0
  1726  	}
  1727  	return x.SharedDataDirCount(uv, ddir)
  1728  }
  1729  
  1730  // AddLegacy adds a legacy version, is only called when no prior
  1731  // versions exist, safe to use it by only one function in xl-storage(RenameData)
  1732  func (x *xlMetaV2) AddLegacy(m *xlMetaV1Object) error {
  1733  	if !m.valid() {
  1734  		return errFileCorrupt
  1735  	}
  1736  	m.VersionID = nullVersionID
  1737  
  1738  	return x.addVersion(xlMetaV2Version{ObjectV1: m, Type: LegacyType, WrittenByVersion: globalVersionUnix})
  1739  }
  1740  
  1741  // ToFileInfo converts xlMetaV2 into a common FileInfo datastructure
  1742  // for consumption across callers.
  1743  func (x xlMetaV2) ToFileInfo(volume, path, versionID string, inclFreeVers, allParts bool) (fi FileInfo, err error) {
  1744  	var uv uuid.UUID
  1745  	if versionID != "" && versionID != nullVersionID {
  1746  		uv, err = uuid.Parse(versionID)
  1747  		if err != nil {
  1748  			logger.LogIf(GlobalContext, fmt.Errorf("invalid versionID specified %s", versionID))
  1749  			return fi, errFileVersionNotFound
  1750  		}
  1751  	}
  1752  	var succModTime int64
  1753  	isLatest := true
  1754  	nonFreeVersions := len(x.versions)
  1755  
  1756  	var (
  1757  		freeFi    FileInfo
  1758  		freeFound bool
  1759  	)
  1760  	found := false
  1761  	for _, ver := range x.versions {
  1762  		header := &ver.header
  1763  		// skip listing free-version unless explicitly requested via versionID
  1764  		if header.FreeVersion() {
  1765  			nonFreeVersions--
  1766  			// remember the latest free version; will return this FileInfo if no non-free version remain
  1767  			var freeVersion xlMetaV2Version
  1768  			if inclFreeVers && !freeFound {
  1769  				// ignore unmarshalling errors, will return errFileNotFound in that case
  1770  				if _, err := freeVersion.unmarshalV(x.metaV, ver.meta); err == nil {
  1771  					if freeFi, err = freeVersion.ToFileInfo(volume, path, allParts); err == nil {
  1772  						freeFi.IsLatest = true // when this is returned, it would be the latest free version remaining.
  1773  						freeFound = true
  1774  					}
  1775  				}
  1776  			}
  1777  
  1778  			if header.VersionID != uv {
  1779  				continue
  1780  			}
  1781  		}
  1782  		if found {
  1783  			continue
  1784  		}
  1785  
  1786  		// We need a specific version, skip...
  1787  		if versionID != "" && uv != header.VersionID {
  1788  			isLatest = false
  1789  			succModTime = header.ModTime
  1790  			continue
  1791  		}
  1792  
  1793  		// We found what we need.
  1794  		found = true
  1795  		var version xlMetaV2Version
  1796  		if _, err := version.unmarshalV(x.metaV, ver.meta); err != nil {
  1797  			return fi, err
  1798  		}
  1799  		if fi, err = version.ToFileInfo(volume, path, allParts); err != nil {
  1800  			return fi, err
  1801  		}
  1802  		fi.IsLatest = isLatest
  1803  		if succModTime != 0 {
  1804  			fi.SuccessorModTime = time.Unix(0, succModTime)
  1805  		}
  1806  	}
  1807  	if !found {
  1808  		if versionID == "" {
  1809  			if inclFreeVers && nonFreeVersions == 0 {
  1810  				if freeFound {
  1811  					return freeFi, nil
  1812  				}
  1813  			}
  1814  			return FileInfo{}, errFileNotFound
  1815  		}
  1816  
  1817  		return FileInfo{}, errFileVersionNotFound
  1818  	}
  1819  	fi.NumVersions = nonFreeVersions
  1820  	return fi, err
  1821  }
  1822  
  1823  // ListVersions lists current versions, and current deleted
  1824  // versions returns error for unexpected entries.
  1825  // showPendingDeletes is set to true if ListVersions needs to list objects marked deleted
  1826  // but waiting to be replicated
  1827  func (x xlMetaV2) ListVersions(volume, path string, allParts bool) ([]FileInfo, error) {
  1828  	versions := make([]FileInfo, 0, len(x.versions))
  1829  	var err error
  1830  
  1831  	var dst xlMetaV2Version
  1832  	for _, version := range x.versions {
  1833  		_, err = dst.unmarshalV(x.metaV, version.meta)
  1834  		if err != nil {
  1835  			return versions, err
  1836  		}
  1837  		fi, err := dst.ToFileInfo(volume, path, allParts)
  1838  		if err != nil {
  1839  			return versions, err
  1840  		}
  1841  		fi.NumVersions = len(x.versions)
  1842  		versions = append(versions, fi)
  1843  	}
  1844  
  1845  	for i := range versions {
  1846  		versions[i].NumVersions = len(versions)
  1847  		if i > 0 {
  1848  			versions[i].SuccessorModTime = versions[i-1].ModTime
  1849  		}
  1850  	}
  1851  	if len(versions) > 0 {
  1852  		versions[0].IsLatest = true
  1853  	}
  1854  	return versions, nil
  1855  }
  1856  
  1857  // mergeXLV2Versions will merge all versions, typically from different disks
  1858  // that have at least quorum entries in all metas.
  1859  // Each version slice should be sorted.
  1860  // Quorum must be the minimum number of matching metadata files.
  1861  // Quorum should be > 1 and <= len(versions).
  1862  // If strict is set to false, entries that match type
  1863  func mergeXLV2Versions(quorum int, strict bool, requestedVersions int, versions ...[]xlMetaV2ShallowVersion) (merged []xlMetaV2ShallowVersion) {
  1864  	if quorum <= 0 {
  1865  		quorum = 1
  1866  	}
  1867  	if len(versions) < quorum || len(versions) == 0 {
  1868  		return nil
  1869  	}
  1870  	if len(versions) == 1 {
  1871  		return versions[0]
  1872  	}
  1873  	if quorum == 1 {
  1874  		// No need for non-strict checks if quorum is 1.
  1875  		strict = true
  1876  	}
  1877  	// Shallow copy input
  1878  	versions = append(make([][]xlMetaV2ShallowVersion, 0, len(versions)), versions...)
  1879  
  1880  	var nVersions int // captures all non-free versions
  1881  
  1882  	// Our result
  1883  	merged = make([]xlMetaV2ShallowVersion, 0, len(versions[0]))
  1884  	tops := make([]xlMetaV2ShallowVersion, len(versions))
  1885  	for {
  1886  		// Step 1 create slice with all top versions.
  1887  		tops = tops[:0]
  1888  		var topSig xlMetaV2VersionHeader
  1889  		consistent := true // Are all signatures consistent (shortcut)
  1890  		for _, vers := range versions {
  1891  			if len(vers) == 0 {
  1892  				consistent = false
  1893  				continue
  1894  			}
  1895  			ver := vers[0]
  1896  			if len(tops) == 0 {
  1897  				consistent = true
  1898  				topSig = ver.header
  1899  			} else {
  1900  				consistent = consistent && ver.header == topSig
  1901  			}
  1902  			tops = append(tops, vers[0])
  1903  		}
  1904  
  1905  		// Check if done...
  1906  		if len(tops) < quorum {
  1907  			// We couldn't gather enough for quorum
  1908  			break
  1909  		}
  1910  
  1911  		var latest xlMetaV2ShallowVersion
  1912  		if consistent {
  1913  			// All had the same signature, easy.
  1914  			latest = tops[0]
  1915  			merged = append(merged, latest)
  1916  
  1917  			// Calculate latest 'n' non-free versions.
  1918  			if !latest.header.FreeVersion() {
  1919  				nVersions++
  1920  			}
  1921  
  1922  		} else {
  1923  			// Find latest.
  1924  			var latestCount int
  1925  			for i, ver := range tops {
  1926  				if ver.header == latest.header {
  1927  					latestCount++
  1928  					continue
  1929  				}
  1930  				if i == 0 || ver.header.sortsBefore(latest.header) {
  1931  					switch {
  1932  					case i == 0 || latestCount == 0:
  1933  						latestCount = 1
  1934  					case !strict && ver.header.matchesNotStrict(latest.header):
  1935  						latestCount++
  1936  					default:
  1937  						latestCount = 1
  1938  					}
  1939  					latest = ver
  1940  					continue
  1941  				}
  1942  
  1943  				// Mismatch, but older.
  1944  				if latestCount > 0 && !strict && ver.header.matchesNotStrict(latest.header) {
  1945  					latestCount++
  1946  					continue
  1947  				}
  1948  				if latestCount > 0 && ver.header.VersionID == latest.header.VersionID {
  1949  					// Version IDs match, but otherwise unable to resolve.
  1950  					// We are either strict, or don't have enough information to match.
  1951  					// Switch to a pure counting algo.
  1952  					x := make(map[xlMetaV2VersionHeader]int, len(tops))
  1953  					for _, a := range tops {
  1954  						if a.header.VersionID != ver.header.VersionID {
  1955  							continue
  1956  						}
  1957  						if !strict {
  1958  							a.header.Signature = [4]byte{}
  1959  						}
  1960  						x[a.header]++
  1961  					}
  1962  					latestCount = 0
  1963  					for k, v := range x {
  1964  						if v < latestCount {
  1965  							continue
  1966  						}
  1967  						if v == latestCount && latest.header.sortsBefore(k) {
  1968  							// Tiebreak, use sort.
  1969  							continue
  1970  						}
  1971  						for _, a := range tops {
  1972  							hdr := a.header
  1973  							if !strict {
  1974  								hdr.Signature = [4]byte{}
  1975  							}
  1976  							if hdr == k {
  1977  								latest = a
  1978  							}
  1979  						}
  1980  						latestCount = v
  1981  					}
  1982  					break
  1983  				}
  1984  			}
  1985  			if latestCount >= quorum {
  1986  				merged = append(merged, latest)
  1987  
  1988  				// Calculate latest 'n' non-free versions.
  1989  				if !latest.header.FreeVersion() {
  1990  					nVersions++
  1991  				}
  1992  			}
  1993  		}
  1994  
  1995  		// Remove from all streams up until latest modtime or if selected.
  1996  		for i, vers := range versions {
  1997  			for _, ver := range vers {
  1998  				// Truncate later modtimes, not selected.
  1999  				if ver.header.ModTime > latest.header.ModTime {
  2000  					versions[i] = versions[i][1:]
  2001  					continue
  2002  				}
  2003  				// Truncate matches
  2004  				if ver.header == latest.header {
  2005  					versions[i] = versions[i][1:]
  2006  					continue
  2007  				}
  2008  
  2009  				// Truncate non-empty version and type matches
  2010  				if latest.header.VersionID == ver.header.VersionID {
  2011  					versions[i] = versions[i][1:]
  2012  					continue
  2013  				}
  2014  				// Skip versions with version id we already emitted.
  2015  				for _, mergedV := range merged {
  2016  					if ver.header.VersionID == mergedV.header.VersionID {
  2017  						versions[i] = versions[i][1:]
  2018  						continue
  2019  					}
  2020  				}
  2021  				// Keep top entry (and remaining)...
  2022  				break
  2023  			}
  2024  		}
  2025  
  2026  		if requestedVersions > 0 && requestedVersions == nVersions {
  2027  			merged = append(merged, versions[0]...)
  2028  			break
  2029  		}
  2030  	}
  2031  
  2032  	// Sanity check. Enable if duplicates show up.
  2033  	if false {
  2034  		found := make(map[[16]byte]struct{})
  2035  		for _, ver := range merged {
  2036  			if _, ok := found[ver.header.VersionID]; ok {
  2037  				panic("found dupe")
  2038  			}
  2039  			found[ver.header.VersionID] = struct{}{}
  2040  		}
  2041  	}
  2042  	return merged
  2043  }
  2044  
  2045  type xlMetaBuf []byte
  2046  
  2047  // ToFileInfo converts xlMetaV2 into a common FileInfo datastructure
  2048  // for consumption across callers.
  2049  func (x xlMetaBuf) ToFileInfo(volume, path, versionID string, allParts bool) (fi FileInfo, err error) {
  2050  	var uv uuid.UUID
  2051  	if versionID != "" && versionID != nullVersionID {
  2052  		uv, err = uuid.Parse(versionID)
  2053  		if err != nil {
  2054  			logger.LogIf(GlobalContext, fmt.Errorf("invalid versionID specified %s", versionID))
  2055  			return fi, errFileVersionNotFound
  2056  		}
  2057  	}
  2058  	versions, headerV, metaV, buf, err := decodeXLHeaders(x)
  2059  	if err != nil {
  2060  		return fi, err
  2061  	}
  2062  	var header xlMetaV2VersionHeader
  2063  	var succModTime int64
  2064  	isLatest := true
  2065  	nonFreeVersions := versions
  2066  	found := false
  2067  	err = decodeVersions(buf, versions, func(idx int, hdr, meta []byte) error {
  2068  		if _, err := header.unmarshalV(headerV, hdr); err != nil {
  2069  			return err
  2070  		}
  2071  
  2072  		// skip listing free-version unless explicitly requested via versionID
  2073  		if header.FreeVersion() {
  2074  			nonFreeVersions--
  2075  			if header.VersionID != uv {
  2076  				return nil
  2077  			}
  2078  		}
  2079  		if found {
  2080  			return nil
  2081  		}
  2082  
  2083  		// We need a specific version, skip...
  2084  		if versionID != "" && uv != header.VersionID {
  2085  			isLatest = false
  2086  			succModTime = header.ModTime
  2087  			return nil
  2088  		}
  2089  
  2090  		// We found what we need.
  2091  		found = true
  2092  		var version xlMetaV2Version
  2093  		if _, err := version.unmarshalV(metaV, meta); err != nil {
  2094  			return err
  2095  		}
  2096  		if fi, err = version.ToFileInfo(volume, path, allParts); err != nil {
  2097  			return err
  2098  		}
  2099  		fi.IsLatest = isLatest
  2100  		if succModTime != 0 {
  2101  			fi.SuccessorModTime = time.Unix(0, succModTime)
  2102  		}
  2103  		return nil
  2104  	})
  2105  	if !found {
  2106  		if versionID == "" {
  2107  			return FileInfo{}, errFileNotFound
  2108  		}
  2109  
  2110  		return FileInfo{}, errFileVersionNotFound
  2111  	}
  2112  	fi.NumVersions = nonFreeVersions
  2113  	return fi, err
  2114  }
  2115  
  2116  // ListVersions lists current versions, and current deleted
  2117  // versions returns error for unexpected entries.
  2118  // showPendingDeletes is set to true if ListVersions needs to list objects marked deleted
  2119  // but waiting to be replicated
  2120  func (x xlMetaBuf) ListVersions(volume, path string, allParts bool) ([]FileInfo, error) {
  2121  	vers, _, metaV, buf, err := decodeXLHeaders(x)
  2122  	if err != nil {
  2123  		return nil, err
  2124  	}
  2125  	var succModTime time.Time
  2126  	isLatest := true
  2127  	dst := make([]FileInfo, 0, vers)
  2128  	var xl xlMetaV2Version
  2129  	err = decodeVersions(buf, vers, func(idx int, hdr, meta []byte) error {
  2130  		if _, err := xl.unmarshalV(metaV, meta); err != nil {
  2131  			return err
  2132  		}
  2133  		if !xl.Valid() {
  2134  			return errFileCorrupt
  2135  		}
  2136  		fi, err := xl.ToFileInfo(volume, path, allParts)
  2137  		if err != nil {
  2138  			return err
  2139  		}
  2140  		fi.IsLatest = isLatest
  2141  		fi.SuccessorModTime = succModTime
  2142  		fi.NumVersions = vers
  2143  		isLatest = false
  2144  		succModTime = xl.getModTime()
  2145  
  2146  		dst = append(dst, fi)
  2147  		return nil
  2148  	})
  2149  	return dst, err
  2150  }
  2151  
  2152  // IsLatestDeleteMarker returns true if latest version is a deletemarker or there are no versions.
  2153  // If any error occurs false is returned.
  2154  func (x xlMetaBuf) IsLatestDeleteMarker() bool {
  2155  	vers, headerV, _, buf, err := decodeXLHeaders(x)
  2156  	if err != nil {
  2157  		return false
  2158  	}
  2159  	if vers == 0 {
  2160  		return true
  2161  	}
  2162  	isDeleteMarker := false
  2163  
  2164  	_ = decodeVersions(buf, vers, func(idx int, hdr, _ []byte) error {
  2165  		var xl xlMetaV2VersionHeader
  2166  		if _, err := xl.unmarshalV(headerV, hdr); err != nil {
  2167  			return errDoneForNow
  2168  		}
  2169  		isDeleteMarker = xl.Type == DeleteType
  2170  		return errDoneForNow
  2171  	})
  2172  	return isDeleteMarker
  2173  }
  2174  
  2175  // AllHidden returns true are no versions that would show up in a listing (ie all free markers)
  2176  // Optionally also return early if top is a delete marker.
  2177  func (x xlMetaBuf) AllHidden(topDeleteMarker bool) bool {
  2178  	vers, headerV, _, buf, err := decodeXLHeaders(x)
  2179  	if err != nil {
  2180  		return false
  2181  	}
  2182  	if vers == 0 {
  2183  		return true
  2184  	}
  2185  	hidden := true
  2186  
  2187  	var xl xlMetaV2VersionHeader
  2188  	_ = decodeVersions(buf, vers, func(idx int, hdr, _ []byte) error {
  2189  		if _, err := xl.unmarshalV(headerV, hdr); err != nil {
  2190  			return errDoneForNow
  2191  		}
  2192  		if topDeleteMarker && idx == 0 && xl.Type == DeleteType {
  2193  			hidden = true
  2194  			return errDoneForNow
  2195  		}
  2196  		if !xl.FreeVersion() {
  2197  			hidden = false
  2198  			return errDoneForNow
  2199  		}
  2200  		// Check next version
  2201  		return nil
  2202  	})
  2203  	return hidden
  2204  }