| 
									
										
										
										
											2021-04-19 03:41:13 +08: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/>.
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2020-08-21 00:41:15 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	"hash" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Implementation to calculate bitrot for the whole file.
 | 
					
						
							|  |  |  | type wholeBitrotWriter struct { | 
					
						
							|  |  |  | 	disk      StorageAPI | 
					
						
							|  |  |  | 	volume    string | 
					
						
							|  |  |  | 	filePath  string | 
					
						
							|  |  |  | 	shardSize int64 // This is the shard size of the erasure logic
 | 
					
						
							|  |  |  | 	hash.Hash       // For bitrot hash
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (b *wholeBitrotWriter) Write(p []byte) (int, error) { | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	err := b.disk.AppendFile(context.TODO(), b.volume, b.filePath, p) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-08-21 00:41:15 +08:00
										 |  |  | 		logger.LogIf(GlobalContext, fmt.Errorf("Disk: %s returned %w", b.disk, err)) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	_, err = b.Hash.Write(p) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-08-21 00:41:15 +08:00
										 |  |  | 		logger.LogIf(GlobalContext, fmt.Errorf("Disk: %s returned %w", b.disk, err)) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return len(p), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (b *wholeBitrotWriter) Close() error { | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Returns whole-file bitrot writer.
 | 
					
						
							| 
									
										
										
										
											2019-05-01 07:27:31 +08:00
										 |  |  | func newWholeBitrotWriter(disk StorageAPI, volume, filePath string, algo BitrotAlgorithm, shardSize int64) io.WriteCloser { | 
					
						
							|  |  |  | 	return &wholeBitrotWriter{disk, volume, filePath, shardSize, algo.New()} | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Implementation to verify bitrot for the whole file.
 | 
					
						
							|  |  |  | type wholeBitrotReader struct { | 
					
						
							|  |  |  | 	disk       StorageAPI | 
					
						
							|  |  |  | 	volume     string | 
					
						
							|  |  |  | 	filePath   string | 
					
						
							|  |  |  | 	verifier   *BitrotVerifier // Holds the bit-rot info
 | 
					
						
							|  |  |  | 	tillOffset int64           // Affects the length of data requested in disk.ReadFile depending on Read()'s offset
 | 
					
						
							|  |  |  | 	buf        []byte          // Holds bit-rot verified data
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (b *wholeBitrotReader) ReadAt(buf []byte, offset int64) (n int, err error) { | 
					
						
							|  |  |  | 	if b.buf == nil { | 
					
						
							|  |  |  | 		b.buf = make([]byte, b.tillOffset-offset) | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 		if _, err := b.disk.ReadFile(context.TODO(), b.volume, b.filePath, offset, b.buf, b.verifier); err != nil { | 
					
						
							| 
									
										
										
										
											2020-09-19 03:09:05 +08:00
										 |  |  | 			logger.LogIf(GlobalContext, fmt.Errorf("Disk: %s -> %s/%s returned %w", b.disk, b.volume, b.filePath, err)) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 			return 0, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(b.buf) < len(buf) { | 
					
						
							| 
									
										
										
										
											2020-09-19 03:09:05 +08:00
										 |  |  | 		logger.LogIf(GlobalContext, fmt.Errorf("Disk: %s -> %s/%s returned %w", b.disk, b.volume, b.filePath, errLessData)) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 		return 0, errLessData | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	n = copy(buf, b.buf) | 
					
						
							|  |  |  | 	b.buf = b.buf[n:] | 
					
						
							|  |  |  | 	return n, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Returns whole-file bitrot reader.
 | 
					
						
							|  |  |  | func newWholeBitrotReader(disk StorageAPI, volume, filePath string, algo BitrotAlgorithm, tillOffset int64, sum []byte) *wholeBitrotReader { | 
					
						
							|  |  |  | 	return &wholeBitrotReader{ | 
					
						
							|  |  |  | 		disk:       disk, | 
					
						
							|  |  |  | 		volume:     volume, | 
					
						
							|  |  |  | 		filePath:   filePath, | 
					
						
							|  |  |  | 		verifier:   &BitrotVerifier{algo, sum}, | 
					
						
							|  |  |  | 		tillOffset: tillOffset, | 
					
						
							|  |  |  | 		buf:        nil, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |