github.com/hyperledger-labs/bdls@v2.1.1+incompatible/core/chaincode/platforms/util/writer.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package util
     8  
     9  import (
    10  	"archive/tar"
    11  	"bufio"
    12  	"fmt"
    13  	"io"
    14  	"io/ioutil"
    15  	"os"
    16  	"path"
    17  	"path/filepath"
    18  	"strings"
    19  	"time"
    20  
    21  	"github.com/hyperledger/fabric/internal/ccmetadata"
    22  	"github.com/pkg/errors"
    23  )
    24  
    25  // WriteFolderToTarPackage writes source files to a tarball.
    26  // This utility is used for node js chaincode packaging, but not golang chaincode.
    27  // Golang chaincode has more sophisticated file packaging, as implemented in golang/platform.go.
    28  func WriteFolderToTarPackage(tw *tar.Writer, srcPath string, excludeDirs []string, includeFileTypeMap map[string]bool, excludeFileTypeMap map[string]bool) error {
    29  	rootDirectory := filepath.Clean(srcPath)
    30  
    31  	logger.Debugw("writing folder to package", "rootDirectory", rootDirectory)
    32  
    33  	var success bool
    34  	walkFn := func(localpath string, info os.FileInfo, err error) error {
    35  		if err != nil {
    36  			logger.Errorf("Visit %s failed: %s", localpath, err)
    37  			return err
    38  		}
    39  
    40  		if info.Mode().IsDir() {
    41  			for _, excluded := range append(excludeDirs, ".git") {
    42  				if info.Name() == excluded {
    43  					return filepath.SkipDir
    44  				}
    45  			}
    46  			return nil
    47  		}
    48  
    49  		ext := filepath.Ext(localpath)
    50  		if _, ok := includeFileTypeMap[ext]; includeFileTypeMap != nil && !ok {
    51  			return nil
    52  		}
    53  		if excludeFileTypeMap[ext] {
    54  			return nil
    55  		}
    56  
    57  		relpath, err := filepath.Rel(rootDirectory, localpath)
    58  		if err != nil {
    59  			return err
    60  		}
    61  		packagepath := filepath.ToSlash(relpath)
    62  
    63  		// if file is metadata, keep the /META-INF directory, e.g: META-INF/statedb/couchdb/indexes/indexOwner.json
    64  		// otherwise file is source code, put it in /src dir, e.g: src/marbles_chaincode.js
    65  		if strings.HasPrefix(localpath, filepath.Join(rootDirectory, "META-INF")) {
    66  			// Hidden files are not supported as metadata, therefore ignore them.
    67  			// User often doesn't know that hidden files are there, and may not be able to delete them, therefore warn user rather than error out.
    68  			if strings.HasPrefix(info.Name(), ".") {
    69  				logger.Warningf("Ignoring hidden file in metadata directory: %s", packagepath)
    70  				return nil
    71  			}
    72  
    73  			fileBytes, err := ioutil.ReadFile(localpath)
    74  			if err != nil {
    75  				return err
    76  			}
    77  
    78  			// Validate metadata file for inclusion in tar
    79  			// Validation is based on the fully qualified path of the file
    80  			err = ccmetadata.ValidateMetadataFile(packagepath, fileBytes)
    81  			if err != nil {
    82  				return err
    83  			}
    84  		} else { // file is not metadata, include in src
    85  			packagepath = path.Join("src", packagepath)
    86  		}
    87  
    88  		err = WriteFileToPackage(localpath, packagepath, tw)
    89  		if err != nil {
    90  			return fmt.Errorf("Error writing file to package: %s", err)
    91  		}
    92  
    93  		success = true
    94  		return nil
    95  	}
    96  
    97  	if err := filepath.Walk(rootDirectory, walkFn); err != nil {
    98  		logger.Infof("Error walking rootDirectory: %s", err)
    99  		return err
   100  	}
   101  
   102  	if !success {
   103  		return errors.Errorf("no source files found in '%s'", srcPath)
   104  	}
   105  	return nil
   106  }
   107  
   108  // WriteFileToPackage writes a file to a tar stream.
   109  func WriteFileToPackage(localpath string, packagepath string, tw *tar.Writer) error {
   110  	logger.Debug("Writing file to tarball:", packagepath)
   111  	fd, err := os.Open(localpath)
   112  	if err != nil {
   113  		return fmt.Errorf("%s: %s", localpath, err)
   114  	}
   115  	defer fd.Close()
   116  
   117  	fi, err := fd.Stat()
   118  	if err != nil {
   119  		return fmt.Errorf("%s: %s", localpath, err)
   120  	}
   121  
   122  	header, err := tar.FileInfoHeader(fi, localpath)
   123  	if err != nil {
   124  		return fmt.Errorf("failed calculating FileInfoHeader: %s", err)
   125  	}
   126  
   127  	// Take the variance out of the tar by using zero time and fixed uid/gid.
   128  	var zeroTime time.Time
   129  	header.AccessTime = zeroTime
   130  	header.ModTime = zeroTime
   131  	header.ChangeTime = zeroTime
   132  	header.Name = packagepath
   133  	header.Mode = 0100644
   134  	header.Uid = 500
   135  	header.Gid = 500
   136  	header.Uname = ""
   137  	header.Gname = ""
   138  
   139  	err = tw.WriteHeader(header)
   140  	if err != nil {
   141  		return fmt.Errorf("failed to write header for %s: %s", localpath, err)
   142  	}
   143  
   144  	is := bufio.NewReader(fd)
   145  	_, err = io.Copy(tw, is)
   146  	if err != nil {
   147  		return fmt.Errorf("failed to write %s as %s: %s", localpath, packagepath, err)
   148  	}
   149  
   150  	return nil
   151  }