github.com/true-sqn/fabric@v2.1.1+incompatible/core/chaincode/persistence/persistence.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package persistence
     8  
     9  import (
    10  	"encoding/hex"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"os"
    14  	"path/filepath"
    15  	"regexp"
    16  	"strings"
    17  
    18  	"github.com/hyperledger/fabric/common/chaincode"
    19  	"github.com/hyperledger/fabric/common/flogging"
    20  	"github.com/hyperledger/fabric/common/util"
    21  
    22  	"github.com/pkg/errors"
    23  )
    24  
    25  var logger = flogging.MustGetLogger("chaincode.persistence")
    26  
    27  // IOReadWriter defines the interface needed for reading, writing, removing, and
    28  // checking for existence of a specified file
    29  type IOReadWriter interface {
    30  	ReadDir(string) ([]os.FileInfo, error)
    31  	ReadFile(string) ([]byte, error)
    32  	Remove(name string) error
    33  	WriteFile(string, string, []byte) error
    34  	MakeDir(string, os.FileMode) error
    35  	Exists(path string) (bool, error)
    36  }
    37  
    38  // FilesystemIO is the production implementation of the IOWriter interface
    39  type FilesystemIO struct {
    40  }
    41  
    42  // WriteFile writes a file to the filesystem; it does so atomically
    43  // by first writing to a temp file and then renaming the file so that
    44  // if the operation crashes midway we're not stuck with a bad package
    45  func (f *FilesystemIO) WriteFile(path, name string, data []byte) error {
    46  	if path == "" {
    47  		return errors.New("empty path not allowed")
    48  	}
    49  	tmpFile, err := ioutil.TempFile(path, ".ccpackage.")
    50  	if err != nil {
    51  		return errors.Wrapf(err, "error creating temp file in directory '%s'", path)
    52  	}
    53  	defer os.Remove(tmpFile.Name())
    54  
    55  	if n, err := tmpFile.Write(data); err != nil || n != len(data) {
    56  		if err == nil {
    57  			err = errors.Errorf(
    58  				"failed to write the entire content of the file, expected %d, wrote %d",
    59  				len(data), n)
    60  		}
    61  		return errors.Wrapf(err, "error writing to temp file '%s'", tmpFile.Name())
    62  	}
    63  
    64  	if err := tmpFile.Close(); err != nil {
    65  		return errors.Wrapf(err, "error closing temp file '%s'", tmpFile.Name())
    66  	}
    67  
    68  	if err := os.Rename(tmpFile.Name(), filepath.Join(path, name)); err != nil {
    69  		return errors.Wrapf(err, "error renaming temp file '%s'", tmpFile.Name())
    70  	}
    71  
    72  	return nil
    73  }
    74  
    75  // Remove removes a file from the filesystem - used for rolling back an in-flight
    76  // Save operation upon a failure
    77  func (f *FilesystemIO) Remove(name string) error {
    78  	return os.Remove(name)
    79  }
    80  
    81  // ReadFile reads a file from the filesystem
    82  func (f *FilesystemIO) ReadFile(filename string) ([]byte, error) {
    83  	return ioutil.ReadFile(filename)
    84  }
    85  
    86  // ReadDir reads a directory from the filesystem
    87  func (f *FilesystemIO) ReadDir(dirname string) ([]os.FileInfo, error) {
    88  	return ioutil.ReadDir(dirname)
    89  }
    90  
    91  // MakeDir makes a directory on the filesystem (and any
    92  // necessary parent directories).
    93  func (f *FilesystemIO) MakeDir(dirname string, mode os.FileMode) error {
    94  	return os.MkdirAll(dirname, mode)
    95  }
    96  
    97  // Exists checks whether a file exists
    98  func (*FilesystemIO) Exists(path string) (bool, error) {
    99  	_, err := os.Stat(path)
   100  	if err == nil {
   101  		return true, nil
   102  	}
   103  	if os.IsNotExist(err) {
   104  		return false, nil
   105  	}
   106  
   107  	return false, errors.Wrapf(err, "could not determine whether file '%s' exists", path)
   108  }
   109  
   110  // Store holds the information needed for persisting a chaincode install package
   111  type Store struct {
   112  	Path       string
   113  	ReadWriter IOReadWriter
   114  }
   115  
   116  // NewStore creates a new chaincode persistence store using
   117  // the provided path on the filesystem.
   118  func NewStore(path string) *Store {
   119  	store := &Store{
   120  		Path:       path,
   121  		ReadWriter: &FilesystemIO{},
   122  	}
   123  	store.Initialize()
   124  	return store
   125  }
   126  
   127  // Initialize checks for the existence of the _lifecycle chaincodes
   128  // directory and creates it if it has not yet been created.
   129  func (s *Store) Initialize() {
   130  	var (
   131  		exists bool
   132  		err    error
   133  	)
   134  	if exists, err = s.ReadWriter.Exists(s.Path); exists {
   135  		return
   136  	}
   137  	if err != nil {
   138  		panic(fmt.Sprintf("Initialization of chaincode store failed: %s", err))
   139  	}
   140  	if err = s.ReadWriter.MakeDir(s.Path, 0750); err != nil {
   141  		panic(fmt.Sprintf("Could not create _lifecycle chaincodes install path: %s", err))
   142  	}
   143  }
   144  
   145  // Save persists chaincode install package bytes. It returns
   146  // the hash of the chaincode install package
   147  func (s *Store) Save(label string, ccInstallPkg []byte) (string, error) {
   148  	hash := util.ComputeSHA256(ccInstallPkg)
   149  	packageID := packageID(label, hash)
   150  
   151  	ccInstallPkgFileName := CCFileName(packageID)
   152  	ccInstallPkgFilePath := filepath.Join(s.Path, ccInstallPkgFileName)
   153  
   154  	if exists, _ := s.ReadWriter.Exists(ccInstallPkgFilePath); exists {
   155  		// chaincode install package was already installed
   156  		return packageID, nil
   157  	}
   158  
   159  	if err := s.ReadWriter.WriteFile(s.Path, ccInstallPkgFileName, ccInstallPkg); err != nil {
   160  		err = errors.Wrapf(err, "error writing chaincode install package to %s", ccInstallPkgFilePath)
   161  		logger.Error(err.Error())
   162  		return "", err
   163  	}
   164  
   165  	return packageID, nil
   166  }
   167  
   168  // Load loads a persisted chaincode install package bytes with
   169  // the given packageID.
   170  func (s *Store) Load(packageID string) ([]byte, error) {
   171  	ccInstallPkgPath := filepath.Join(s.Path, CCFileName(packageID))
   172  
   173  	exists, err := s.ReadWriter.Exists(ccInstallPkgPath)
   174  	if err != nil {
   175  		return nil, errors.Wrapf(err, "could not determine whether chaincode install package '%s' exists", packageID)
   176  	}
   177  	if !exists {
   178  		return nil, &CodePackageNotFoundErr{
   179  			PackageID: packageID,
   180  		}
   181  	}
   182  
   183  	ccInstallPkg, err := s.ReadWriter.ReadFile(ccInstallPkgPath)
   184  	if err != nil {
   185  		err = errors.Wrapf(err, "error reading chaincode install package at %s", ccInstallPkgPath)
   186  		return nil, err
   187  	}
   188  
   189  	return ccInstallPkg, nil
   190  }
   191  
   192  // Delete deletes a persisted chaincode.  Note, there is no locking,
   193  // so this should only be performed if the chaincode has already
   194  // been marked built.
   195  func (s *Store) Delete(packageID string) error {
   196  	ccInstallPkgPath := filepath.Join(s.Path, CCFileName(packageID))
   197  	return s.ReadWriter.Remove(ccInstallPkgPath)
   198  }
   199  
   200  // CodePackageNotFoundErr is the error returned when a code package cannot
   201  // be found in the persistence store
   202  type CodePackageNotFoundErr struct {
   203  	PackageID string
   204  }
   205  
   206  func (e CodePackageNotFoundErr) Error() string {
   207  	return fmt.Sprintf("chaincode install package '%s' not found", e.PackageID)
   208  }
   209  
   210  // ListInstalledChaincodes returns an array with information about the
   211  // chaincodes installed in the persistence store
   212  func (s *Store) ListInstalledChaincodes() ([]chaincode.InstalledChaincode, error) {
   213  	files, err := s.ReadWriter.ReadDir(s.Path)
   214  	if err != nil {
   215  		return nil, errors.Wrapf(err, "error reading chaincode directory at %s", s.Path)
   216  	}
   217  
   218  	installedChaincodes := []chaincode.InstalledChaincode{}
   219  	for _, file := range files {
   220  		if instCC, isInstCC := installedChaincodeFromFilename(file.Name()); isInstCC {
   221  			installedChaincodes = append(installedChaincodes, instCC)
   222  		}
   223  	}
   224  	return installedChaincodes, nil
   225  }
   226  
   227  // GetChaincodeInstallPath returns the path where chaincodes
   228  // are installed
   229  func (s *Store) GetChaincodeInstallPath() string {
   230  	return s.Path
   231  }
   232  
   233  func packageID(label string, hash []byte) string {
   234  	return fmt.Sprintf("%s:%x", label, hash)
   235  }
   236  
   237  func CCFileName(packageID string) string {
   238  	return strings.Replace(packageID, ":", ".", 1) + ".tar.gz"
   239  }
   240  
   241  var packageFileMatcher = regexp.MustCompile("^(.+)[.]([0-9a-f]{64})[.]tar[.]gz$")
   242  
   243  func installedChaincodeFromFilename(fileName string) (chaincode.InstalledChaincode, bool) {
   244  	matches := packageFileMatcher.FindStringSubmatch(fileName)
   245  	if len(matches) == 3 {
   246  		label := matches[1]
   247  		hash, _ := hex.DecodeString(matches[2])
   248  		packageID := packageID(label, hash)
   249  
   250  		return chaincode.InstalledChaincode{
   251  			Label:     label,
   252  			Hash:      hash,
   253  			PackageID: packageID,
   254  		}, true
   255  	}
   256  
   257  	return chaincode.InstalledChaincode{}, false
   258  }