github.com/jaypipes/ghw@v0.21.1/pkg/snapshot/pack.go (about) 1 // 2 // Use and distribution licensed under the Apache license version 2. 3 // 4 // See the COPYING file in the root project directory for full text. 5 // 6 7 package snapshot 8 9 import ( 10 "archive/tar" 11 "compress/gzip" 12 "errors" 13 "fmt" 14 "io" 15 "os" 16 "path/filepath" 17 ) 18 19 // PackFrom creates the snapshot named `snapshotName` from the 20 // directory tree whose root is `sourceRoot`. 21 func PackFrom(snapshotName, sourceRoot string) error { 22 f, err := OpenDestination(snapshotName) 23 if err != nil { 24 return err 25 } 26 defer f.Close() 27 28 return PackWithWriter(f, sourceRoot) 29 } 30 31 // OpenDestination opens the `snapshotName` file for writing, bailing out 32 // if the file seems to exist and have existing content already. 33 // This is done to avoid accidental overwrites. 34 func OpenDestination(snapshotName string) (*os.File, error) { 35 f, err := os.OpenFile(snapshotName, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600) 36 if err != nil { 37 if !errors.Is(err, os.ErrExist) { 38 return nil, err 39 } 40 fs, err := os.Stat(snapshotName) 41 if err != nil { 42 return nil, err 43 } 44 if fs.Size() > 0 { 45 return nil, fmt.Errorf("file %s already exists and is of size > 0", snapshotName) 46 } 47 f, err = os.OpenFile(snapshotName, os.O_WRONLY, 0600) 48 if err != nil { 49 return nil, err 50 } 51 } 52 return f, nil 53 } 54 55 // PakcWithWriter creates a snapshot sending all the binary data to the 56 // given `fw` writer. The snapshot is made from the directory tree whose 57 // root is `sourceRoot`. 58 func PackWithWriter(fw io.Writer, sourceRoot string) error { 59 gzw := gzip.NewWriter(fw) 60 defer gzw.Close() 61 62 tw := tar.NewWriter(gzw) 63 defer tw.Close() 64 65 return createSnapshot(tw, sourceRoot) 66 } 67 68 func createSnapshot(tw *tar.Writer, buildDir string) error { 69 return filepath.Walk(buildDir, func(path string, fi os.FileInfo, _ error) error { 70 if path == buildDir { 71 return nil 72 } 73 var link string 74 var err error 75 76 if fi.Mode()&os.ModeSymlink != 0 { 77 trace("processing symlink %s\n", path) 78 link, err = os.Readlink(path) 79 if err != nil { 80 return err 81 } 82 } 83 84 hdr, err := tar.FileInfoHeader(fi, link) 85 if err != nil { 86 return err 87 } 88 relPath, err := filepath.Rel(buildDir, path) 89 if err != nil { 90 return err 91 } 92 hdr.Name = relPath 93 94 if err = tw.WriteHeader(hdr); err != nil { 95 return err 96 } 97 98 switch hdr.Typeflag { 99 case tar.TypeReg, tar.TypeRegA: 100 f, err := os.Open(path) 101 if err != nil { 102 return err 103 } 104 defer f.Close() 105 if _, err = io.Copy(tw, f); err != nil { 106 return err 107 } 108 } 109 return nil 110 }) 111 }