export.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. //go:build windows
  2. package ociwclayer
  3. import (
  4. "archive/tar"
  5. "context"
  6. "io"
  7. "path/filepath"
  8. "github.com/Microsoft/go-winio/backuptar"
  9. "github.com/Microsoft/hcsshim/internal/wclayer"
  10. )
  11. // ExportLayerToTar writes an OCI layer tar stream from the provided on-disk layer.
  12. // The caller must specify the parent layers, if any, ordered from lowest to
  13. // highest layer.
  14. //
  15. // The layer will be mounted for this process, so the caller should ensure that
  16. // it is not currently mounted.
  17. func ExportLayerToTar(ctx context.Context, w io.Writer, path string, parentLayerPaths []string) error {
  18. err := wclayer.ActivateLayer(ctx, path)
  19. if err != nil {
  20. return err
  21. }
  22. defer func() {
  23. _ = wclayer.DeactivateLayer(ctx, path)
  24. }()
  25. // Prepare and unprepare the layer to ensure that it has been initialized.
  26. err = wclayer.PrepareLayer(ctx, path, parentLayerPaths)
  27. if err != nil {
  28. return err
  29. }
  30. err = wclayer.UnprepareLayer(ctx, path)
  31. if err != nil {
  32. return err
  33. }
  34. r, err := wclayer.NewLayerReader(ctx, path, parentLayerPaths)
  35. if err != nil {
  36. return err
  37. }
  38. err = writeTarFromLayer(ctx, r, w)
  39. cerr := r.Close()
  40. if err != nil {
  41. return err
  42. }
  43. return cerr
  44. }
  45. func writeTarFromLayer(ctx context.Context, r wclayer.LayerReader, w io.Writer) error {
  46. linkRecords := make(map[[16]byte]string)
  47. t := tar.NewWriter(w)
  48. for {
  49. select {
  50. case <-ctx.Done():
  51. return ctx.Err()
  52. default:
  53. }
  54. name, size, fileInfo, err := r.Next()
  55. if err == io.EOF {
  56. break
  57. }
  58. if err != nil {
  59. return err
  60. }
  61. if fileInfo == nil {
  62. // Write a whiteout file.
  63. hdr := &tar.Header{
  64. Name: filepath.ToSlash(filepath.Join(filepath.Dir(name), whiteoutPrefix+filepath.Base(name))),
  65. }
  66. err := t.WriteHeader(hdr)
  67. if err != nil {
  68. return err
  69. }
  70. } else {
  71. numberOfLinks, fileIDInfo, err := r.LinkInfo()
  72. if err != nil {
  73. return err
  74. }
  75. if numberOfLinks > 1 {
  76. if linkName, ok := linkRecords[fileIDInfo.FileID]; ok {
  77. // We've seen this file before, by another name, so put a hardlink in the tar stream.
  78. hdr := backuptar.BasicInfoHeader(name, 0, fileInfo)
  79. hdr.Mode = 0644
  80. hdr.Typeflag = tar.TypeLink
  81. hdr.Linkname = linkName
  82. if err := t.WriteHeader(hdr); err != nil {
  83. return err
  84. }
  85. continue
  86. }
  87. // All subsequent names for this file will be hard-linked to this name
  88. linkRecords[fileIDInfo.FileID] = filepath.ToSlash(name)
  89. }
  90. err = backuptar.WriteTarFileFromBackupStream(t, r, name, size, fileInfo)
  91. if err != nil {
  92. return err
  93. }
  94. }
  95. }
  96. return t.Close()
  97. }