storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/erasure-utils.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2016-2020 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cmd 18 19 import ( 20 "bytes" 21 "context" 22 "fmt" 23 "io" 24 25 "github.com/klauspost/reedsolomon" 26 27 "storj.io/minio/cmd/logger" 28 ) 29 30 // getDataBlockLen - get length of data blocks from encoded blocks. 31 func getDataBlockLen(enBlocks [][]byte, dataBlocks int) int { 32 size := 0 33 // Figure out the data block length. 34 for _, block := range enBlocks[:dataBlocks] { 35 size += len(block) 36 } 37 return size 38 } 39 40 // Writes all the data blocks from encoded blocks until requested 41 // outSize length. Provides a way to skip bytes until the offset. 42 func writeDataBlocks(ctx context.Context, dst io.Writer, enBlocks [][]byte, dataBlocks int, offset int64, length int64) (int64, error) { 43 // Offset and out size cannot be negative. 44 if offset < 0 || length < 0 { 45 logger.LogIf(ctx, errUnexpected) 46 return 0, errUnexpected 47 } 48 49 // Do we have enough blocks? 50 if len(enBlocks) < dataBlocks { 51 logger.LogIf(ctx, fmt.Errorf("diskBlocks(%d)/dataBlocks(%d) - %w", len(enBlocks), dataBlocks, reedsolomon.ErrTooFewShards)) 52 return 0, reedsolomon.ErrTooFewShards 53 } 54 55 // Do we have enough data? 56 if int64(getDataBlockLen(enBlocks, dataBlocks)) < length { 57 logger.LogIf(ctx, fmt.Errorf("getDataBlockLen(enBlocks, dataBlocks)(%d)/length(%d) - %w", getDataBlockLen(enBlocks, dataBlocks), length, reedsolomon.ErrShortData)) 58 return 0, reedsolomon.ErrShortData 59 } 60 61 // Counter to decrement total left to write. 62 write := length 63 64 // Counter to increment total written. 65 var totalWritten int64 66 67 // Write all data blocks to dst. 68 for _, block := range enBlocks[:dataBlocks] { 69 // Skip blocks until we have reached our offset. 70 if offset >= int64(len(block)) { 71 // Decrement offset. 72 offset -= int64(len(block)) 73 continue 74 } else { 75 // Skip until offset. 76 block = block[offset:] 77 78 // Reset the offset for next iteration to read everything 79 // from subsequent blocks. 80 offset = 0 81 } 82 83 // We have written all the blocks, write the last remaining block. 84 if write < int64(len(block)) { 85 n, err := io.Copy(dst, bytes.NewReader(block[:write])) 86 if err != nil { 87 if err != io.ErrClosedPipe { 88 logger.LogIf(ctx, err) 89 } 90 return 0, err 91 } 92 totalWritten += n 93 break 94 } 95 96 // Copy the block. 97 n, err := io.Copy(dst, bytes.NewReader(block)) 98 if err != nil { 99 // The writer will be closed incase of range queries, which will emit ErrClosedPipe. 100 if err != io.ErrClosedPipe { 101 logger.LogIf(ctx, err) 102 } 103 return 0, err 104 } 105 106 // Decrement output size. 107 write -= n 108 109 // Increment written. 110 totalWritten += n 111 } 112 113 // Success. 114 return totalWritten, nil 115 }