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 }