pack.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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. "errors"
  11. "fmt"
  12. "io"
  13. "os"
  14. "path/filepath"
  15. "strings"
  16. )
  17. // PackFrom creates the snapshot named `snapshotName` from the
  18. // directory tree whose root is `sourceRoot`.
  19. func PackFrom(snapshotName, sourceRoot string) error {
  20. f, err := OpenDestination(snapshotName)
  21. if err != nil {
  22. return err
  23. }
  24. defer f.Close()
  25. return PackWithWriter(f, sourceRoot)
  26. }
  27. // OpenDestination opens the `snapshotName` file for writing, bailing out
  28. // if the file seems to exist and have existing content already.
  29. // This is done to avoid accidental overwrites.
  30. func OpenDestination(snapshotName string) (*os.File, error) {
  31. var f *os.File
  32. var err error
  33. if _, err = os.Stat(snapshotName); errors.Is(err, os.ErrNotExist) {
  34. if f, err = os.Create(snapshotName); err != nil {
  35. return nil, err
  36. }
  37. } else if err != nil {
  38. return nil, err
  39. } else {
  40. f, err := os.OpenFile(snapshotName, os.O_WRONLY, 0600)
  41. if err != nil {
  42. return nil, err
  43. }
  44. fs, err := f.Stat()
  45. if err != nil {
  46. return nil, err
  47. }
  48. if fs.Size() > 0 {
  49. return nil, fmt.Errorf("File %s already exists and is of size >0", snapshotName)
  50. }
  51. }
  52. return f, nil
  53. }
  54. // PakcWithWriter creates a snapshot sending all the binary data to the
  55. // given `fw` writer. The snapshot is made from the directory tree whose
  56. // root is `sourceRoot`.
  57. func PackWithWriter(fw io.Writer, sourceRoot string) error {
  58. gzw := gzip.NewWriter(fw)
  59. defer gzw.Close()
  60. tw := tar.NewWriter(gzw)
  61. defer tw.Close()
  62. return createSnapshot(tw, sourceRoot)
  63. }
  64. func createSnapshot(tw *tar.Writer, buildDir string) error {
  65. return filepath.Walk(buildDir, func(path string, fi os.FileInfo, _ error) error {
  66. if path == buildDir {
  67. return nil
  68. }
  69. var link string
  70. var err error
  71. if fi.Mode()&os.ModeSymlink != 0 {
  72. trace("processing symlink %s\n", path)
  73. link, err = os.Readlink(path)
  74. if err != nil {
  75. return err
  76. }
  77. }
  78. hdr, err := tar.FileInfoHeader(fi, link)
  79. if err != nil {
  80. return err
  81. }
  82. hdr.Name = strings.TrimPrefix(strings.TrimPrefix(path, buildDir), string(os.PathSeparator))
  83. if err = tw.WriteHeader(hdr); err != nil {
  84. return err
  85. }
  86. switch hdr.Typeflag {
  87. case tar.TypeReg, tar.TypeRegA:
  88. f, err := os.Open(path)
  89. if err != nil {
  90. return err
  91. }
  92. if _, err = io.Copy(tw, f); err != nil {
  93. return err
  94. }
  95. f.Close()
  96. }
  97. return nil
  98. })
  99. }