unpack.go 2.7 KB

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