exportlayer.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package hcsshim
  2. import (
  3. "io"
  4. "io/ioutil"
  5. "os"
  6. "syscall"
  7. "github.com/Microsoft/go-winio"
  8. "github.com/Sirupsen/logrus"
  9. )
  10. // ExportLayer will create a folder at exportFolderPath and fill that folder with
  11. // the transport format version of the layer identified by layerId. This transport
  12. // format includes any metadata required for later importing the layer (using
  13. // ImportLayer), and requires the full list of parent layer paths in order to
  14. // perform the export.
  15. func ExportLayer(info DriverInfo, layerId string, exportFolderPath string, parentLayerPaths []string) error {
  16. title := "hcsshim::ExportLayer "
  17. logrus.Debugf(title+"flavour %d layerId %s folder %s", info.Flavour, layerId, exportFolderPath)
  18. // Generate layer descriptors
  19. layers, err := layerPathsToDescriptors(parentLayerPaths)
  20. if err != nil {
  21. return err
  22. }
  23. // Convert info to API calling convention
  24. infop, err := convertDriverInfo(info)
  25. if err != nil {
  26. logrus.Error(err)
  27. return err
  28. }
  29. err = exportLayer(&infop, layerId, exportFolderPath, layers)
  30. if err != nil {
  31. err = makeErrorf(err, title, "layerId=%s flavour=%d folder=%s", layerId, info.Flavour, exportFolderPath)
  32. logrus.Error(err)
  33. return err
  34. }
  35. logrus.Debugf(title+"succeeded flavour=%d layerId=%s folder=%s", info.Flavour, layerId, exportFolderPath)
  36. return nil
  37. }
  38. type LayerReader interface {
  39. Next() (string, int64, *winio.FileBasicInfo, error)
  40. Read(b []byte) (int, error)
  41. Close() error
  42. }
  43. // FilterLayerReader provides an interface for extracting the contents of an on-disk layer.
  44. type FilterLayerReader struct {
  45. context uintptr
  46. }
  47. // Next reads the next available file from a layer, ensuring that parent directories are always read
  48. // before child files and directories.
  49. //
  50. // Next returns the file's relative path, size, and basic file metadata. Read() should be used to
  51. // extract a Win32 backup stream with the remainder of the metadata and the data.
  52. func (r *FilterLayerReader) Next() (string, int64, *winio.FileBasicInfo, error) {
  53. var fileNamep *uint16
  54. fileInfo := &winio.FileBasicInfo{}
  55. var deleted uint32
  56. var fileSize int64
  57. err := exportLayerNext(r.context, &fileNamep, fileInfo, &fileSize, &deleted)
  58. if err != nil {
  59. if err == syscall.ERROR_NO_MORE_FILES {
  60. err = io.EOF
  61. } else {
  62. err = makeError(err, "ExportLayerNext", "")
  63. }
  64. return "", 0, nil, err
  65. }
  66. fileName := convertAndFreeCoTaskMemString(fileNamep)
  67. if deleted != 0 {
  68. fileInfo = nil
  69. }
  70. if fileName[0] == '\\' {
  71. fileName = fileName[1:]
  72. }
  73. return fileName, fileSize, fileInfo, nil
  74. }
  75. // Read reads from the current file's Win32 backup stream.
  76. func (r *FilterLayerReader) Read(b []byte) (int, error) {
  77. var bytesRead uint32
  78. err := exportLayerRead(r.context, b, &bytesRead)
  79. if err != nil {
  80. return 0, makeError(err, "ExportLayerRead", "")
  81. }
  82. if bytesRead == 0 {
  83. return 0, io.EOF
  84. }
  85. return int(bytesRead), nil
  86. }
  87. // Close frees resources associated with the layer reader. It will return an
  88. // error if there was an error while reading the layer or of the layer was not
  89. // completely read.
  90. func (r *FilterLayerReader) Close() (err error) {
  91. if r.context != 0 {
  92. err = exportLayerEnd(r.context)
  93. if err != nil {
  94. err = makeError(err, "ExportLayerEnd", "")
  95. }
  96. r.context = 0
  97. }
  98. return
  99. }
  100. // NewLayerReader returns a new layer reader for reading the contents of an on-disk layer.
  101. // The caller must have taken the SeBackupPrivilege privilege
  102. // to call this and any methods on the resulting LayerReader.
  103. func NewLayerReader(info DriverInfo, layerID string, parentLayerPaths []string) (LayerReader, error) {
  104. if procExportLayerBegin.Find() != nil {
  105. // The new layer reader is not available on this Windows build. Fall back to the
  106. // legacy export code path.
  107. path, err := ioutil.TempDir("", "hcs")
  108. if err != nil {
  109. return nil, err
  110. }
  111. err = ExportLayer(info, layerID, path, parentLayerPaths)
  112. if err != nil {
  113. os.RemoveAll(path)
  114. return nil, err
  115. }
  116. return &legacyLayerReaderWrapper{newLegacyLayerReader(path)}, nil
  117. }
  118. layers, err := layerPathsToDescriptors(parentLayerPaths)
  119. if err != nil {
  120. return nil, err
  121. }
  122. infop, err := convertDriverInfo(info)
  123. if err != nil {
  124. return nil, err
  125. }
  126. r := &FilterLayerReader{}
  127. err = exportLayerBegin(&infop, layerID, layers, &r.context)
  128. if err != nil {
  129. return nil, makeError(err, "ExportLayerBegin", "")
  130. }
  131. return r, err
  132. }
  133. type legacyLayerReaderWrapper struct {
  134. *legacyLayerReader
  135. }
  136. func (r *legacyLayerReaderWrapper) Close() error {
  137. err := r.legacyLayerReader.Close()
  138. os.RemoveAll(r.root)
  139. return err
  140. }