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 }