1 package wclayer
2
3 import (
4 "context"
5 "fmt"
6 "os"
7 "path/filepath"
8 "syscall"
9
10 "github.com/Microsoft/hcsshim/internal/hcserror"
11 "github.com/Microsoft/hcsshim/internal/longpath"
12 "github.com/Microsoft/hcsshim/internal/oc"
13 "github.com/Microsoft/hcsshim/internal/safefile"
14 "github.com/Microsoft/hcsshim/internal/winapi"
15 "github.com/pkg/errors"
16 "go.opencensus.io/trace"
17 "golang.org/x/sys/windows"
18 )
19
20 var hiveNames = []string{"DEFAULT", "SAM", "SECURITY", "SOFTWARE", "SYSTEM"}
21
22
23 func ensureHive(path string, root *os.File) (err error) {
24 _, err = safefile.LstatRelative(path, root)
25 if err != nil && !os.IsNotExist(err) {
26 return fmt.Errorf("accessing %s: %w", path, err)
27 }
28
29 version := windows.RtlGetVersion()
30 if version == nil {
31 return fmt.Errorf("failed to get OS version")
32 }
33
34 var fullPath string
35 fullPath, err = longpath.LongAbs(filepath.Join(root.Name(), path))
36 if err != nil {
37 return fmt.Errorf("getting path: %w", err)
38 }
39
40 var key syscall.Handle
41 err = winapi.ORCreateHive(&key)
42 if err != nil {
43 return fmt.Errorf("creating hive: %w", err)
44 }
45
46 defer func() {
47 closeErr := winapi.ORCloseHive(key)
48 if closeErr != nil && err == nil {
49 err = fmt.Errorf("closing hive key: %w", closeErr)
50 }
51 }()
52
53 err = winapi.ORSaveHive(key, fullPath, version.MajorVersion, version.MinorVersion)
54 if err != nil {
55 return fmt.Errorf("saving hive: %w", err)
56 }
57
58 return nil
59 }
60
61 func ensureBaseLayer(root *os.File) (hasUtilityVM bool, err error) {
62
63 const hiveSourcePath = "Files\\Windows\\System32\\config"
64 if err = safefile.MkdirAllRelative(hiveSourcePath, root); err != nil {
65 return
66 }
67
68 for _, hiveName := range hiveNames {
69 hivePath := filepath.Join(hiveSourcePath, hiveName)
70 if err = ensureHive(hivePath, root); err != nil {
71 return
72 }
73 }
74
75 stat, err := safefile.LstatRelative(utilityVMFilesPath, root)
76
77 if os.IsNotExist(err) {
78 return false, nil
79 }
80
81 if err != nil {
82 return
83 }
84
85 if !stat.Mode().IsDir() {
86 fullPath := filepath.Join(root.Name(), utilityVMFilesPath)
87 return false, errors.Errorf("%s has unexpected file mode %s", fullPath, stat.Mode().String())
88 }
89
90 const bcdRelativePath = "EFI\\Microsoft\\Boot\\BCD"
91
92
93
94
95 bcdPath := filepath.Join(utilityVMFilesPath, bcdRelativePath)
96
97 stat, err = safefile.LstatRelative(bcdPath, root)
98 if err != nil {
99 return false, errors.Wrapf(err, "UtilityVM must contain '%s'", bcdRelativePath)
100 }
101
102 if !stat.Mode().IsRegular() {
103 fullPath := filepath.Join(root.Name(), bcdPath)
104 return false, errors.Errorf("%s has unexpected file mode %s", fullPath, stat.Mode().String())
105 }
106
107 return true, nil
108 }
109
110 func convertToBaseLayer(ctx context.Context, root *os.File) error {
111 hasUtilityVM, err := ensureBaseLayer(root)
112
113 if err != nil {
114 return err
115 }
116
117 if err := ProcessBaseLayer(ctx, root.Name()); err != nil {
118 return err
119 }
120
121 if !hasUtilityVM {
122 return nil
123 }
124
125 err = safefile.EnsureNotReparsePointRelative(utilityVMPath, root)
126 if err != nil {
127 return err
128 }
129
130 utilityVMPath := filepath.Join(root.Name(), utilityVMPath)
131 return ProcessUtilityVMImage(ctx, utilityVMPath)
132 }
133
134
135
136
137 func ConvertToBaseLayer(ctx context.Context, path string) (err error) {
138 title := "hcsshim::ConvertToBaseLayer"
139 ctx, span := trace.StartSpan(ctx, title)
140 defer span.End()
141 defer func() { oc.SetSpanStatus(span, err) }()
142 span.AddAttributes(trace.StringAttribute("path", path))
143
144 root, err := safefile.OpenRoot(path)
145 if err != nil {
146 return hcserror.New(err, title+" - failed", "")
147 }
148 defer func() {
149 if err2 := root.Close(); err == nil && err2 != nil {
150 err = hcserror.New(err2, title+" - failed", "")
151 }
152 }()
153
154 if err = convertToBaseLayer(ctx, root); err != nil {
155 return hcserror.New(err, title+" - failed", "")
156 }
157 return nil
158 }
159
View as plain text