2021-04-18 15:41:13 -04:00
// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
2020-06-12 23:04:01 -04:00
package cmd
import (
2022-03-04 23:01:26 -05:00
"errors"
2021-11-18 15:15:22 -05:00
"github.com/zeebo/xxh3"
2020-06-12 23:04:01 -04:00
)
2024-06-07 18:18:44 -04:00
// getFileInfoVersions partitions this object's versions such that,
// - fivs.Versions has all the non-free versions
// - fivs.FreeVersions has all the free versions
//
// if inclFreeVersions is true all the versions are in fivs.Versions, free and non-free versions alike.
//
// Note: Only the scanner requires fivs.Versions to have exclusively non-free versions. This is used while enforcing NewerNoncurrentVersions lifecycle element.
2024-08-08 11:29:58 -04:00
func getFileInfoVersions ( xlMetaBuf [ ] byte , volume , path string , inclFreeVersions bool ) ( FileInfoVersions , error ) {
fivs , err := getAllFileInfoVersions ( xlMetaBuf , volume , path , true )
2021-12-02 14:29:16 -05:00
if err != nil {
return fivs , err
}
2024-06-07 18:18:44 -04:00
// If inclFreeVersions is false, partition the versions in fivs.Versions
// such that finally fivs.Versions has
// all the non-free versions and fivs.FreeVersions has all the free
// versions.
2021-12-02 14:29:16 -05:00
n := 0
for _ , fi := range fivs . Versions {
2024-06-07 18:18:44 -04:00
// filter our tier object delete marker
if fi . TierFreeVersion ( ) {
if ! inclFreeVersions {
fivs . FreeVersions = append ( fivs . FreeVersions , fi )
}
2021-12-02 14:29:16 -05:00
} else {
2024-06-07 18:18:44 -04:00
if ! inclFreeVersions {
fivs . Versions [ n ] = fi
}
n ++
2021-11-19 20:54:10 -05:00
}
2020-06-12 23:04:01 -04:00
}
2024-06-07 18:18:44 -04:00
if ! inclFreeVersions {
fivs . Versions = fivs . Versions [ : n ]
}
2021-12-02 14:29:16 -05:00
// Update numversions
for i := range fivs . Versions {
fivs . Versions [ i ] . NumVersions = n
2020-06-12 23:04:01 -04:00
}
2021-12-02 14:29:16 -05:00
return fivs , nil
}
2020-06-12 23:04:01 -04:00
2023-09-02 10:49:24 -04:00
func getAllFileInfoVersions ( xlMetaBuf [ ] byte , volume , path string , allParts bool ) ( FileInfoVersions , error ) {
2021-12-02 14:29:16 -05:00
var versions [ ] FileInfo
var err error
2022-05-31 22:06:57 -04:00
if buf , _ , e := isIndexedMetaV2 ( xlMetaBuf ) ; e != nil {
return FileInfoVersions { } , e
} else if buf != nil {
2023-09-02 10:49:24 -04:00
versions , err = buf . ListVersions ( volume , path , allParts )
2021-12-02 14:29:16 -05:00
} else {
var xlMeta xlMetaV2
if err := xlMeta . LoadOrConvert ( xlMetaBuf ) ; err != nil {
return FileInfoVersions { } , err
}
2023-09-02 10:49:24 -04:00
versions , err = xlMeta . ListVersions ( volume , path , allParts )
2021-12-02 14:29:16 -05:00
}
2022-03-04 23:01:26 -05:00
if err == nil && len ( versions ) == 0 {
// This special case is needed to handle len(xlMeta.versions) == 0
versions = [ ] FileInfo {
{
Volume : volume ,
Name : path ,
Deleted : true ,
IsLatest : true ,
ModTime : timeSentinel1970 ,
} ,
}
}
if err != nil {
2020-06-12 23:04:01 -04:00
return FileInfoVersions { } , err
}
return FileInfoVersions {
Volume : volume ,
Name : path ,
2021-12-02 14:29:16 -05:00
Versions : versions ,
LatestModTime : versions [ 0 ] . ModTime ,
2020-06-12 23:04:01 -04:00
} , nil
}
2024-06-17 10:29:18 -04:00
type fileInfoOpts struct {
InclFreeVersions bool
Data bool
}
func getFileInfo ( xlMetaBuf [ ] byte , volume , path , versionID string , opts fileInfoOpts ) ( FileInfo , error ) {
2021-12-02 14:29:16 -05:00
var fi FileInfo
var err error
var inData xlMetaInlineData
2022-05-31 22:06:57 -04:00
if buf , data , e := isIndexedMetaV2 ( xlMetaBuf ) ; e != nil {
return FileInfo { } , e
} else if buf != nil {
2021-12-02 14:29:16 -05:00
inData = data
2024-08-08 11:29:58 -04:00
fi , err = buf . ToFileInfo ( volume , path , versionID , true )
2022-03-04 23:01:26 -05:00
if len ( buf ) != 0 && errors . Is ( err , errFileNotFound ) {
// This special case is needed to handle len(xlMeta.versions) == 0
return FileInfo {
Volume : volume ,
Name : path ,
VersionID : versionID ,
Deleted : true ,
IsLatest : true ,
ModTime : timeSentinel1970 ,
} , nil
}
2021-12-02 14:29:16 -05:00
} else {
var xlMeta xlMetaV2
if err := xlMeta . LoadOrConvert ( xlMetaBuf ) ; err != nil {
return FileInfo { } , err
2021-04-01 16:09:23 -04:00
}
2022-03-04 23:01:26 -05:00
if len ( xlMeta . versions ) == 0 {
// This special case is needed to handle len(xlMeta.versions) == 0
return FileInfo {
Volume : volume ,
Name : path ,
VersionID : versionID ,
Deleted : true ,
IsLatest : true ,
ModTime : timeSentinel1970 ,
} , nil
}
2021-12-02 14:29:16 -05:00
inData = xlMeta . data
2024-08-08 11:29:58 -04:00
fi , err = xlMeta . ToFileInfo ( volume , path , versionID , opts . InclFreeVersions , true )
2020-06-12 23:04:01 -04:00
}
2024-06-17 10:29:18 -04:00
if ! opts . Data || err != nil {
2021-12-02 14:29:16 -05:00
return fi , err
2020-06-12 23:04:01 -04:00
}
2021-12-02 14:29:16 -05:00
versionID = fi . VersionID
if versionID == "" {
versionID = nullVersionID
2020-06-12 23:04:01 -04:00
}
2024-06-17 10:29:18 -04:00
2021-12-02 14:29:16 -05:00
fi . Data = inData . find ( versionID )
if len ( fi . Data ) == 0 {
// PR #11758 used DataDir, preserve it
// for users who might have used master
// branch
fi . Data = inData . find ( fi . DataDir )
}
return fi , nil
2020-06-12 23:04:01 -04:00
}
2021-03-04 17:36:23 -05:00
2021-11-18 15:15:22 -05:00
// hashDeterministicString will return a deterministic hash for the map values.
// Trivial collisions are avoided, but this is by no means a strong hash.
func hashDeterministicString ( m map [ string ] string ) uint64 {
// Seed (random)
2022-01-02 12:15:06 -05:00
crc := uint64 ( 0xc2b40bbac11a7295 )
2021-11-18 15:15:22 -05:00
// Xor each value to make order independent
for k , v := range m {
// Separate key and value with an individual xor with a random number.
// Add values of each, so they cannot be trivially collided.
crc ^ = ( xxh3 . HashString ( k ) ^ 0x4ee3bbaf7ab2506b ) + ( xxh3 . HashString ( v ) ^ 0x8da4c8da66194257 )
}
return crc
}
// hashDeterministicBytes will return a deterministic (weak) hash for the map values.
// Trivial collisions are avoided, but this is by no means a strong hash.
func hashDeterministicBytes ( m map [ string ] [ ] byte ) uint64 {
2022-01-02 12:15:06 -05:00
crc := uint64 ( 0x1bbc7e1dde654743 )
2021-11-18 15:15:22 -05:00
for k , v := range m {
crc ^ = ( xxh3 . HashString ( k ) ^ 0x4ee3bbaf7ab2506b ) + ( xxh3 . Hash ( v ) ^ 0x8da4c8da66194257 )
}
return crc
}