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  }