2016-05-04 15:18:20 -04:00
/ *
2019-04-09 14:39:42 -04:00
* MinIO Cloud Storage , ( C ) 2016 , 2017 , 2018 MinIO , Inc .
2016-05-04 15:18:20 -04:00
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
2016-08-18 19:23:42 -04:00
package cmd
2016-05-04 15:18:20 -04:00
2016-05-07 03:59:43 -04:00
import (
2019-09-24 21:47:26 -04:00
"bytes"
2018-04-05 18:04:40 -04:00
"context"
2020-01-13 16:09:10 -05:00
"encoding/hex"
2016-05-07 03:59:43 -04:00
"encoding/json"
2020-09-28 22:39:32 -04:00
"errors"
2016-05-07 03:59:43 -04:00
"fmt"
2018-02-15 20:45:57 -05:00
"io/ioutil"
2018-07-18 23:17:35 -04:00
"reflect"
2020-01-13 16:09:10 -05:00
"sync"
2018-02-15 20:45:57 -05:00
2018-02-20 21:42:09 -05:00
humanize "github.com/dustin/go-humanize"
2020-01-13 16:09:10 -05:00
"github.com/minio/minio/cmd/config/storageclass"
2018-07-18 23:17:35 -04:00
"github.com/minio/minio/cmd/logger"
2020-01-13 16:09:10 -05:00
"github.com/minio/minio/pkg/color"
2019-10-14 12:44:51 -04:00
"github.com/minio/minio/pkg/sync/errgroup"
2018-02-20 21:42:09 -05:00
sha256 "github.com/minio/sha256-simd"
2017-06-12 20:40:28 -04:00
)
const (
2020-06-12 23:04:01 -04:00
// Represents Erasure backend.
formatBackendErasure = "xl"
2017-06-12 20:40:28 -04:00
2020-06-12 23:04:01 -04:00
// formatErasureV1.Erasure.Version - version '1'.
formatErasureVersionV1 = "1"
2018-02-15 20:45:57 -05:00
2020-06-12 23:04:01 -04:00
// formatErasureV2.Erasure.Version - version '2'.
formatErasureVersionV2 = "2"
2018-02-15 20:45:57 -05:00
2020-06-12 23:04:01 -04:00
// formatErasureV3.Erasure.Version - version '3'.
formatErasureVersionV3 = "3"
2018-03-15 16:55:23 -04:00
2020-06-12 23:04:01 -04:00
// Distribution algorithm used, legacy
formatErasureVersionV2DistributionAlgoLegacy = "CRCMOD"
// Distributed algorithm used, current
formatErasureVersionV3DistributionAlgo = "SIPMOD"
2018-01-08 17:30:55 -05:00
)
2017-06-12 20:40:28 -04:00
2018-02-15 20:45:57 -05:00
// Offline disk UUID represents an offline disk.
const offlineDiskUUID = "ffffffff-ffff-ffff-ffff-ffffffffffff"
// Used to detect the version of "xl" format.
2020-06-12 23:04:01 -04:00
type formatErasureVersionDetect struct {
Erasure struct {
2018-02-15 20:45:57 -05:00
Version string ` json:"version" `
} ` json:"xl" `
}
// Represents the V1 backend disk structure version
// under `.minio.sys` and actual data namespace.
2020-06-12 23:04:01 -04:00
// formatErasureV1 - structure holds format config version '1'.
type formatErasureV1 struct {
2018-01-08 17:30:55 -05:00
formatMetaV1
2020-06-12 23:04:01 -04:00
Erasure struct {
2018-01-08 17:30:55 -05:00
Version string ` json:"version" ` // Version of 'xl' format.
Disk string ` json:"disk" ` // Disk field carries assigned disk uuid.
// JBOD field carries the input disk order generated the first
// time when fresh disks were supplied.
JBOD [ ] string ` json:"jbod" `
2020-06-12 23:04:01 -04:00
} ` json:"xl" ` // Erasure field holds xl format.
2017-06-12 20:40:28 -04:00
}
2018-02-15 20:45:57 -05:00
// Represents the V2 backend disk structure version
// under `.minio.sys` and actual data namespace.
2020-06-12 23:04:01 -04:00
// formatErasureV2 - structure holds format config version '2'.
2018-07-16 23:26:42 -04:00
// The V2 format to support "large bucket" support where a bucket
// can span multiple erasure sets.
2020-06-12 23:04:01 -04:00
type formatErasureV2 struct {
2018-07-16 23:26:42 -04:00
formatMetaV1
2020-06-12 23:04:01 -04:00
Erasure struct {
2018-02-15 20:45:57 -05:00
Version string ` json:"version" ` // Version of 'xl' format.
This string ` json:"this" ` // This field carries assigned disk uuid.
// Sets field carries the input disk order generated the first
// time when fresh disks were supplied, it is a two dimensional
// array second dimension represents list of disks used per set.
Sets [ ] [ ] string ` json:"sets" `
// Distribution algorithm represents the hashing algorithm
// to pick the right set index for an object.
DistributionAlgo string ` json:"distributionAlgo" `
} ` json:"xl" `
}
2020-06-12 23:04:01 -04:00
// formatErasureV3 struct is same as formatErasureV2 struct except that formatErasureV3.Erasure.Version is "3" indicating
2018-03-15 16:55:23 -04:00
// the simplified multipart backend which is a flat hierarchy now.
// In .minio.sys/multipart we have:
2020-06-12 23:04:01 -04:00
// sha256(bucket/object)/uploadID/[xl.meta, part.1, part.2 ....]
type formatErasureV3 struct {
2018-07-16 23:26:42 -04:00
formatMetaV1
2020-06-12 23:04:01 -04:00
Erasure struct {
2018-03-15 16:55:23 -04:00
Version string ` json:"version" ` // Version of 'xl' format.
This string ` json:"this" ` // This field carries assigned disk uuid.
// Sets field carries the input disk order generated the first
// time when fresh disks were supplied, it is a two dimensional
// array second dimension represents list of disks used per set.
Sets [ ] [ ] string ` json:"sets" `
// Distribution algorithm represents the hashing algorithm
// to pick the right set index for an object.
DistributionAlgo string ` json:"distributionAlgo" `
} ` json:"xl" `
}
2020-06-12 23:04:01 -04:00
func ( f * formatErasureV3 ) Clone ( ) * formatErasureV3 {
2019-11-21 07:24:51 -05:00
b , err := json . Marshal ( f )
if err != nil {
panic ( err )
}
2020-06-12 23:04:01 -04:00
var dst formatErasureV3
2019-11-21 07:24:51 -05:00
if err = json . Unmarshal ( b , & dst ) ; err != nil {
panic ( err )
}
return & dst
}
2020-06-12 23:04:01 -04:00
// Returns formatErasure.Erasure.Version
2020-10-24 16:23:08 -04:00
func newFormatErasureV3 ( numSets int , setLen int , distributionAlgo string ) * formatErasureV3 {
2020-06-12 23:04:01 -04:00
format := & formatErasureV3 { }
2018-02-15 20:45:57 -05:00
format . Version = formatMetaVersionV1
2020-06-12 23:04:01 -04:00
format . Format = formatBackendErasure
2018-07-18 23:17:35 -04:00
format . ID = mustGetUUID ( )
2020-06-12 23:04:01 -04:00
format . Erasure . Version = formatErasureVersionV3
2020-10-24 16:23:08 -04:00
if distributionAlgo == "" {
format . Erasure . DistributionAlgo = formatErasureVersionV3DistributionAlgo
} else {
format . Erasure . DistributionAlgo = distributionAlgo
}
2020-06-12 23:04:01 -04:00
format . Erasure . Sets = make ( [ ] [ ] string , numSets )
2018-02-15 20:45:57 -05:00
for i := 0 ; i < numSets ; i ++ {
2020-06-12 23:04:01 -04:00
format . Erasure . Sets [ i ] = make ( [ ] string , setLen )
2018-02-15 20:45:57 -05:00
for j := 0 ; j < setLen ; j ++ {
2020-06-12 23:04:01 -04:00
format . Erasure . Sets [ i ] [ j ] = mustGetUUID ( )
2018-02-15 20:45:57 -05:00
}
}
return format
}
2020-06-12 23:04:01 -04:00
// Returns format Erasure version after reading `format.json`, returns
// successfully the version only if the backend is Erasure.
func formatGetBackendErasureVersion ( formatPath string ) ( string , error ) {
2018-02-15 20:45:57 -05:00
meta := & formatMetaV1 { }
b , err := ioutil . ReadFile ( formatPath )
if err != nil {
return "" , err
}
if err = json . Unmarshal ( b , meta ) ; err != nil {
return "" , err
}
if meta . Version != formatMetaVersionV1 {
return "" , fmt . Errorf ( ` format.Version expected: %s, got: %s ` , formatMetaVersionV1 , meta . Version )
}
2020-06-12 23:04:01 -04:00
if meta . Format != formatBackendErasure {
return "" , fmt . Errorf ( ` found backend %s, expected %s ` , meta . Format , formatBackendErasure )
2019-12-23 19:31:03 -05:00
}
2020-06-12 23:04:01 -04:00
// Erasure backend found, proceed to detect version.
format := & formatErasureVersionDetect { }
2019-12-23 19:31:03 -05:00
if err = json . Unmarshal ( b , format ) ; err != nil {
return "" , err
}
2020-06-12 23:04:01 -04:00
return format . Erasure . Version , nil
2018-02-15 20:45:57 -05:00
}
// Migrates all previous versions to latest version of `format.json`,
// this code calls migration in sequence, such as V1 is migrated to V2
// first before it V2 migrates to V3.
2020-06-12 23:04:01 -04:00
func formatErasureMigrate ( export string ) error {
2018-02-15 20:45:57 -05:00
formatPath := pathJoin ( export , minioMetaBucket , formatConfigFile )
2020-06-12 23:04:01 -04:00
version , err := formatGetBackendErasureVersion ( formatPath )
2018-02-15 20:45:57 -05:00
if err != nil {
return err
}
switch version {
2020-06-12 23:04:01 -04:00
case formatErasureVersionV1 :
if err = formatErasureMigrateV1ToV2 ( export , version ) ; err != nil {
2018-02-15 20:45:57 -05:00
return err
}
2019-12-23 19:31:03 -05:00
// Migrate successful v1 => v2, proceed to v2 => v3
2020-06-12 23:04:01 -04:00
version = formatErasureVersionV2
2018-02-15 20:45:57 -05:00
fallthrough
2020-06-12 23:04:01 -04:00
case formatErasureVersionV2 :
if err = formatErasureMigrateV2ToV3 ( export , version ) ; err != nil {
2018-03-15 16:55:23 -04:00
return err
}
2019-12-23 19:31:03 -05:00
// Migrate successful v2 => v3, v3 is latest
2020-05-18 12:59:45 -04:00
// version = formatXLVersionV3
2018-03-15 16:55:23 -04:00
fallthrough
2020-06-12 23:04:01 -04:00
case formatErasureVersionV3 :
2019-12-23 19:31:03 -05:00
// v3 is the latest version, return.
2018-02-15 20:45:57 -05:00
return nil
}
return fmt . Errorf ( ` %s: unknown format version %s ` , export , version )
}
// Migrates version V1 of format.json to version V2 of format.json,
// migration fails upon any error.
2020-06-12 23:04:01 -04:00
func formatErasureMigrateV1ToV2 ( export , version string ) error {
if version != formatErasureVersionV1 {
return fmt . Errorf ( ` Disk %s: format version expected %s, found %s ` , export , formatErasureVersionV1 , version )
2018-02-15 20:45:57 -05:00
}
2019-12-23 19:31:03 -05:00
formatPath := pathJoin ( export , minioMetaBucket , formatConfigFile )
2020-06-12 23:04:01 -04:00
formatV1 := & formatErasureV1 { }
2018-02-15 20:45:57 -05:00
b , err := ioutil . ReadFile ( formatPath )
if err != nil {
return err
}
if err = json . Unmarshal ( b , formatV1 ) ; err != nil {
return err
}
2016-06-02 19:34:15 -04:00
2020-06-12 23:04:01 -04:00
formatV2 := & formatErasureV2 { }
2018-03-15 16:55:23 -04:00
formatV2 . Version = formatMetaVersionV1
2020-06-12 23:04:01 -04:00
formatV2 . Format = formatBackendErasure
formatV2 . Erasure . Version = formatErasureVersionV2
formatV2 . Erasure . DistributionAlgo = formatErasureVersionV2DistributionAlgoLegacy
formatV2 . Erasure . This = formatV1 . Erasure . Disk
formatV2 . Erasure . Sets = make ( [ ] [ ] string , 1 )
formatV2 . Erasure . Sets [ 0 ] = make ( [ ] string , len ( formatV1 . Erasure . JBOD ) )
copy ( formatV2 . Erasure . Sets [ 0 ] , formatV1 . Erasure . JBOD )
2018-02-15 20:45:57 -05:00
b , err = json . Marshal ( formatV2 )
if err != nil {
return err
}
return ioutil . WriteFile ( formatPath , b , 0644 )
}
2017-08-03 07:37:02 -04:00
2018-03-15 16:55:23 -04:00
// Migrates V2 for format.json to V3 (Flat hierarchy for multipart)
2020-06-12 23:04:01 -04:00
func formatErasureMigrateV2ToV3 ( export , version string ) error {
if version != formatErasureVersionV2 {
return fmt . Errorf ( ` Disk %s: format version expected %s, found %s ` , export , formatErasureVersionV2 , version )
2018-03-15 16:55:23 -04:00
}
2019-12-23 19:31:03 -05:00
formatPath := pathJoin ( export , minioMetaBucket , formatConfigFile )
2020-06-12 23:04:01 -04:00
formatV2 := & formatErasureV2 { }
2018-03-15 16:55:23 -04:00
b , err := ioutil . ReadFile ( formatPath )
if err != nil {
return err
}
err = json . Unmarshal ( b , formatV2 )
if err != nil {
return err
}
2018-08-06 00:15:28 -04:00
if err = removeAll ( pathJoin ( export , minioMetaMultipartBucket ) ) ; err != nil {
2018-03-15 16:55:23 -04:00
return err
}
2018-08-06 00:15:28 -04:00
if err = mkdirAll ( pathJoin ( export , minioMetaMultipartBucket ) , 0755 ) ; err != nil {
2018-03-15 16:55:23 -04:00
return err
}
// format-V2 struct is exactly same as format-V1 except that version is "3"
// which indicates the simplified multipart backend.
2020-06-12 23:04:01 -04:00
formatV3 := formatErasureV3 { }
2018-03-15 16:55:23 -04:00
formatV3 . Version = formatV2 . Version
formatV3 . Format = formatV2 . Format
2020-06-12 23:04:01 -04:00
formatV3 . Erasure = formatV2 . Erasure
2018-03-15 16:55:23 -04:00
2020-06-12 23:04:01 -04:00
formatV3 . Erasure . Version = formatErasureVersionV3
2018-03-15 16:55:23 -04:00
b , err = json . Marshal ( formatV3 )
if err != nil {
return err
}
return ioutil . WriteFile ( formatPath , b , 0644 )
}
2018-02-15 20:45:57 -05:00
// countErrs - count a specific error.
func countErrs ( errs [ ] error , err error ) int {
var i = 0
for _ , err1 := range errs {
2018-04-10 12:36:37 -04:00
if err1 == err {
2018-02-15 20:45:57 -05:00
i ++
}
}
return i
2016-06-02 19:34:15 -04:00
}
2018-02-15 20:45:57 -05:00
// Does all errors indicate we need to initialize all disks?.
2020-06-12 23:04:01 -04:00
func shouldInitErasureDisks ( errs [ ] error ) bool {
2018-02-15 20:45:57 -05:00
return countErrs ( errs , errUnformattedDisk ) == len ( errs )
}
2018-04-12 18:43:38 -04:00
// Check if unformatted disks are equal to write quorum.
func quorumUnformattedDisks ( errs [ ] error ) bool {
return countErrs ( errs , errUnformattedDisk ) >= ( len ( errs ) / 2 ) + 1
}
2020-06-12 23:04:01 -04:00
// loadFormatErasureAll - load all format config from all input disks in parallel.
func loadFormatErasureAll ( storageDisks [ ] StorageAPI , heal bool ) ( [ ] * formatErasureV3 , [ ] error ) {
2016-06-02 19:34:15 -04:00
// Initialize list of errors.
2019-10-14 12:44:51 -04:00
g := errgroup . WithNErrs ( len ( storageDisks ) )
2016-06-02 19:34:15 -04:00
// Initialize format configs.
2020-06-12 23:04:01 -04:00
var formats = make ( [ ] * formatErasureV3 , len ( storageDisks ) )
2016-06-02 19:34:15 -04:00
2018-01-22 17:54:55 -05:00
// Load format from each disk in parallel
2019-10-14 12:44:51 -04:00
for index := range storageDisks {
index := index
g . Go ( func ( ) error {
if storageDisks [ index ] == nil {
return errDiskNotFound
}
2020-06-12 23:04:01 -04:00
format , err := loadFormatErasure ( storageDisks [ index ] )
2019-10-14 12:44:51 -04:00
if err != nil {
return err
2016-06-02 19:34:15 -04:00
}
2018-01-08 17:30:55 -05:00
formats [ index ] = format
2020-03-27 17:48:30 -04:00
if ! heal {
// If no healing required, make the disks valid and
// online.
2020-06-12 23:04:01 -04:00
storageDisks [ index ] . SetDiskID ( format . Erasure . This )
2020-03-27 17:48:30 -04:00
}
2019-10-14 12:44:51 -04:00
return nil
} , index )
2016-06-02 19:34:15 -04:00
}
2019-10-14 12:44:51 -04:00
// Return all formats and errors if any.
return formats , g . Wait ( )
2016-06-02 19:34:15 -04:00
}
2020-09-28 22:39:32 -04:00
func saveFormatErasure ( disk StorageAPI , format * formatErasureV3 , heal bool ) error {
2020-08-18 17:37:26 -04:00
if disk == nil || format == nil {
2020-03-27 17:48:30 -04:00
return errDiskNotFound
}
2020-08-18 17:37:26 -04:00
diskID := format . Erasure . This
2020-06-12 23:04:01 -04:00
if err := makeFormatErasureMetaVolumes ( disk ) ; err != nil {
2020-03-27 17:48:30 -04:00
return err
}
2018-02-15 20:45:57 -05:00
// Marshal and write to disk.
formatBytes , err := json . Marshal ( format )
if err != nil {
return err
2017-08-03 07:37:02 -04:00
}
2019-12-23 19:31:03 -05:00
tmpFormat := mustGetUUID ( )
2019-09-24 21:47:26 -04:00
2018-02-15 20:45:57 -05:00
// Purge any existing temporary file, okay to ignore errors here.
2020-09-04 12:45:06 -04:00
defer disk . DeleteFile ( context . TODO ( ) , minioMetaBucket , tmpFormat )
2017-08-03 07:37:02 -04:00
2019-12-23 19:31:03 -05:00
// write to unique file.
2020-09-04 12:45:06 -04:00
if err = disk . WriteAll ( context . TODO ( ) , minioMetaBucket , tmpFormat , bytes . NewReader ( formatBytes ) ) ; err != nil {
2018-02-15 20:45:57 -05:00
return err
2017-08-03 07:37:02 -04:00
}
2019-09-24 21:47:26 -04:00
// Rename file `uuid.json` --> `format.json`.
2020-09-04 12:45:06 -04:00
if err = disk . RenameFile ( context . TODO ( ) , minioMetaBucket , tmpFormat , minioMetaBucket , formatConfigFile ) ; err != nil {
2020-03-27 17:48:30 -04:00
return err
}
disk . SetDiskID ( diskID )
2020-09-28 22:39:32 -04:00
if heal {
htracker := healingTracker {
ID : diskID ,
}
htrackerBytes , err := htracker . MarshalMsg ( nil )
if err != nil {
return err
}
return disk . WriteAll ( context . TODO ( ) , minioMetaBucket ,
pathJoin ( bucketMetaPrefix , slashSeparator , healingTrackerFilename ) ,
bytes . NewReader ( htrackerBytes ) )
}
2020-03-27 17:48:30 -04:00
return nil
2016-05-20 05:22:22 -04:00
}
2020-04-06 19:51:18 -04:00
var ignoredHiddenDirectories = map [ string ] struct { } {
2020-05-26 12:32:33 -04:00
minioMetaBucket : { } , // metabucket '.minio.sys'
".minio" : { } , // users may choose to double down the backend as the config folder for certs
".snapshot" : { } , // .snapshot for ignoring NetApp based persistent volumes WAFL snapshot
"lost+found" : { } , // 'lost+found' directory default on ext4 filesystems
"$RECYCLE.BIN" : { } , // windows specific directory for each drive (hidden)
"System Volume Information" : { } , // windows specific directory for each drive (hidden)
2018-11-02 14:31:55 -04:00
}
2020-04-06 19:51:18 -04:00
func isHiddenDirectories ( vols ... VolInfo ) bool {
for _ , vol := range vols {
if _ , ok := ignoredHiddenDirectories [ vol . Name ] ; ok {
continue
2018-11-02 14:31:55 -04:00
}
2020-04-06 19:51:18 -04:00
return false
2018-11-02 14:31:55 -04:00
}
2020-04-06 19:51:18 -04:00
return true
2018-11-02 14:31:55 -04:00
}
2020-06-12 23:04:01 -04:00
// loadFormatErasure - loads format.json from disk.
func loadFormatErasure ( disk StorageAPI ) ( format * formatErasureV3 , err error ) {
2020-09-04 12:45:06 -04:00
buf , err := disk . ReadAll ( context . TODO ( ) , minioMetaBucket , formatConfigFile )
2016-06-25 17:51:06 -04:00
if err != nil {
2016-05-20 05:22:22 -04:00
// 'file not found' and 'volume not found' as
// same. 'volume not found' usually means its a fresh disk.
if err == errFileNotFound || err == errVolumeNotFound {
var vols [ ] VolInfo
2020-09-04 12:45:06 -04:00
vols , err = disk . ListVols ( context . TODO ( ) )
2016-05-20 05:22:22 -04:00
if err != nil {
return nil , err
}
2020-04-06 19:51:18 -04:00
if ! isHiddenDirectories ( vols ... ) {
// 'format.json' not found, but we found user data, reject such disks.
2020-08-03 21:17:48 -04:00
return nil , fmt . Errorf ( "some unexpected files '%v' found on %s: %w" ,
vols , disk , errCorruptedFormat )
2016-05-20 05:22:22 -04:00
}
// No other data found, its a fresh disk.
return nil , errUnformattedDisk
}
2016-05-07 03:59:43 -04:00
return nil , err
2016-05-04 15:18:20 -04:00
}
2016-06-24 05:06:23 -04:00
// Try to decode format json into formatConfigV1 struct.
2020-06-12 23:04:01 -04:00
format = & formatErasureV3 { }
2016-06-25 17:51:06 -04:00
if err = json . Unmarshal ( buf , format ) ; err != nil {
2016-05-07 03:59:43 -04:00
return nil , err
2016-05-04 15:18:20 -04:00
}
2016-06-24 05:06:23 -04:00
// Success.
2016-05-20 05:22:22 -04:00
return format , nil
}
2020-06-12 23:04:01 -04:00
// Valid formatErasure basic versions.
func checkFormatErasureValue ( formatErasure * formatErasureV3 ) error {
2018-02-15 20:45:57 -05:00
// Validate format version and format type.
2020-06-12 23:04:01 -04:00
if formatErasure . Version != formatMetaVersionV1 {
return fmt . Errorf ( "Unsupported version of backend format [%s] found" , formatErasure . Version )
2016-07-26 06:18:47 -04:00
}
2020-06-12 23:04:01 -04:00
if formatErasure . Format != formatBackendErasure {
return fmt . Errorf ( "Unsupported backend format [%s] found" , formatErasure . Format )
2017-08-03 07:37:02 -04:00
}
2020-06-12 23:04:01 -04:00
if formatErasure . Erasure . Version != formatErasureVersionV3 {
return fmt . Errorf ( "Unsupported Erasure backend format found [%s]" , formatErasure . Erasure . Version )
2018-01-22 17:54:55 -05:00
}
return nil
2017-08-03 07:37:02 -04:00
}
2018-02-15 20:45:57 -05:00
// Check all format values.
2020-08-26 22:29:35 -04:00
func checkFormatErasureValues ( formats [ ] * formatErasureV3 , setDriveCount int ) error {
2020-06-12 23:04:01 -04:00
for i , formatErasure := range formats {
if formatErasure == nil {
2018-02-15 20:45:57 -05:00
continue
}
2020-06-12 23:04:01 -04:00
if err := checkFormatErasureValue ( formatErasure ) ; err != nil {
2018-02-15 20:45:57 -05:00
return err
}
2020-06-12 23:04:01 -04:00
if len ( formats ) != len ( formatErasure . Erasure . Sets ) * len ( formatErasure . Erasure . Sets [ 0 ] ) {
2018-02-20 21:42:09 -05:00
return fmt . Errorf ( "%s disk is already being used in another erasure deployment. (Number of disks specified: %d but the number of disks found in the %s disk's format.json: %d)" ,
2020-06-12 23:04:01 -04:00
humanize . Ordinal ( i + 1 ) , len ( formats ) , humanize . Ordinal ( i + 1 ) , len ( formatErasure . Erasure . Sets ) * len ( formatErasure . Erasure . Sets [ 0 ] ) )
2018-02-15 20:45:57 -05:00
}
2020-04-27 17:39:57 -04:00
// Only if custom erasure drive count is set,
// we should fail here other proceed to honor what
// is present on the disk.
2020-08-26 22:29:35 -04:00
if globalCustomErasureDriveCount && len ( formatErasure . Erasure . Sets [ 0 ] ) != setDriveCount {
return fmt . Errorf ( "%s disk is already formatted with %d drives per erasure set. This cannot be changed to %d, please revert your MINIO_ERASURE_SET_DRIVE_COUNT setting" , humanize . Ordinal ( i + 1 ) , len ( formatErasure . Erasure . Sets [ 0 ] ) , setDriveCount )
2020-03-08 16:30:25 -04:00
}
2017-08-03 07:37:02 -04:00
}
2018-02-15 20:45:57 -05:00
return nil
}
2017-08-03 07:37:02 -04:00
2020-06-12 23:04:01 -04:00
// Get Deployment ID for the Erasure sets from format.json.
2018-07-18 23:17:35 -04:00
// This need not be in quorum. Even if one of the format.json
// file has this value, we assume it is valid.
// If more than one format.json's have different id, it is considered a corrupt
// backend format.
2020-06-12 23:04:01 -04:00
func formatErasureGetDeploymentID ( refFormat * formatErasureV3 , formats [ ] * formatErasureV3 ) ( string , error ) {
2018-07-18 23:17:35 -04:00
var deploymentID string
for _ , format := range formats {
if format == nil || format . ID == "" {
continue
}
2020-06-12 23:04:01 -04:00
if reflect . DeepEqual ( format . Erasure . Sets , refFormat . Erasure . Sets ) {
2018-07-18 23:17:35 -04:00
// Found an ID in one of the format.json file
// Set deploymentID for the first time.
if deploymentID == "" {
deploymentID = format . ID
} else if deploymentID != format . ID {
// DeploymentID found earlier doesn't match with the
// current format.json's ID.
2020-08-03 21:17:48 -04:00
return "" , fmt . Errorf ( "Deployment IDs do not match expected %s, got %s: %w" ,
deploymentID , format . ID , errCorruptedFormat )
2018-07-18 23:17:35 -04:00
}
}
}
return deploymentID , nil
}
2020-06-12 23:04:01 -04:00
// formatErasureFixDeploymentID - Add deployment id if it is not present.
func formatErasureFixDeploymentID ( endpoints Endpoints , storageDisks [ ] StorageAPI , refFormat * formatErasureV3 ) ( err error ) {
2018-07-18 23:17:35 -04:00
// Attempt to load all `format.json` from all disks.
2020-08-03 21:17:48 -04:00
formats , _ := loadFormatErasureAll ( storageDisks , false )
2018-07-18 23:17:35 -04:00
for index := range formats {
2020-06-12 23:04:01 -04:00
// If the Erasure sets do not match, set those formats to nil,
2018-07-18 23:17:35 -04:00
// We do not have to update the ID on those format.json file.
2020-06-12 23:04:01 -04:00
if formats [ index ] != nil && ! reflect . DeepEqual ( formats [ index ] . Erasure . Sets , refFormat . Erasure . Sets ) {
2018-07-18 23:17:35 -04:00
formats [ index ] = nil
}
}
2020-08-03 21:17:48 -04:00
2020-06-12 23:04:01 -04:00
refFormat . ID , err = formatErasureGetDeploymentID ( refFormat , formats )
2018-07-18 23:17:35 -04:00
if err != nil {
return err
}
// If ID is set, then some other node got the lock
// before this node could and generated an ID
// for the deployment. No need to generate one.
if refFormat . ID != "" {
return nil
}
// ID is generated for the first time,
// We set the ID in all the formats and update.
refFormat . ID = mustGetUUID ( )
for _ , format := range formats {
if format != nil {
format . ID = refFormat . ID
}
}
// Deployment ID needs to be set on all the disks.
// Save `format.json` across all disks.
2020-06-12 23:04:01 -04:00
return saveFormatErasureAll ( GlobalContext , storageDisks , formats )
2018-07-18 23:17:35 -04:00
}
// Update only the valid local disks which have not been updated before.
2020-06-12 23:04:01 -04:00
func formatErasureFixLocalDeploymentID ( endpoints Endpoints , storageDisks [ ] StorageAPI , refFormat * formatErasureV3 ) error {
2018-07-18 23:17:35 -04:00
// If this server was down when the deploymentID was updated
// then we make sure that we update the local disks with the deploymentID.
2020-03-27 17:48:30 -04:00
// Initialize errs to collect errors inside go-routine.
g := errgroup . WithNErrs ( len ( storageDisks ) )
for index := range storageDisks {
index := index
g . Go ( func ( ) error {
if endpoints [ index ] . IsLocal && storageDisks [ index ] != nil && storageDisks [ index ] . IsOnline ( ) {
2020-06-12 23:04:01 -04:00
format , err := loadFormatErasure ( storageDisks [ index ] )
2020-03-27 17:48:30 -04:00
if err != nil {
// Disk can be offline etc.
// ignore the errors seen here.
return nil
}
if format . ID != "" {
return nil
}
2020-06-12 23:04:01 -04:00
if ! reflect . DeepEqual ( format . Erasure . Sets , refFormat . Erasure . Sets ) {
2020-03-27 17:48:30 -04:00
return nil
}
format . ID = refFormat . ID
2020-09-28 22:39:32 -04:00
// Heal the drive if we fixed its deployment ID.
if err := saveFormatErasure ( storageDisks [ index ] , format , true ) ; err != nil {
2020-04-09 12:30:02 -04:00
logger . LogIf ( GlobalContext , err )
2020-03-27 17:48:30 -04:00
return fmt . Errorf ( "Unable to save format.json, %w" , err )
}
2018-07-18 23:17:35 -04:00
}
2020-03-27 17:48:30 -04:00
return nil
} , index )
}
for _ , err := range g . Wait ( ) {
if err != nil {
return err
2018-07-18 23:17:35 -04:00
}
}
return nil
}
2020-06-12 23:04:01 -04:00
// Get backend Erasure format in quorum `format.json`.
func getFormatErasureInQuorum ( formats [ ] * formatErasureV3 ) ( * formatErasureV3 , error ) {
2018-02-15 20:45:57 -05:00
formatHashes := make ( [ ] string , len ( formats ) )
for i , format := range formats {
if format == nil {
2018-01-22 17:54:55 -05:00
continue
}
2018-02-15 20:45:57 -05:00
h := sha256 . New ( )
2020-06-12 23:04:01 -04:00
for _ , set := range format . Erasure . Sets {
2018-02-15 20:45:57 -05:00
for _ , diskID := range set {
h . Write ( [ ] byte ( diskID ) )
2016-06-22 20:18:31 -04:00
}
2016-06-01 19:15:56 -04:00
}
2018-02-15 20:45:57 -05:00
formatHashes [ i ] = hex . EncodeToString ( h . Sum ( nil ) )
2016-06-01 19:15:56 -04:00
}
2016-07-26 06:18:47 -04:00
2018-02-15 20:45:57 -05:00
formatCountMap := make ( map [ string ] int )
for _ , hash := range formatHashes {
if hash == "" {
continue
}
formatCountMap [ hash ] ++
}
2016-06-01 19:15:56 -04:00
2018-02-15 20:45:57 -05:00
maxHash := ""
maxCount := 0
for hash , count := range formatCountMap {
if count > maxCount {
maxCount = count
maxHash = hash
2016-07-28 19:49:59 -04:00
}
2018-02-15 20:45:57 -05:00
}
if maxCount < len ( formats ) / 2 {
2020-06-12 23:04:01 -04:00
return nil , errErasureReadQuorum
2018-02-15 20:45:57 -05:00
}
for i , hash := range formatHashes {
if hash == maxHash {
2019-11-21 07:24:51 -05:00
format := formats [ i ] . Clone ( )
2020-06-12 23:04:01 -04:00
format . Erasure . This = ""
2019-11-21 07:24:51 -05:00
return format , nil
2016-07-28 19:49:59 -04:00
}
}
2018-02-15 20:45:57 -05:00
2020-06-12 23:04:01 -04:00
return nil , errErasureReadQuorum
2016-07-28 19:49:59 -04:00
}
2020-06-12 23:04:01 -04:00
func formatErasureV3Check ( reference * formatErasureV3 , format * formatErasureV3 ) error {
2019-11-21 07:24:51 -05:00
tmpFormat := format . Clone ( )
2020-06-12 23:04:01 -04:00
this := tmpFormat . Erasure . This
tmpFormat . Erasure . This = ""
if len ( reference . Erasure . Sets ) != len ( format . Erasure . Sets ) {
return fmt . Errorf ( "Expected number of sets %d, got %d" , len ( reference . Erasure . Sets ) , len ( format . Erasure . Sets ) )
2018-02-15 20:45:57 -05:00
}
2017-08-03 07:37:02 -04:00
2018-02-15 20:45:57 -05:00
// Make sure that the sets match.
2020-06-12 23:04:01 -04:00
for i := range reference . Erasure . Sets {
if len ( reference . Erasure . Sets [ i ] ) != len ( format . Erasure . Sets [ i ] ) {
2018-02-15 20:45:57 -05:00
return fmt . Errorf ( "Each set should be of same size, expected %d got %d" ,
2020-06-12 23:04:01 -04:00
len ( reference . Erasure . Sets [ i ] ) , len ( format . Erasure . Sets [ i ] ) )
2016-07-28 19:49:59 -04:00
}
2020-06-12 23:04:01 -04:00
for j := range reference . Erasure . Sets [ i ] {
if reference . Erasure . Sets [ i ] [ j ] != format . Erasure . Sets [ i ] [ j ] {
2020-10-24 16:23:08 -04:00
return fmt . Errorf ( "UUID on positions %d:%d do not match with, expected %s got %s: (%w)" ,
i , j , reference . Erasure . Sets [ i ] [ j ] , format . Erasure . Sets [ i ] [ j ] , errInconsistentDisk )
2016-07-28 19:49:59 -04:00
}
}
2018-02-15 20:45:57 -05:00
}
// Make sure that the diskID is found in the set.
2020-06-12 23:04:01 -04:00
for i := 0 ; i < len ( tmpFormat . Erasure . Sets ) ; i ++ {
for j := 0 ; j < len ( tmpFormat . Erasure . Sets [ i ] ) ; j ++ {
if this == tmpFormat . Erasure . Sets [ i ] [ j ] {
2018-02-15 20:45:57 -05:00
return nil
2016-07-28 19:49:59 -04:00
}
}
}
2020-06-12 23:04:01 -04:00
return fmt . Errorf ( "Disk ID %s not found in any disk sets %s" , this , format . Erasure . Sets )
2016-07-28 19:49:59 -04:00
}
2020-01-15 15:36:52 -05:00
// Initializes meta volume only on local storage disks.
2020-06-12 23:04:01 -04:00
func initErasureMetaVolumesInLocalDisks ( storageDisks [ ] StorageAPI , formats [ ] * formatErasureV3 ) error {
2020-01-15 15:36:52 -05:00
// Compute the local disks eligible for meta volumes (re)initialization
var disksToInit [ ] StorageAPI
for index := range storageDisks {
2020-05-19 17:27:20 -04:00
if formats [ index ] == nil || storageDisks [ index ] == nil || ! storageDisks [ index ] . IsLocal ( ) {
2020-01-15 15:36:52 -05:00
// Ignore create meta volume on disks which are not found or not local.
continue
}
disksToInit = append ( disksToInit , storageDisks [ index ] )
}
2019-12-23 19:31:03 -05:00
// Initialize errs to collect errors inside go-routine.
2020-01-15 15:36:52 -05:00
g := errgroup . WithNErrs ( len ( disksToInit ) )
2019-12-23 19:31:03 -05:00
// Initialize all disks in parallel.
2020-01-15 15:36:52 -05:00
for index := range disksToInit {
// Initialize a new index variable in each loop so each
// goroutine will return its own instance of index variable.
2019-12-23 19:31:03 -05:00
index := index
g . Go ( func ( ) error {
2020-06-12 23:04:01 -04:00
return makeFormatErasureMetaVolumes ( disksToInit [ index ] )
2019-12-23 19:31:03 -05:00
} , index )
}
// Return upon first error.
for _ , err := range g . Wait ( ) {
if err == nil {
continue
}
return toObjectErr ( err , minioMetaBucket )
}
// Return success here.
return nil
}
2020-09-28 22:39:32 -04:00
// saveFormatErasureAllWithErrs - populates `format.json` on disks in its order.
// also adds `.healing.bin` on the disks which are being actively healed.
func saveFormatErasureAllWithErrs ( ctx context . Context , storageDisks [ ] StorageAPI , fErrs [ ] error , formats [ ] * formatErasureV3 ) error {
g := errgroup . WithNErrs ( len ( storageDisks ) )
// Write `format.json` to all disks.
for index := range storageDisks {
index := index
g . Go ( func ( ) error {
if formats [ index ] == nil {
return errDiskNotFound
}
return saveFormatErasure ( storageDisks [ index ] , formats [ index ] , errors . Is ( fErrs [ index ] , errUnformattedDisk ) )
} , index )
}
writeQuorum := getWriteQuorum ( len ( storageDisks ) )
// Wait for the routines to finish.
return reduceWriteQuorumErrs ( ctx , g . Wait ( ) , nil , writeQuorum )
}
2020-06-12 23:04:01 -04:00
// saveFormatErasureAll - populates `format.json` on disks in its order.
func saveFormatErasureAll ( ctx context . Context , storageDisks [ ] StorageAPI , formats [ ] * formatErasureV3 ) error {
2019-10-14 12:44:51 -04:00
g := errgroup . WithNErrs ( len ( storageDisks ) )
2016-07-28 19:49:59 -04:00
2018-02-15 20:45:57 -05:00
// Write `format.json` to all disks.
2019-10-14 12:44:51 -04:00
for index := range storageDisks {
index := index
g . Go ( func ( ) error {
2020-07-17 13:08:04 -04:00
if formats [ index ] == nil {
return errDiskNotFound
}
2020-09-28 22:39:32 -04:00
return saveFormatErasure ( storageDisks [ index ] , formats [ index ] , false )
2019-10-14 12:44:51 -04:00
} , index )
2016-07-28 19:49:59 -04:00
}
2020-03-31 12:32:16 -04:00
writeQuorum := getWriteQuorum ( len ( storageDisks ) )
2019-10-14 12:44:51 -04:00
// Wait for the routines to finish.
return reduceWriteQuorumErrs ( ctx , g . Wait ( ) , nil , writeQuorum )
2018-02-15 20:45:57 -05:00
}
2018-04-04 00:58:48 -04:00
// relinquishes the underlying connection for all storage disks.
func closeStorageDisks ( storageDisks [ ] StorageAPI ) {
for _ , disk := range storageDisks {
if disk == nil {
continue
}
disk . Close ( )
}
}
2019-09-27 19:47:12 -04:00
// Initialize storage disks for each endpoint.
// Errors are returned for each endpoint with matching index.
2019-11-19 20:42:27 -05:00
func initStorageDisksWithErrors ( endpoints Endpoints ) ( [ ] StorageAPI , [ ] error ) {
2018-02-15 20:45:57 -05:00
// Bootstrap disks.
storageDisks := make ( [ ] StorageAPI , len ( endpoints ) )
2019-10-14 12:44:51 -04:00
g := errgroup . WithNErrs ( len ( endpoints ) )
for index := range endpoints {
index := index
2020-08-18 17:37:26 -04:00
g . Go ( func ( ) ( err error ) {
storageDisks [ index ] , err = newStorageAPI ( endpoints [ index ] )
return err
2019-10-14 12:44:51 -04:00
} , index )
2019-08-02 15:17:26 -04:00
}
2019-10-14 12:44:51 -04:00
return storageDisks , g . Wait ( )
2019-08-02 15:17:26 -04:00
}
2020-06-12 23:04:01 -04:00
// formatErasureV3ThisEmpty - find out if '.This' field is empty
2018-03-19 12:13:00 -04:00
// in any of the input `formats`, if yes return true.
2020-06-12 23:04:01 -04:00
func formatErasureV3ThisEmpty ( formats [ ] * formatErasureV3 ) bool {
2018-03-19 12:13:00 -04:00
for _ , format := range formats {
if format == nil {
continue
}
// NOTE: This code is specifically needed when migrating version
// V1 to V2 to V3, in a scenario such as this we only need to handle
// single sets since we never used to support multiple sets in releases
// with V1 format version.
2020-06-12 23:04:01 -04:00
if len ( format . Erasure . Sets ) > 1 {
2018-03-19 12:13:00 -04:00
continue
}
2020-06-12 23:04:01 -04:00
if format . Erasure . This == "" {
2018-03-19 12:13:00 -04:00
return true
}
}
return false
}
2020-06-12 23:04:01 -04:00
// fixFormatErasureV3 - fix format Erasure configuration on all disks.
func fixFormatErasureV3 ( storageDisks [ ] StorageAPI , endpoints Endpoints , formats [ ] * formatErasureV3 ) error {
2020-03-27 17:48:30 -04:00
g := errgroup . WithNErrs ( len ( formats ) )
for i := range formats {
i := i
g . Go ( func ( ) error {
if formats [ i ] == nil || ! endpoints [ i ] . IsLocal {
return nil
}
// NOTE: This code is specifically needed when migrating version
// V1 to V2 to V3, in a scenario such as this we only need to handle
// single sets since we never used to support multiple sets in releases
// with V1 format version.
2020-06-12 23:04:01 -04:00
if len ( formats [ i ] . Erasure . Sets ) > 1 {
2020-03-27 17:48:30 -04:00
return nil
}
2020-06-12 23:04:01 -04:00
if formats [ i ] . Erasure . This == "" {
formats [ i ] . Erasure . This = formats [ i ] . Erasure . Sets [ 0 ] [ i ]
2020-09-28 22:39:32 -04:00
// Heal the drive if drive has .This empty.
if err := saveFormatErasure ( storageDisks [ i ] , formats [ i ] , true ) ; err != nil {
2020-03-27 17:48:30 -04:00
return err
}
2018-03-19 12:13:00 -04:00
}
2020-03-27 17:48:30 -04:00
return nil
} , i )
}
for _ , err := range g . Wait ( ) {
if err != nil {
return err
2018-03-19 12:13:00 -04:00
}
}
return nil
2020-03-27 17:48:30 -04:00
2018-03-19 12:13:00 -04:00
}
2020-06-12 23:04:01 -04:00
// initFormatErasure - save Erasure format configuration on all disks.
2020-10-24 16:23:08 -04:00
func initFormatErasure ( ctx context . Context , storageDisks [ ] StorageAPI , setCount , setDriveCount int , distributionAlgo string , deploymentID string , sErrs [ ] error ) ( * formatErasureV3 , error ) {
format := newFormatErasureV3 ( setCount , setDriveCount , distributionAlgo )
2020-06-12 23:04:01 -04:00
formats := make ( [ ] * formatErasureV3 , len ( storageDisks ) )
2020-08-26 22:29:35 -04:00
wantAtMost := ecDrivesNoConfig ( setDriveCount )
2016-05-04 15:18:20 -04:00
2018-02-15 20:45:57 -05:00
for i := 0 ; i < setCount ; i ++ {
2020-08-26 22:29:35 -04:00
hostCount := make ( map [ string ] int , setDriveCount )
for j := 0 ; j < setDriveCount ; j ++ {
disk := storageDisks [ i * setDriveCount + j ]
2019-11-21 07:24:51 -05:00
newFormat := format . Clone ( )
2020-06-12 23:04:01 -04:00
newFormat . Erasure . This = format . Erasure . Sets [ i ] [ j ]
2019-11-20 05:09:30 -05:00
if deploymentID != "" {
newFormat . ID = deploymentID
}
2020-01-13 16:09:10 -05:00
hostCount [ disk . Hostname ( ) ] ++
2020-08-26 22:29:35 -04:00
formats [ i * setDriveCount + j ] = newFormat
2018-02-15 20:45:57 -05:00
}
2020-01-13 16:09:10 -05:00
if len ( hostCount ) > 0 {
var once sync . Once
for host , count := range hostCount {
if count > wantAtMost {
if host == "" {
host = "local"
}
once . Do ( func ( ) {
if len ( hostCount ) == 1 {
return
}
logger . Info ( " * Set %v:" , i + 1 )
2020-08-26 22:29:35 -04:00
for j := 0 ; j < setDriveCount ; j ++ {
disk := storageDisks [ i * setDriveCount + j ]
2020-01-13 16:09:10 -05:00
logger . Info ( " - Drive: %s" , disk . String ( ) )
}
} )
logger . Info ( color . Yellow ( "WARNING:" ) + " Host %v has more than %v drives of set. " +
"A host failure will result in data becoming unavailable." , host , wantAtMost )
}
}
}
2016-05-04 15:18:20 -04:00
}
2020-08-18 17:37:26 -04:00
// Mark all root disks down
markRootDisksAsDown ( storageDisks , sErrs )
2018-02-15 20:45:57 -05:00
// Save formats `format.json` across all disks.
2020-06-12 23:04:01 -04:00
if err := saveFormatErasureAll ( ctx , storageDisks , formats ) ; err != nil {
2018-02-15 20:45:57 -05:00
return nil , err
2017-04-18 13:35:17 -04:00
}
2018-02-15 20:45:57 -05:00
2020-06-12 23:04:01 -04:00
return getFormatErasureInQuorum ( formats )
2017-04-18 13:35:17 -04:00
}
2020-01-13 16:09:10 -05:00
// ecDrivesNoConfig returns the erasure coded drives in a set if no config has been set.
// It will attempt to read it from env variable and fall back to drives/2.
2020-08-26 22:29:35 -04:00
func ecDrivesNoConfig ( setDriveCount int ) int {
2020-01-13 16:09:10 -05:00
ecDrives := globalStorageClass . GetParityForSC ( storageclass . STANDARD )
if ecDrives == 0 {
2020-08-26 22:29:35 -04:00
cfg , err := storageclass . LookupConfig ( nil , setDriveCount )
2020-01-13 16:09:10 -05:00
if err == nil {
ecDrives = cfg . Standard . Parity
}
if ecDrives == 0 {
2020-08-26 22:29:35 -04:00
ecDrives = setDriveCount / 2
2020-01-13 16:09:10 -05:00
}
}
return ecDrives
}
2020-06-12 23:04:01 -04:00
// Make Erasure backend meta volumes.
func makeFormatErasureMetaVolumes ( disk StorageAPI ) error {
2020-03-27 17:48:30 -04:00
if disk == nil {
return errDiskNotFound
}
2019-12-23 19:31:03 -05:00
// Attempt to create MinIO internal buckets.
2020-09-04 12:45:06 -04:00
return disk . MakeVolBulk ( context . TODO ( ) , minioMetaBucket , minioMetaTmpBucket , minioMetaMultipartBucket , dataUsageBucket )
2016-12-11 18:18:55 -05:00
}
2018-02-15 20:45:57 -05:00
// Get all UUIDs which are present in reference format should
// be present in the list of formats provided, those are considered
// as online UUIDs.
2020-06-12 23:04:01 -04:00
func getOnlineUUIDs ( refFormat * formatErasureV3 , formats [ ] * formatErasureV3 ) ( onlineUUIDs [ ] string ) {
2018-02-15 20:45:57 -05:00
for _ , format := range formats {
if format == nil {
continue
}
2020-06-12 23:04:01 -04:00
for _ , set := range refFormat . Erasure . Sets {
2018-02-15 20:45:57 -05:00
for _ , uuid := range set {
2020-06-12 23:04:01 -04:00
if format . Erasure . This == uuid {
2018-02-15 20:45:57 -05:00
onlineUUIDs = append ( onlineUUIDs , uuid )
}
}
}
2016-05-07 03:59:43 -04:00
}
2018-02-15 20:45:57 -05:00
return onlineUUIDs
}
2016-06-22 20:18:31 -04:00
2018-02-15 20:45:57 -05:00
// Look for all UUIDs which are not present in reference format
// but are present in the onlineUUIDs list, construct of list such
// offline UUIDs.
2020-06-12 23:04:01 -04:00
func getOfflineUUIDs ( refFormat * formatErasureV3 , formats [ ] * formatErasureV3 ) ( offlineUUIDs [ ] string ) {
2018-02-15 20:45:57 -05:00
onlineUUIDs := getOnlineUUIDs ( refFormat , formats )
2020-06-12 23:04:01 -04:00
for i , set := range refFormat . Erasure . Sets {
2018-02-15 20:45:57 -05:00
for j , uuid := range set {
var found bool
for _ , onlineUUID := range onlineUUIDs {
2020-06-12 23:04:01 -04:00
if refFormat . Erasure . Sets [ i ] [ j ] == onlineUUID {
2018-02-15 20:45:57 -05:00
found = true
}
}
if ! found {
offlineUUIDs = append ( offlineUUIDs , uuid )
}
}
2016-05-04 15:18:20 -04:00
}
2018-02-15 20:45:57 -05:00
return offlineUUIDs
2018-01-08 17:30:55 -05:00
}
2018-02-15 20:45:57 -05:00
// Mark all UUIDs that are offline.
2020-10-24 16:23:08 -04:00
func markUUIDsOffline ( refFormat * formatErasureV3 , formats [ ] * formatErasureV3 , errs [ ] error ) {
2018-02-15 20:45:57 -05:00
offlineUUIDs := getOfflineUUIDs ( refFormat , formats )
2020-06-12 23:04:01 -04:00
for i , set := range refFormat . Erasure . Sets {
2020-10-24 16:23:08 -04:00
setDriveCount := len ( set )
2018-02-15 20:45:57 -05:00
for j := range set {
for _ , offlineUUID := range offlineUUIDs {
2020-10-24 16:23:08 -04:00
if refFormat . Erasure . Sets [ i ] [ j ] == offlineUUID &&
errors . Is ( errs [ i * setDriveCount + j ] , errUnformattedDisk ) {
// Unformatted drive gets an offline disk UUID
2020-06-12 23:04:01 -04:00
refFormat . Erasure . Sets [ i ] [ j ] = offlineDiskUUID
2018-02-15 20:45:57 -05:00
}
}
}
2016-08-30 22:22:27 -04:00
}
2018-02-15 20:45:57 -05:00
}
2016-08-30 22:22:27 -04:00
2018-02-15 20:45:57 -05:00
// Initialize a new set of set formats which will be written to all disks.
2020-08-26 22:29:35 -04:00
func newHealFormatSets ( refFormat * formatErasureV3 , setCount , setDriveCount int , formats [ ] * formatErasureV3 , errs [ ] error ) [ ] [ ] * formatErasureV3 {
2020-06-12 23:04:01 -04:00
newFormats := make ( [ ] [ ] * formatErasureV3 , setCount )
for i := range refFormat . Erasure . Sets {
2020-08-26 22:29:35 -04:00
newFormats [ i ] = make ( [ ] * formatErasureV3 , setDriveCount )
2018-02-15 20:45:57 -05:00
}
2020-06-12 23:04:01 -04:00
for i := range refFormat . Erasure . Sets {
for j := range refFormat . Erasure . Sets [ i ] {
2020-08-26 22:29:35 -04:00
if errs [ i * setDriveCount + j ] == errUnformattedDisk || errs [ i * setDriveCount + j ] == nil {
2020-06-12 23:04:01 -04:00
newFormats [ i ] [ j ] = & formatErasureV3 { }
2018-02-15 20:45:57 -05:00
newFormats [ i ] [ j ] . Version = refFormat . Version
2019-01-22 21:32:06 -05:00
newFormats [ i ] [ j ] . ID = refFormat . ID
2018-02-15 20:45:57 -05:00
newFormats [ i ] [ j ] . Format = refFormat . Format
2020-06-12 23:04:01 -04:00
newFormats [ i ] [ j ] . Erasure . Version = refFormat . Erasure . Version
newFormats [ i ] [ j ] . Erasure . DistributionAlgo = refFormat . Erasure . DistributionAlgo
2018-02-15 20:45:57 -05:00
}
2020-08-26 22:29:35 -04:00
if errs [ i * setDriveCount + j ] == errUnformattedDisk {
2020-06-12 23:04:01 -04:00
newFormats [ i ] [ j ] . Erasure . This = ""
newFormats [ i ] [ j ] . Erasure . Sets = nil
2018-02-15 20:45:57 -05:00
continue
}
2020-08-26 22:29:35 -04:00
if errs [ i * setDriveCount + j ] == nil {
newFormats [ i ] [ j ] . Erasure . This = formats [ i * setDriveCount + j ] . Erasure . This
2020-06-12 23:04:01 -04:00
newFormats [ i ] [ j ] . Erasure . Sets = nil
2018-02-15 20:45:57 -05:00
}
}
}
return newFormats
2016-05-04 15:18:20 -04:00
}