github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/common/ledger/snapshot/file.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package snapshot 8 9 import ( 10 "bufio" 11 "encoding/binary" 12 "fmt" 13 "hash" 14 "io" 15 "os" 16 17 "github.com/golang/protobuf/proto" 18 "github.com/pkg/errors" 19 ) 20 21 type NewHashFunc func() (hash.Hash, error) 22 23 // FileWriter creates a new file for ledger snapshot. This is expected to be used by various 24 // components of ledger, such as blockstorage and statedb for exporting the relevant snapshot data 25 type FileWriter struct { 26 file *os.File 27 hasher hash.Hash 28 bufWriter *bufio.Writer 29 multiWriter io.Writer 30 varintReusableBuf []byte 31 } 32 33 // CreateFile creates a new file for exporting the ledger snapshot data 34 // This function returns an error if the file already exists. The `dataformat` is the first byte 35 // written to the file. The function newHash is used to construct an hash.Hash for computing the hash-sum of the data stream 36 func CreateFile(filePath string, dataformat byte, newHashFunc NewHashFunc) (*FileWriter, error) { 37 hashImpl, err := newHashFunc() 38 if err != nil { 39 return nil, err 40 } 41 // create the file only if it does not already exist. 42 // set the permission mode to read-only, as once the file is closed, we do not support modifying the file 43 file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0444) 44 if err != nil { 45 return nil, errors.Wrapf(err, "error while creating the snapshot file: %s", filePath) 46 } 47 bufWriter := bufio.NewWriter(file) 48 multiWriter := io.MultiWriter(bufWriter, hashImpl) 49 if _, err := multiWriter.Write([]byte{dataformat}); err != nil { 50 file.Close() 51 return nil, errors.Wrapf(err, "error while writing data format to the snapshot file: %s", filePath) 52 } 53 return &FileWriter{ 54 file: file, 55 bufWriter: bufWriter, 56 multiWriter: multiWriter, 57 hasher: hashImpl, 58 varintReusableBuf: make([]byte, binary.MaxVarintLen64), 59 }, nil 60 } 61 62 // EncodeString encodes and appends the string to the data stream 63 func (c *FileWriter) EncodeString(str string) error { 64 return c.EncodeBytes([]byte(str)) 65 } 66 67 // EncodeString encodes and appends a proto message to the data stream 68 func (c *FileWriter) EncodeProtoMessage(m proto.Message) error { 69 b, err := proto.Marshal(m) 70 if err != nil { 71 return errors.Wrapf(err, "error marshalling proto message to write to the snapshot file: %s", c.file.Name()) 72 } 73 return c.EncodeBytes(b) 74 } 75 76 // EncodeBytes encodes and appends bytes to the data stream 77 func (c *FileWriter) EncodeBytes(b []byte) error { 78 if err := c.EncodeUVarint(uint64(len(b))); err != nil { 79 return err 80 } 81 if _, err := c.multiWriter.Write(b); err != nil { 82 return errors.Wrapf(err, "error while writing data to the snapshot file: %s", c.file.Name()) 83 } 84 return nil 85 } 86 87 // EncodeUVarint encodes and appends a number to the data stream 88 func (c *FileWriter) EncodeUVarint(u uint64) error { 89 n := binary.PutUvarint(c.varintReusableBuf, u) 90 if _, err := c.multiWriter.Write(c.varintReusableBuf[:n]); err != nil { 91 return errors.Wrapf(err, "error while writing data to the snapshot file: %s", c.file.Name()) 92 } 93 return nil 94 } 95 96 // Done closes the snapshot file and returns the final hash of the data stream 97 func (c *FileWriter) Done() ([]byte, error) { 98 if err := c.bufWriter.Flush(); err != nil { 99 return nil, errors.Wrapf(err, "error while flushing to the snapshot file: %s ", c.file.Name()) 100 } 101 if err := c.file.Sync(); err != nil { 102 return nil, err 103 } 104 if err := c.file.Close(); err != nil { 105 return nil, errors.Wrapf(err, "error while closing the snapshot file: %s ", c.file.Name()) 106 } 107 return c.hasher.Sum(nil), nil 108 } 109 110 // Close closes the underlying file, if not already done. A consumer can invoke this function if the consumer 111 // encountered some error and simply wants to abandon the snapshot file creation (typically, intended to be used in a defer statement) 112 func (c *FileWriter) Close() error { 113 if c == nil { 114 return nil 115 } 116 return errors.Wrapf(c.file.Close(), "error while closing the snapshot file: %s", c.file.Name()) 117 } 118 119 // FileReader reads from a ledger snapshot file. This is expected to be used for loading the ledger snapshot data 120 // during bootstrapping a channel from snapshot. The data should be read, using the functions `DecodeXXX`, 121 // in the same sequence in which the data was written by the functions `EncodeXXX` in the `FileCreator`. 122 // Note that the FileReader does not verifies the hash of stream and it is expected that the hash has been verified 123 // by the consumer. Later, if we decide to perform this, on-the-side, while loading the snapshot data, the FileRedear, 124 // like the FileCreator, would take a `hasher` as an input 125 type FileReader struct { 126 file *os.File 127 bufReader *bufio.Reader 128 reusableByteSlice []byte 129 } 130 131 // OpenFile constructs a FileReader. This function returns an error if the format of the file, stored in the 132 // first byte, does not match with the expectedDataFormat 133 func OpenFile(filePath string, expectDataformat byte) (*FileReader, error) { 134 file, err := os.Open(filePath) 135 if err != nil { 136 return nil, errors.Wrapf(err, "error while opening the snapshot file: %s", filePath) 137 } 138 bufReader := bufio.NewReader(file) 139 dataFormat, err := bufReader.ReadByte() 140 if err != nil { 141 file.Close() 142 return nil, errors.Wrapf(err, "error while reading from the snapshot file: %s", filePath) 143 } 144 if dataFormat != expectDataformat { 145 file.Close() 146 return nil, errors.New(fmt.Sprintf("unexpected data format: %x", dataFormat)) 147 } 148 return &FileReader{ 149 file: file, 150 bufReader: bufReader, 151 }, nil 152 } 153 154 // DecodeString reads and decodes a string 155 func (r *FileReader) DecodeString() (string, error) { 156 b, err := r.decodeBytes() 157 return string(b), err 158 } 159 160 // DecodeBytes reads and decodes bytes 161 func (r *FileReader) DecodeBytes() ([]byte, error) { 162 b, err := r.decodeBytes() 163 if err != nil { 164 return nil, err 165 } 166 c := make([]byte, len(b)) 167 copy(c, b) 168 return c, nil 169 } 170 171 // DecodeUVarInt reads and decodes a number 172 func (r *FileReader) DecodeUVarInt() (uint64, error) { 173 u, err := binary.ReadUvarint(r.bufReader) 174 if err != nil { 175 return 0, errors.Wrapf(err, "error while reading from snapshot file: %s", r.file.Name()) 176 } 177 return u, nil 178 } 179 180 // DecodeProtoMessage reads and decodes a protoMessage 181 func (r *FileReader) DecodeProtoMessage(m proto.Message) error { 182 b, err := r.decodeBytes() 183 if err != nil { 184 return err 185 } 186 return proto.Unmarshal(b, m) 187 } 188 189 // Close closes the file 190 func (r *FileReader) Close() error { 191 if r == nil { 192 return nil 193 } 194 return errors.Wrapf(r.file.Close(), "error while closing the snapshot file: %s", r.file.Name()) 195 } 196 197 func (r *FileReader) decodeBytes() ([]byte, error) { 198 sizeUint, err := r.DecodeUVarInt() 199 if err != nil { 200 return nil, err 201 } 202 size := int(sizeUint) 203 if size == 0 { 204 return []byte{}, nil 205 } 206 if len(r.reusableByteSlice) < size { 207 r.reusableByteSlice = make([]byte, size) 208 } 209 if _, err := r.bufReader.Read(r.reusableByteSlice[0:size]); err != nil { 210 return nil, errors.Wrapf(err, "error while reading from snapshot file: %s", r.file.Name()) 211 } 212 return r.reusableByteSlice[0:size], nil 213 }