|
|
|
/*
|
|
|
|
* Minio Cloud Storage, (C) 2016 Minio, Inc.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import "hash"
|
|
|
|
|
|
|
|
// HealFile tries to reconstruct a bitrot encoded file spread over all available disks. HealFile will read the valid parts of the file,
|
|
|
|
// reconstruct the missing data and write the reconstructed parts back to the disks.
|
|
|
|
// It will try to read the valid parts from the file under the given volume and path and tries to reconstruct the file under the given
|
|
|
|
// healVolume and healPath. The given algorithm will be used to verify the valid parts and to protect the reconstructed file.
|
|
|
|
func (s ErasureStorage) HealFile(offlineDisks []StorageAPI, volume, path string, blocksize int64, healVolume, healPath string, size int64, algorithm BitrotAlgorithm, checksums [][]byte) (f ErasureFileInfo, err error) {
|
|
|
|
if !algorithm.Available() {
|
|
|
|
return f, traceError(errBitrotHashAlgoInvalid)
|
|
|
|
}
|
|
|
|
f.Checksums = make([][]byte, len(s.disks))
|
|
|
|
hashers, verifiers := make([]hash.Hash, len(s.disks)), make([]*BitrotVerifier, len(s.disks))
|
|
|
|
for i, disk := range s.disks {
|
|
|
|
if disk == OfflineDisk {
|
|
|
|
hashers[i] = algorithm.New()
|
|
|
|
} else {
|
|
|
|
verifiers[i] = NewBitrotVerifier(algorithm, checksums[i])
|
|
|
|
f.Checksums[i] = checksums[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
blocks := make([][]byte, len(s.disks))
|
|
|
|
chunksize := getChunkSize(blocksize, s.dataBlocks)
|
|
|
|
for offset := int64(0); offset < size; offset += blocksize {
|
|
|
|
if size < blocksize {
|
|
|
|
blocksize = size
|
|
|
|
chunksize = getChunkSize(blocksize, s.dataBlocks)
|
|
|
|
}
|
|
|
|
numReads := 0
|
|
|
|
for i, disk := range s.disks {
|
|
|
|
if disk != OfflineDisk {
|
|
|
|
if blocks[i] == nil {
|
|
|
|
blocks[i] = make([]byte, chunksize)
|
|
|
|
}
|
|
|
|
blocks[i] = blocks[i][:chunksize]
|
|
|
|
if !verifiers[i].IsVerified() {
|
|
|
|
_, err = disk.ReadFileWithVerify(volume, path, offset, blocks[i], verifiers[i])
|
|
|
|
} else {
|
|
|
|
_, err = disk.ReadFile(volume, path, offset, blocks[i])
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
blocks[i] = nil
|
|
|
|
} else {
|
|
|
|
numReads++
|
|
|
|
}
|
|
|
|
if numReads == s.dataBlocks { // we have enough data to reconstruct
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err = s.ErasureDecodeDataAndParityBlocks(blocks); err != nil {
|
|
|
|
return f, err
|
|
|
|
}
|
|
|
|
for i, disk := range s.disks {
|
|
|
|
if disk != OfflineDisk {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err = offlineDisks[i].AppendFile(healVolume, healPath, blocks[i]); err != nil {
|
|
|
|
return f, traceError(err)
|
|
|
|
}
|
|
|
|
hashers[i].Write(blocks[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
f.Size = size
|
|
|
|
f.Algorithm = algorithm
|
|
|
|
for i, disk := range s.disks {
|
|
|
|
if disk != OfflineDisk {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
f.Checksums[i] = hashers[i].Sum(nil)
|
|
|
|
}
|
|
|
|
return f, nil
|
|
|
|
}
|