converttobaselayer.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package wclayer
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "syscall"
  8. "github.com/Microsoft/hcsshim/internal/hcserror"
  9. "github.com/Microsoft/hcsshim/internal/longpath"
  10. "github.com/Microsoft/hcsshim/internal/oc"
  11. "github.com/Microsoft/hcsshim/internal/safefile"
  12. "github.com/Microsoft/hcsshim/internal/winapi"
  13. "github.com/pkg/errors"
  14. "go.opencensus.io/trace"
  15. "golang.org/x/sys/windows"
  16. )
  17. var hiveNames = []string{"DEFAULT", "SAM", "SECURITY", "SOFTWARE", "SYSTEM"}
  18. // Ensure the given file exists as an ordinary file, and create a minimal hive file if not.
  19. func ensureHive(path string, root *os.File) (err error) {
  20. _, err = safefile.LstatRelative(path, root)
  21. if err != nil && !os.IsNotExist(err) {
  22. return fmt.Errorf("accessing %s: %w", path, err)
  23. }
  24. version := windows.RtlGetVersion()
  25. if version == nil {
  26. return fmt.Errorf("failed to get OS version")
  27. }
  28. var fullPath string
  29. fullPath, err = longpath.LongAbs(filepath.Join(root.Name(), path))
  30. if err != nil {
  31. return fmt.Errorf("getting path: %w", err)
  32. }
  33. var key syscall.Handle
  34. err = winapi.ORCreateHive(&key)
  35. if err != nil {
  36. return fmt.Errorf("creating hive: %w", err)
  37. }
  38. defer func() {
  39. closeErr := winapi.ORCloseHive(key)
  40. if closeErr != nil && err == nil {
  41. err = fmt.Errorf("closing hive key: %w", closeErr)
  42. }
  43. }()
  44. err = winapi.ORSaveHive(key, fullPath, version.MajorVersion, version.MinorVersion)
  45. if err != nil {
  46. return fmt.Errorf("saving hive: %w", err)
  47. }
  48. return nil
  49. }
  50. func ensureBaseLayer(root *os.File) (hasUtilityVM bool, err error) {
  51. // The base layer registry hives will be copied from here
  52. const hiveSourcePath = "Files\\Windows\\System32\\config"
  53. if err = safefile.MkdirAllRelative(hiveSourcePath, root); err != nil {
  54. return
  55. }
  56. for _, hiveName := range hiveNames {
  57. hivePath := filepath.Join(hiveSourcePath, hiveName)
  58. if err = ensureHive(hivePath, root); err != nil {
  59. return
  60. }
  61. }
  62. stat, err := safefile.LstatRelative(utilityVMFilesPath, root)
  63. if os.IsNotExist(err) {
  64. return false, nil
  65. }
  66. if err != nil {
  67. return
  68. }
  69. if !stat.Mode().IsDir() {
  70. fullPath := filepath.Join(root.Name(), utilityVMFilesPath)
  71. return false, errors.Errorf("%s has unexpected file mode %s", fullPath, stat.Mode().String())
  72. }
  73. const bcdRelativePath = "EFI\\Microsoft\\Boot\\BCD"
  74. // Just check that this exists as a regular file. If it exists but is not a valid registry hive,
  75. // ProcessUtilityVMImage will complain:
  76. // "The registry could not read in, or write out, or flush, one of the files that contain the system's image of the registry."
  77. bcdPath := filepath.Join(utilityVMFilesPath, bcdRelativePath)
  78. stat, err = safefile.LstatRelative(bcdPath, root)
  79. if err != nil {
  80. return false, errors.Wrapf(err, "UtilityVM must contain '%s'", bcdRelativePath)
  81. }
  82. if !stat.Mode().IsRegular() {
  83. fullPath := filepath.Join(root.Name(), bcdPath)
  84. return false, errors.Errorf("%s has unexpected file mode %s", fullPath, stat.Mode().String())
  85. }
  86. return true, nil
  87. }
  88. func convertToBaseLayer(ctx context.Context, root *os.File) error {
  89. hasUtilityVM, err := ensureBaseLayer(root)
  90. if err != nil {
  91. return err
  92. }
  93. if err := ProcessBaseLayer(ctx, root.Name()); err != nil {
  94. return err
  95. }
  96. if !hasUtilityVM {
  97. return nil
  98. }
  99. err = safefile.EnsureNotReparsePointRelative(utilityVMPath, root)
  100. if err != nil {
  101. return err
  102. }
  103. utilityVMPath := filepath.Join(root.Name(), utilityVMPath)
  104. return ProcessUtilityVMImage(ctx, utilityVMPath)
  105. }
  106. // ConvertToBaseLayer processes a candidate base layer, i.e. a directory
  107. // containing the desired file content under Files/, and optionally the
  108. // desired file content for a UtilityVM under UtilityVM/Files/
  109. func ConvertToBaseLayer(ctx context.Context, path string) (err error) {
  110. title := "hcsshim::ConvertToBaseLayer"
  111. ctx, span := trace.StartSpan(ctx, title)
  112. defer span.End()
  113. defer func() { oc.SetSpanStatus(span, err) }()
  114. span.AddAttributes(trace.StringAttribute("path", path))
  115. root, err := safefile.OpenRoot(path)
  116. if err != nil {
  117. return hcserror.New(err, title+" - failed", "")
  118. }
  119. defer func() {
  120. if err2 := root.Close(); err == nil && err2 != nil {
  121. err = hcserror.New(err2, title+" - failed", "")
  122. }
  123. }()
  124. if err = convertToBaseLayer(ctx, root); err != nil {
  125. return hcserror.New(err, title+" - failed", "")
  126. }
  127. return nil
  128. }