github.com/jaypipes/ghw@v0.21.1/pkg/snapshot/unpack.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 "io" 13 "os" 14 "path/filepath" 15 16 "github.com/jaypipes/ghw/pkg/option" 17 ) 18 19 const ( 20 TargetRoot = "ghw-snapshot-*" 21 ) 22 23 const ( 24 // If set, `ghw` will not unpack the snapshot in the user-supplied directory 25 // unless the aforementioned directory is empty. 26 OwnTargetDirectory = 1 << iota 27 ) 28 29 // Clanup removes the unpacket snapshot from the target root. 30 // Please not that the environs variable `GHW_SNAPSHOT_PRESERVE`, if set, 31 // will make this function silently skip. 32 func Cleanup(targetRoot string) error { 33 if option.EnvOrDefaultSnapshotPreserve() { 34 return nil 35 } 36 return os.RemoveAll(targetRoot) 37 } 38 39 // Unpack expands the given snapshot in a temporary directory managed by `ghw`. Returns the path of that directory. 40 func Unpack(snapshotName string) (string, error) { 41 targetRoot, err := os.MkdirTemp("", TargetRoot) 42 if err != nil { 43 return "", err 44 } 45 _, err = UnpackInto(snapshotName, targetRoot, 0) 46 return targetRoot, err 47 } 48 49 // UnpackInto expands the given snapshot in a client-supplied directory. 50 // Returns true if the snapshot was actually unpacked, false otherwise 51 func UnpackInto(snapshotName, targetRoot string, flags uint) (bool, error) { 52 if (flags&OwnTargetDirectory) == OwnTargetDirectory && !isEmptyDir(targetRoot) { 53 return false, nil 54 } 55 snap, err := os.Open(snapshotName) 56 if err != nil { 57 return false, err 58 } 59 defer snap.Close() 60 return true, Untar(targetRoot, snap) 61 } 62 63 // Untar extracts data from the given reader (providing data in tar.gz format) and unpacks it in the given directory. 64 func Untar(root string, r io.Reader) error { 65 var err error 66 gzr, err := gzip.NewReader(r) 67 if err != nil { 68 return err 69 } 70 defer gzr.Close() 71 72 tr := tar.NewReader(gzr) 73 for { 74 header, err := tr.Next() 75 if err == io.EOF { 76 // we are done 77 return nil 78 } 79 80 if err != nil { 81 // bail out 82 return err 83 } 84 85 if header == nil { 86 // TODO: how come? 87 continue 88 } 89 90 target := filepath.Join(root, header.Name) 91 mode := os.FileMode(header.Mode) 92 93 switch header.Typeflag { 94 case tar.TypeDir: 95 err = os.MkdirAll(target, mode) 96 if err != nil { 97 return err 98 } 99 100 case tar.TypeReg: 101 dst, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, mode) 102 if err != nil { 103 return err 104 } 105 106 _, err = io.Copy(dst, tr) 107 if err != nil { 108 return err 109 } 110 111 dst.Close() 112 113 case tar.TypeSymlink: 114 err = os.Symlink(header.Linkname, target) 115 if err != nil { 116 return err 117 } 118 } 119 } 120 } 121 122 func isEmptyDir(name string) bool { 123 entries, err := os.ReadDir(name) 124 if err != nil { 125 return false 126 } 127 return len(entries) == 0 128 }