import.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. //go:build windows
  2. package ociwclayer
  3. import (
  4. "archive/tar"
  5. "bufio"
  6. "context"
  7. "io"
  8. "os"
  9. "path"
  10. "path/filepath"
  11. "strings"
  12. winio "github.com/Microsoft/go-winio"
  13. "github.com/Microsoft/go-winio/backuptar"
  14. "github.com/Microsoft/hcsshim/internal/wclayer"
  15. )
  16. const whiteoutPrefix = ".wh."
  17. var (
  18. // mutatedFiles is a list of files that are mutated by the import process
  19. // and must be backed up and restored.
  20. mutatedFiles = map[string]string{
  21. "UtilityVM/Files/EFI/Microsoft/Boot/BCD": "bcd.bak",
  22. "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG": "bcd.log.bak",
  23. "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG1": "bcd.log1.bak",
  24. "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG2": "bcd.log2.bak",
  25. }
  26. )
  27. // ImportLayerFromTar reads a layer from an OCI layer tar stream and extracts it to the
  28. // specified path. The caller must specify the parent layers, if any, ordered
  29. // from lowest to highest layer.
  30. //
  31. // The caller must ensure that the thread or process has acquired backup and
  32. // restore privileges.
  33. //
  34. // This function returns the total size of the layer's files, in bytes.
  35. func ImportLayerFromTar(ctx context.Context, r io.Reader, path string, parentLayerPaths []string) (int64, error) {
  36. err := os.MkdirAll(path, 0)
  37. if err != nil {
  38. return 0, err
  39. }
  40. w, err := wclayer.NewLayerWriter(ctx, path, parentLayerPaths)
  41. if err != nil {
  42. return 0, err
  43. }
  44. n, err := writeLayerFromTar(ctx, r, w, path)
  45. cerr := w.Close()
  46. if err != nil {
  47. return 0, err
  48. }
  49. if cerr != nil {
  50. return 0, cerr
  51. }
  52. return n, nil
  53. }
  54. func writeLayerFromTar(ctx context.Context, r io.Reader, w wclayer.LayerWriter, root string) (int64, error) {
  55. t := tar.NewReader(r)
  56. hdr, err := t.Next()
  57. totalSize := int64(0)
  58. buf := bufio.NewWriter(nil)
  59. for err == nil {
  60. select {
  61. case <-ctx.Done():
  62. return 0, ctx.Err()
  63. default:
  64. }
  65. base := path.Base(hdr.Name)
  66. if strings.HasPrefix(base, whiteoutPrefix) {
  67. name := path.Join(path.Dir(hdr.Name), base[len(whiteoutPrefix):])
  68. err = w.Remove(filepath.FromSlash(name))
  69. if err != nil {
  70. return 0, err
  71. }
  72. hdr, err = t.Next()
  73. } else if hdr.Typeflag == tar.TypeLink {
  74. err = w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname))
  75. if err != nil {
  76. return 0, err
  77. }
  78. hdr, err = t.Next()
  79. } else {
  80. var (
  81. name string
  82. size int64
  83. fileInfo *winio.FileBasicInfo
  84. )
  85. name, size, fileInfo, err = backuptar.FileInfoFromHeader(hdr)
  86. if err != nil {
  87. return 0, err
  88. }
  89. err = w.Add(filepath.FromSlash(name), fileInfo)
  90. if err != nil {
  91. return 0, err
  92. }
  93. hdr, err = writeBackupStreamFromTarAndSaveMutatedFiles(buf, w, t, hdr, root)
  94. totalSize += size
  95. }
  96. }
  97. if err != io.EOF {
  98. return 0, err
  99. }
  100. return totalSize, nil
  101. }
  102. // writeBackupStreamFromTarAndSaveMutatedFiles reads data from a tar stream and
  103. // writes it to a backup stream, and also saves any files that will be mutated
  104. // by the import layer process to a backup location.
  105. func writeBackupStreamFromTarAndSaveMutatedFiles(buf *bufio.Writer, w io.Writer, t *tar.Reader, hdr *tar.Header, root string) (nextHdr *tar.Header, err error) {
  106. var bcdBackup *os.File
  107. var bcdBackupWriter *winio.BackupFileWriter
  108. if backupPath, ok := mutatedFiles[hdr.Name]; ok {
  109. bcdBackup, err = os.Create(filepath.Join(root, backupPath))
  110. if err != nil {
  111. return nil, err
  112. }
  113. defer func() {
  114. cerr := bcdBackup.Close()
  115. if err == nil {
  116. err = cerr
  117. }
  118. }()
  119. bcdBackupWriter = winio.NewBackupFileWriter(bcdBackup, false)
  120. defer func() {
  121. cerr := bcdBackupWriter.Close()
  122. if err == nil {
  123. err = cerr
  124. }
  125. }()
  126. buf.Reset(io.MultiWriter(w, bcdBackupWriter))
  127. } else {
  128. buf.Reset(w)
  129. }
  130. defer func() {
  131. ferr := buf.Flush()
  132. if err == nil {
  133. err = ferr
  134. }
  135. }()
  136. return backuptar.WriteBackupStreamFromTarFile(buf, t, hdr)
  137. }