importlayer.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. package hcsshim
  2. import (
  3. "errors"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "github.com/Microsoft/go-winio"
  8. "github.com/Sirupsen/logrus"
  9. )
  10. // ImportLayer will take the contents of the folder at importFolderPath and import
  11. // that into a layer with the id layerId. Note that in order to correctly populate
  12. // the layer and interperet the transport format, all parent layers must already
  13. // be present on the system at the paths provided in parentLayerPaths.
  14. func ImportLayer(info DriverInfo, layerID string, importFolderPath string, parentLayerPaths []string) error {
  15. title := "hcsshim::ImportLayer "
  16. logrus.Debugf(title+"flavour %d layerId %s folder %s", info.Flavour, layerID, importFolderPath)
  17. // Generate layer descriptors
  18. layers, err := layerPathsToDescriptors(parentLayerPaths)
  19. if err != nil {
  20. return err
  21. }
  22. // Convert info to API calling convention
  23. infop, err := convertDriverInfo(info)
  24. if err != nil {
  25. logrus.Error(err)
  26. return err
  27. }
  28. err = importLayer(&infop, layerID, importFolderPath, layers)
  29. if err != nil {
  30. err = makeErrorf(err, title, "layerId=%s flavour=%d folder=%s", layerID, info.Flavour, importFolderPath)
  31. logrus.Error(err)
  32. return err
  33. }
  34. logrus.Debugf(title+"succeeded flavour=%d layerId=%s folder=%s", info.Flavour, layerID, importFolderPath)
  35. return nil
  36. }
  37. // LayerWriter is an interface that supports writing a new container image layer.
  38. type LayerWriter interface {
  39. // Add adds a file to the layer with given metadata.
  40. Add(name string, fileInfo *winio.FileBasicInfo) error
  41. // AddLink adds a hard link to the layer. The target must already have been added.
  42. AddLink(name string, target string) error
  43. // Remove removes a file that was present in a parent layer from the layer.
  44. Remove(name string) error
  45. // Write writes data to the current file. The data must be in the format of a Win32
  46. // backup stream.
  47. Write(b []byte) (int, error)
  48. // Close finishes the layer writing process and releases any resources.
  49. Close() error
  50. }
  51. // FilterLayerWriter provides an interface to write the contents of a layer to the file system.
  52. type FilterLayerWriter struct {
  53. context uintptr
  54. }
  55. // Add adds a file or directory to the layer. The file's parent directory must have already been added.
  56. //
  57. // name contains the file's relative path. fileInfo contains file times and file attributes; the rest
  58. // of the file metadata and the file data must be written as a Win32 backup stream to the Write() method.
  59. // winio.BackupStreamWriter can be used to facilitate this.
  60. func (w *FilterLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error {
  61. if name[0] != '\\' {
  62. name = `\` + name
  63. }
  64. err := importLayerNext(w.context, name, fileInfo)
  65. if err != nil {
  66. return makeError(err, "ImportLayerNext", "")
  67. }
  68. return nil
  69. }
  70. // AddLink adds a hard link to the layer. The target of the link must have already been added.
  71. func (w *FilterLayerWriter) AddLink(name string, target string) error {
  72. return errors.New("hard links not yet supported")
  73. }
  74. // Remove removes a file from the layer. The file must have been present in the parent layer.
  75. //
  76. // name contains the file's relative path.
  77. func (w *FilterLayerWriter) Remove(name string) error {
  78. if name[0] != '\\' {
  79. name = `\` + name
  80. }
  81. err := importLayerNext(w.context, name, nil)
  82. if err != nil {
  83. return makeError(err, "ImportLayerNext", "")
  84. }
  85. return nil
  86. }
  87. // Write writes more backup stream data to the current file.
  88. func (w *FilterLayerWriter) Write(b []byte) (int, error) {
  89. err := importLayerWrite(w.context, b)
  90. if err != nil {
  91. err = makeError(err, "ImportLayerWrite", "")
  92. return 0, err
  93. }
  94. return len(b), err
  95. }
  96. // Close completes the layer write operation. The error must be checked to ensure that the
  97. // operation was successful.
  98. func (w *FilterLayerWriter) Close() (err error) {
  99. if w.context != 0 {
  100. err = importLayerEnd(w.context)
  101. if err != nil {
  102. err = makeError(err, "ImportLayerEnd", "")
  103. }
  104. w.context = 0
  105. }
  106. return
  107. }
  108. type legacyLayerWriterWrapper struct {
  109. *legacyLayerWriter
  110. info DriverInfo
  111. layerID string
  112. path string
  113. parentLayerPaths []string
  114. }
  115. func (r *legacyLayerWriterWrapper) Close() error {
  116. defer os.RemoveAll(r.root)
  117. err := r.legacyLayerWriter.Close()
  118. if err != nil {
  119. return err
  120. }
  121. // Use the original path here because ImportLayer does not support long paths for the source in TP5.
  122. // But do use a long path for the destination to work around another bug with directories
  123. // with MAX_PATH - 12 < length < MAX_PATH.
  124. info := r.info
  125. fullPath, err := makeLongAbsPath(filepath.Join(info.HomeDir, r.layerID))
  126. if err != nil {
  127. return err
  128. }
  129. info.HomeDir = ""
  130. if err = ImportLayer(info, fullPath, r.path, r.parentLayerPaths); err != nil {
  131. return err
  132. }
  133. // Add any hard links that were collected.
  134. for _, lnk := range r.PendingLinks {
  135. if err = os.Remove(lnk.Path); err != nil && !os.IsNotExist(err) {
  136. return err
  137. }
  138. if err = os.Link(lnk.Target, lnk.Path); err != nil {
  139. return err
  140. }
  141. }
  142. // Prepare the utility VM for use if one is present in the layer.
  143. if r.HasUtilityVM {
  144. err = ProcessUtilityVMImage(filepath.Join(fullPath, "UtilityVM"))
  145. if err != nil {
  146. return err
  147. }
  148. }
  149. return nil
  150. }
  151. // NewLayerWriter returns a new layer writer for creating a layer on disk.
  152. // The caller must have taken the SeBackupPrivilege and SeRestorePrivilege privileges
  153. // to call this and any methods on the resulting LayerWriter.
  154. func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) (LayerWriter, error) {
  155. if len(parentLayerPaths) == 0 {
  156. // This is a base layer. It gets imported differently.
  157. return &baseLayerWriter{
  158. root: filepath.Join(info.HomeDir, layerID),
  159. }, nil
  160. }
  161. if procImportLayerBegin.Find() != nil {
  162. // The new layer reader is not available on this Windows build. Fall back to the
  163. // legacy export code path.
  164. path, err := ioutil.TempDir("", "hcs")
  165. if err != nil {
  166. return nil, err
  167. }
  168. return &legacyLayerWriterWrapper{
  169. legacyLayerWriter: newLegacyLayerWriter(path, parentLayerPaths, filepath.Join(info.HomeDir, layerID)),
  170. info: info,
  171. layerID: layerID,
  172. path: path,
  173. parentLayerPaths: parentLayerPaths,
  174. }, nil
  175. }
  176. layers, err := layerPathsToDescriptors(parentLayerPaths)
  177. if err != nil {
  178. return nil, err
  179. }
  180. infop, err := convertDriverInfo(info)
  181. if err != nil {
  182. return nil, err
  183. }
  184. w := &FilterLayerWriter{}
  185. err = importLayerBegin(&infop, layerID, layers, &w.context)
  186. if err != nil {
  187. return nil, makeError(err, "ImportLayerStart", "")
  188. }
  189. return w, nil
  190. }