| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2018 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08: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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"hash" | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/minio/highwayhash" | 
					
						
							|  |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | 	sha256 "github.com/minio/sha256-simd" | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | 	"golang.org/x/crypto/blake2b" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // magic HH-256 key as HH-256 hash of the first 100 decimals of π as utf-8 string with a zero key.
 | 
					
						
							|  |  |  | var magicHighwayHash256Key = []byte("\x4b\xe7\x34\xfa\x8e\x23\x8a\xcd\x26\x3e\x83\xe6\xbb\x96\x85\x52\x04\x0f\x93\x5d\xa3\x9f\x44\x14\x97\xe0\x9d\x13\x22\xde\x36\xa0") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var bitrotAlgorithms = map[BitrotAlgorithm]string{ | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	SHA256:          "sha256", | 
					
						
							|  |  |  | 	BLAKE2b512:      "blake2b", | 
					
						
							|  |  |  | 	HighwayHash256:  "highwayhash256", | 
					
						
							|  |  |  | 	HighwayHash256S: "highwayhash256S", | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // New returns a new hash.Hash calculating the given bitrot algorithm.
 | 
					
						
							|  |  |  | func (a BitrotAlgorithm) New() hash.Hash { | 
					
						
							|  |  |  | 	switch a { | 
					
						
							|  |  |  | 	case SHA256: | 
					
						
							|  |  |  | 		return sha256.New() | 
					
						
							|  |  |  | 	case BLAKE2b512: | 
					
						
							|  |  |  | 		b2, _ := blake2b.New512(nil) // New512 never returns an error if the key is nil
 | 
					
						
							|  |  |  | 		return b2 | 
					
						
							|  |  |  | 	case HighwayHash256: | 
					
						
							|  |  |  | 		hh, _ := highwayhash.New(magicHighwayHash256Key) // New will never return error since key is 256 bit
 | 
					
						
							|  |  |  | 		return hh | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	case HighwayHash256S: | 
					
						
							|  |  |  | 		hh, _ := highwayhash.New(magicHighwayHash256Key) // New will never return error since key is 256 bit
 | 
					
						
							|  |  |  | 		return hh | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2020-04-10 00:30:02 +08:00
										 |  |  | 		logger.CriticalIf(GlobalContext, errors.New("Unsupported bitrot algorithm")) | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | // Available reports whether the given algorithm is available.
 | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | func (a BitrotAlgorithm) Available() bool { | 
					
						
							|  |  |  | 	_, ok := bitrotAlgorithms[a] | 
					
						
							|  |  |  | 	return ok | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // String returns the string identifier for a given bitrot algorithm.
 | 
					
						
							|  |  |  | // If the algorithm is not supported String panics.
 | 
					
						
							|  |  |  | func (a BitrotAlgorithm) String() string { | 
					
						
							|  |  |  | 	name, ok := bitrotAlgorithms[a] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2020-04-10 00:30:02 +08:00
										 |  |  | 		logger.CriticalIf(GlobalContext, errors.New("Unsupported bitrot algorithm")) | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return name | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewBitrotVerifier returns a new BitrotVerifier implementing the given algorithm.
 | 
					
						
							|  |  |  | func NewBitrotVerifier(algorithm BitrotAlgorithm, checksum []byte) *BitrotVerifier { | 
					
						
							|  |  |  | 	return &BitrotVerifier{algorithm, checksum} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // BitrotVerifier can be used to verify protected data.
 | 
					
						
							|  |  |  | type BitrotVerifier struct { | 
					
						
							|  |  |  | 	algorithm BitrotAlgorithm | 
					
						
							|  |  |  | 	sum       []byte | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // BitrotAlgorithmFromString returns a bitrot algorithm from the given string representation.
 | 
					
						
							|  |  |  | // It returns 0 if the string representation does not match any supported algorithm.
 | 
					
						
							|  |  |  | // The zero value of a bitrot algorithm is never supported.
 | 
					
						
							|  |  |  | func BitrotAlgorithmFromString(s string) (a BitrotAlgorithm) { | 
					
						
							|  |  |  | 	for alg, name := range bitrotAlgorithms { | 
					
						
							|  |  |  | 		if name == s { | 
					
						
							|  |  |  | 			return alg | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | func newBitrotWriter(disk StorageAPI, volume, filePath string, length int64, algo BitrotAlgorithm, shardSize int64) io.Writer { | 
					
						
							|  |  |  | 	if algo == HighwayHash256S { | 
					
						
							|  |  |  | 		return newStreamingBitrotWriter(disk, volume, filePath, length, algo, shardSize) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-05-01 07:27:31 +08:00
										 |  |  | 	return newWholeBitrotWriter(disk, volume, filePath, algo, shardSize) | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | func newBitrotReader(disk StorageAPI, bucket string, filePath string, tillOffset int64, algo BitrotAlgorithm, sum []byte, shardSize int64) io.ReaderAt { | 
					
						
							|  |  |  | 	if algo == HighwayHash256S { | 
					
						
							|  |  |  | 		return newStreamingBitrotReader(disk, bucket, filePath, tillOffset, algo, shardSize) | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	return newWholeBitrotReader(disk, bucket, filePath, algo, tillOffset, sum) | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | // Close all the readers.
 | 
					
						
							|  |  |  | func closeBitrotReaders(rs []io.ReaderAt) { | 
					
						
							|  |  |  | 	for _, r := range rs { | 
					
						
							| 
									
										
										
										
											2019-03-27 04:59:33 +08:00
										 |  |  | 		if br, ok := r.(io.Closer); ok { | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 			br.Close() | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | // Close all the writers.
 | 
					
						
							|  |  |  | func closeBitrotWriters(ws []io.Writer) { | 
					
						
							|  |  |  | 	for _, w := range ws { | 
					
						
							| 
									
										
										
										
											2019-03-27 04:59:33 +08:00
										 |  |  | 		if bw, ok := w.(io.Closer); ok { | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 			bw.Close() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | // Returns hash sum for whole-bitrot, nil for streaming-bitrot.
 | 
					
						
							|  |  |  | func bitrotWriterSum(w io.Writer) []byte { | 
					
						
							|  |  |  | 	if bw, ok := w.(*wholeBitrotWriter); ok { | 
					
						
							|  |  |  | 		return bw.Sum(nil) | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-12 04:49:53 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Returns the size of the file with bitrot protection
 | 
					
						
							|  |  |  | func bitrotShardFileSize(size int64, shardSize int64, algo BitrotAlgorithm) int64 { | 
					
						
							|  |  |  | 	if algo != HighwayHash256S { | 
					
						
							|  |  |  | 		return size | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ceilFrac(size, shardSize)*int64(algo.New().Size()) + size | 
					
						
							|  |  |  | } |