podman
223 строки · 8.3 Кб
1package mkcw
2
3import (
4"bytes"
5"encoding/binary"
6"encoding/json"
7"errors"
8"fmt"
9"io"
10"os"
11
12"github.com/containers/buildah/define"
13"github.com/containers/buildah/internal/mkcw/types"
14)
15
16type (
17// WorkloadConfig is the data type which is encoded and stored in an image.
18WorkloadConfig = types.WorkloadConfig
19// SevWorkloadData is the type of data in WorkloadConfig.TeeData when the type is SEV.
20SevWorkloadData = types.SevWorkloadData
21// SnpWorkloadData is the type of data in WorkloadConfig.TeeData when the type is SNP.
22SnpWorkloadData = types.SnpWorkloadData
23// TeeType is one of the known types of trusted execution environments for which we
24// can generate suitable image contents.
25TeeType = define.TeeType
26)
27
28const (
29maxWorkloadConfigSize = 1024 * 1024
30preferredPaddingBoundary = 4096
31// SEV is a known trusted execution environment type: AMD-SEV
32SEV = define.SEV
33// SEV_NO_ES is a known trusted execution environment type: AMD-SEV without encrypted state
34SEV_NO_ES = types.SEV_NO_ES //revive:disable-line:var-naming
35// SNP is a known trusted execution environment type: AMD-SNP
36SNP = define.SNP
37// krun looks for its configuration JSON directly in a disk image if the last twelve bytes
38// of the disk image are this magic value followed by a little-endian 64-bit
39// length-of-the-configuration
40krunMagic = "KRUN"
41)
42
43// ReadWorkloadConfigFromImage reads the workload configuration from the
44// specified disk image file
45func ReadWorkloadConfigFromImage(path string) (WorkloadConfig, error) {
46// Read the last 12 bytes, which should be "KRUN" followed by a 64-bit
47// little-endian length. The (length) bytes immediately preceding
48// these hold the JSON-encoded workloadConfig.
49var wc WorkloadConfig
50f, err := os.Open(path)
51if err != nil {
52return wc, err
53}
54defer f.Close()
55
56// Read those last 12 bytes.
57finalTwelve := make([]byte, 12)
58if _, err = f.Seek(-12, io.SeekEnd); err != nil {
59return wc, fmt.Errorf("checking for workload config signature: %w", err)
60}
61if n, err := f.Read(finalTwelve); err != nil || n != len(finalTwelve) {
62if err != nil && !errors.Is(err, io.EOF) {
63return wc, fmt.Errorf("reading workload config signature (%d bytes read): %w", n, err)
64}
65if n != len(finalTwelve) {
66return wc, fmt.Errorf("short read (expected 12 bytes at the end of %q, got %d)", path, n)
67}
68}
69if magic := string(finalTwelve[0:4]); magic != "KRUN" {
70return wc, fmt.Errorf("expected magic string KRUN in %q, found %q)", path, magic)
71}
72length := binary.LittleEndian.Uint64(finalTwelve[4:])
73if length > maxWorkloadConfigSize {
74return wc, fmt.Errorf("workload config in %q is %d bytes long, which seems unreasonable (max allowed %d)", path, length, maxWorkloadConfigSize)
75}
76
77// Read and decode the config.
78configBytes := make([]byte, length)
79if _, err = f.Seek(-(int64(length) + 12), io.SeekEnd); err != nil {
80return wc, fmt.Errorf("looking for workload config from disk image: %w", err)
81}
82if n, err := f.Read(configBytes); err != nil || n != len(configBytes) {
83if err != nil {
84return wc, fmt.Errorf("reading workload config from disk image: %w", err)
85}
86return wc, fmt.Errorf("short read (expected %d bytes near the end of %q, got %d)", len(configBytes), path, n)
87}
88err = json.Unmarshal(configBytes, &wc)
89if err != nil {
90err = fmt.Errorf("unmarshaling configuration %q: %w", string(configBytes), err)
91}
92return wc, err
93}
94
95// WriteWorkloadConfigToImage writes the workload configuration to the
96// specified disk image file, overwriting a previous configuration if it's
97// asked to and it finds one
98func WriteWorkloadConfigToImage(imageFile *os.File, workloadConfigBytes []byte, overwrite bool) error {
99// Read those last 12 bytes to check if there's a configuration there already, which we should overwrite.
100var overwriteOffset int64
101if overwrite {
102finalTwelve := make([]byte, 12)
103if _, err := imageFile.Seek(-12, io.SeekEnd); err != nil {
104return fmt.Errorf("checking for workload config signature: %w", err)
105}
106if n, err := imageFile.Read(finalTwelve); err != nil || n != len(finalTwelve) {
107if err != nil && !errors.Is(err, io.EOF) {
108return fmt.Errorf("reading workload config signature (%d bytes read): %w", n, err)
109}
110if n != len(finalTwelve) {
111return fmt.Errorf("short read (expected 12 bytes at the end of %q, got %d)", imageFile.Name(), n)
112}
113}
114if magic := string(finalTwelve[0:4]); magic == "KRUN" {
115length := binary.LittleEndian.Uint64(finalTwelve[4:])
116if length < maxWorkloadConfigSize {
117overwriteOffset = int64(length + 12)
118}
119}
120}
121// If we found a configuration in the file, try to figure out how much padding was used.
122paddingSize := int64(preferredPaddingBoundary)
123if overwriteOffset != 0 {
124st, err := imageFile.Stat()
125if err != nil {
126return err
127}
128for _, possiblePaddingLength := range []int64{0x100000, 0x10000, 0x1000, 0x200, 0x100} {
129if overwriteOffset > possiblePaddingLength {
130continue
131}
132if st.Size()%possiblePaddingLength != 0 {
133continue
134}
135if _, err := imageFile.Seek(-possiblePaddingLength, io.SeekEnd); err != nil {
136return fmt.Errorf("checking size of padding at end of file: %w", err)
137}
138buf := make([]byte, possiblePaddingLength)
139n, err := imageFile.Read(buf)
140if err != nil {
141return fmt.Errorf("reading possible padding at end of file: %w", err)
142}
143if n != len(buf) {
144return fmt.Errorf("short read checking size of padding at end of file: %d != %d", n, len(buf))
145}
146if bytes.Equal(buf[:possiblePaddingLength-overwriteOffset], make([]byte, possiblePaddingLength-overwriteOffset)) {
147// everything up to the configuration was zero bytes, so it was padding
148overwriteOffset = possiblePaddingLength
149paddingSize = possiblePaddingLength
150break
151}
152}
153}
154
155// Append the krun configuration to a new buffer.
156var formatted bytes.Buffer
157nWritten, err := formatted.Write(workloadConfigBytes)
158if err != nil {
159return fmt.Errorf("building workload config: %w", err)
160}
161if nWritten != len(workloadConfigBytes) {
162return fmt.Errorf("short write appending configuration to buffer: %d != %d", nWritten, len(workloadConfigBytes))
163}
164// Append the magic string to the buffer.
165nWritten, err = formatted.WriteString(krunMagic)
166if err != nil {
167return fmt.Errorf("building workload config signature: %w", err)
168}
169if nWritten != len(krunMagic) {
170return fmt.Errorf("short write appending krun magic to buffer: %d != %d", nWritten, len(krunMagic))
171}
172// Append the 64-bit little-endian length of the workload configuration to the buffer.
173workloadConfigLengthBytes := make([]byte, 8)
174binary.LittleEndian.PutUint64(workloadConfigLengthBytes, uint64(len(workloadConfigBytes)))
175nWritten, err = formatted.Write(workloadConfigLengthBytes)
176if err != nil {
177return fmt.Errorf("building workload config signature size: %w", err)
178}
179if nWritten != len(workloadConfigLengthBytes) {
180return fmt.Errorf("short write appending configuration length to buffer: %d != %d", nWritten, len(workloadConfigLengthBytes))
181}
182
183// Build a copy of that data, with padding preceding it.
184var padded bytes.Buffer
185if int64(formatted.Len())%paddingSize != 0 {
186extra := paddingSize - (int64(formatted.Len()) % paddingSize)
187nWritten, err := padded.Write(make([]byte, extra))
188if err != nil {
189return fmt.Errorf("buffering padding: %w", err)
190}
191if int64(nWritten) != extra {
192return fmt.Errorf("short write buffering padding for disk image: %d != %d", nWritten, extra)
193}
194}
195extra := int64(formatted.Len())
196nWritten, err = padded.Write(formatted.Bytes())
197if err != nil {
198return fmt.Errorf("buffering workload config: %w", err)
199}
200if int64(nWritten) != extra {
201return fmt.Errorf("short write buffering workload config: %d != %d", nWritten, extra)
202}
203
204// Write the buffer to the file, starting with padding.
205if _, err = imageFile.Seek(-overwriteOffset, io.SeekEnd); err != nil {
206return fmt.Errorf("preparing to write workload config: %w", err)
207}
208nWritten, err = imageFile.Write(padded.Bytes())
209if err != nil {
210return fmt.Errorf("writing workload config: %w", err)
211}
212if nWritten != padded.Len() {
213return fmt.Errorf("short write writing configuration to disk image: %d != %d", nWritten, padded.Len())
214}
215offset, err := imageFile.Seek(0, io.SeekCurrent)
216if err != nil {
217return fmt.Errorf("preparing mark end of disk image: %w", err)
218}
219if err = imageFile.Truncate(offset); err != nil {
220return fmt.Errorf("marking end of disk image: %w", err)
221}
222return nil
223}
224