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 }