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  }