gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/uploadsourcereader.go (about)

     1  package renter
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  
     7  	"gitlab.com/NebulousLabs/errors"
     8  	"gitlab.com/SkynetLabs/skyd/skymodules"
     9  	"go.sia.tech/siad/crypto"
    10  	"go.sia.tech/siad/modules"
    11  )
    12  
    13  // chunkReader implements the ChunkReader interface by wrapping a io.Reader.
    14  type chunkReader struct {
    15  	staticEC        skymodules.ErasureCoder
    16  	staticMasterKey crypto.CipherKey
    17  	staticPieceSize uint64
    18  	staticReader    io.Reader
    19  
    20  	chunkIndex uint64
    21  	peek       []byte
    22  }
    23  
    24  // fanoutChunkReader implements the FanoutChunkReader interface by wrapping a
    25  // ChunkReader.
    26  type fanoutChunkReader struct {
    27  	skymodules.ChunkReader
    28  	fanout         []byte
    29  	staticOnePiece bool
    30  }
    31  
    32  // NewChunkReader creates a new chunkReader.
    33  func NewChunkReader(r io.Reader, ec skymodules.ErasureCoder, mk crypto.CipherKey) skymodules.ChunkReader {
    34  	return NewChunkReaderWithChunkIndex(r, ec, mk, 0)
    35  }
    36  
    37  // NewChunkReaderWithChunkIndex creates a new chunkReader that starts encryption
    38  // at a certain index.
    39  func NewChunkReaderWithChunkIndex(r io.Reader, ec skymodules.ErasureCoder, mk crypto.CipherKey, chunkIndex uint64) skymodules.ChunkReader {
    40  	return &chunkReader{
    41  		chunkIndex:      chunkIndex,
    42  		staticReader:    r,
    43  		staticEC:        ec,
    44  		staticPieceSize: modules.SectorSize - mk.Type().Overhead(),
    45  		staticMasterKey: mk,
    46  	}
    47  }
    48  
    49  // NewFanoutChunkReader creates a new fanoutChunkReader.
    50  func NewFanoutChunkReader(r io.Reader, ec skymodules.ErasureCoder, mk crypto.CipherKey) skymodules.FanoutChunkReader {
    51  	return &fanoutChunkReader{
    52  		ChunkReader:    NewChunkReader(r, ec, mk),
    53  		staticOnePiece: isCompressedFanout(ec, mk.Type()),
    54  	}
    55  }
    56  
    57  // Peek returns whether the next call to ReadChunk is expected to return a
    58  // chunk or if there is no more data.
    59  func (cr *chunkReader) Peek() bool {
    60  	// If 'peek' already has data, then there is more data to consume.
    61  	if len(cr.peek) > 0 {
    62  		return true
    63  	}
    64  
    65  	// Read a byte into peek.
    66  	cr.peek = append(cr.peek, 0)
    67  	_, err := io.ReadFull(cr.staticReader, cr.peek)
    68  	if err != nil {
    69  		return false
    70  	}
    71  	return true
    72  }
    73  
    74  // ReadChunk reads the next chunk from the reader. The returned chunk is erasure
    75  // coded and will always be a full chunk. It also returns the number of bytes
    76  // that this chunk was created from which is useful because the last chunk might
    77  // be padded.
    78  func (cr *chunkReader) ReadChunk() ([][]byte, uint64, error) {
    79  	r := io.MultiReader(bytes.NewReader(cr.peek), cr.staticReader)
    80  	dataPieces, n, err := readDataPieces(r, cr.staticEC, cr.staticPieceSize)
    81  	if err != nil {
    82  		return nil, 0, errors.AddContext(err, "ReadChunk: failed to read data pieces")
    83  	}
    84  	if n == 0 {
    85  		return nil, 0, io.EOF
    86  	}
    87  	logicalChunkData, err := cr.staticEC.EncodeShards(dataPieces)
    88  	if err != nil {
    89  		return nil, 0, errors.AddContext(err, "ReadChunk: failed to encode logical chunk data")
    90  	}
    91  	for pieceIndex := range logicalChunkData {
    92  		padAndEncryptPiece(cr.chunkIndex, uint64(pieceIndex), logicalChunkData, cr.staticMasterKey)
    93  	}
    94  	cr.peek = nil
    95  	cr.chunkIndex++
    96  	return logicalChunkData, n, nil
    97  }
    98  
    99  // Fanout returns the current fanout.
   100  func (cr *fanoutChunkReader) Fanout() []byte {
   101  	return cr.fanout
   102  }
   103  
   104  // ReadChunk reads the next chunk from the reader. The returned chunk is erasure
   105  // coded and will always be a full chunk. It also returns the number of bytes
   106  // that this chunk was created from which is useful because the last chunk might
   107  // be padded.
   108  func (cr *fanoutChunkReader) ReadChunk() ([][]byte, uint64, error) {
   109  	// If the chunk was read successfully, append the fanout.
   110  	chunk, n, err := cr.ChunkReader.ReadChunk()
   111  	if err != nil {
   112  		return chunk, n, err
   113  	}
   114  	// Append the root to the fanout.
   115  	for pieceIndex := range chunk {
   116  		root := crypto.MerkleRoot(chunk[pieceIndex])
   117  		cr.fanout = append(cr.fanout, root[:]...)
   118  
   119  		// If only one piece is needed break out of the inner loop.
   120  		if cr.staticOnePiece {
   121  			break
   122  		}
   123  	}
   124  	return chunk, n, nil
   125  }