podman

Форк
0
223 строки · 8.3 Кб
1
package mkcw
2

3
import (
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

16
type (
17
	// WorkloadConfig is the data type which is encoded and stored in an image.
18
	WorkloadConfig = types.WorkloadConfig
19
	// SevWorkloadData is the type of data in WorkloadConfig.TeeData when the type is SEV.
20
	SevWorkloadData = types.SevWorkloadData
21
	// SnpWorkloadData is the type of data in WorkloadConfig.TeeData when the type is SNP.
22
	SnpWorkloadData = types.SnpWorkloadData
23
	// TeeType is one of the known types of trusted execution environments for which we
24
	// can generate suitable image contents.
25
	TeeType = define.TeeType
26
)
27

28
const (
29
	maxWorkloadConfigSize    = 1024 * 1024
30
	preferredPaddingBoundary = 4096
31
	// SEV is a known trusted execution environment type: AMD-SEV
32
	SEV = define.SEV
33
	// SEV_NO_ES is a known trusted execution environment type: AMD-SEV without encrypted state
34
	SEV_NO_ES = types.SEV_NO_ES //revive:disable-line:var-naming
35
	// SNP is a known trusted execution environment type: AMD-SNP
36
	SNP = 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
40
	krunMagic = "KRUN"
41
)
42

43
// ReadWorkloadConfigFromImage reads the workload configuration from the
44
// specified disk image file
45
func 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.
49
	var wc WorkloadConfig
50
	f, err := os.Open(path)
51
	if err != nil {
52
		return wc, err
53
	}
54
	defer f.Close()
55

56
	// Read those last 12 bytes.
57
	finalTwelve := make([]byte, 12)
58
	if _, err = f.Seek(-12, io.SeekEnd); err != nil {
59
		return wc, fmt.Errorf("checking for workload config signature: %w", err)
60
	}
61
	if n, err := f.Read(finalTwelve); err != nil || n != len(finalTwelve) {
62
		if err != nil && !errors.Is(err, io.EOF) {
63
			return wc, fmt.Errorf("reading workload config signature (%d bytes read): %w", n, err)
64
		}
65
		if n != len(finalTwelve) {
66
			return wc, fmt.Errorf("short read (expected 12 bytes at the end of %q, got %d)", path, n)
67
		}
68
	}
69
	if magic := string(finalTwelve[0:4]); magic != "KRUN" {
70
		return wc, fmt.Errorf("expected magic string KRUN in %q, found %q)", path, magic)
71
	}
72
	length := binary.LittleEndian.Uint64(finalTwelve[4:])
73
	if length > maxWorkloadConfigSize {
74
		return 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.
78
	configBytes := make([]byte, length)
79
	if _, err = f.Seek(-(int64(length) + 12), io.SeekEnd); err != nil {
80
		return wc, fmt.Errorf("looking for workload config from disk image: %w", err)
81
	}
82
	if n, err := f.Read(configBytes); err != nil || n != len(configBytes) {
83
		if err != nil {
84
			return wc, fmt.Errorf("reading workload config from disk image: %w", err)
85
		}
86
		return wc, fmt.Errorf("short read (expected %d bytes near the end of %q, got %d)", len(configBytes), path, n)
87
	}
88
	err = json.Unmarshal(configBytes, &wc)
89
	if err != nil {
90
		err = fmt.Errorf("unmarshaling configuration %q: %w", string(configBytes), err)
91
	}
92
	return 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
98
func 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.
100
	var overwriteOffset int64
101
	if overwrite {
102
		finalTwelve := make([]byte, 12)
103
		if _, err := imageFile.Seek(-12, io.SeekEnd); err != nil {
104
			return fmt.Errorf("checking for workload config signature: %w", err)
105
		}
106
		if n, err := imageFile.Read(finalTwelve); err != nil || n != len(finalTwelve) {
107
			if err != nil && !errors.Is(err, io.EOF) {
108
				return fmt.Errorf("reading workload config signature (%d bytes read): %w", n, err)
109
			}
110
			if n != len(finalTwelve) {
111
				return fmt.Errorf("short read (expected 12 bytes at the end of %q, got %d)", imageFile.Name(), n)
112
			}
113
		}
114
		if magic := string(finalTwelve[0:4]); magic == "KRUN" {
115
			length := binary.LittleEndian.Uint64(finalTwelve[4:])
116
			if length < maxWorkloadConfigSize {
117
				overwriteOffset = int64(length + 12)
118
			}
119
		}
120
	}
121
	// If we found a configuration in the file, try to figure out how much padding was used.
122
	paddingSize := int64(preferredPaddingBoundary)
123
	if overwriteOffset != 0 {
124
		st, err := imageFile.Stat()
125
		if err != nil {
126
			return err
127
		}
128
		for _, possiblePaddingLength := range []int64{0x100000, 0x10000, 0x1000, 0x200, 0x100} {
129
			if overwriteOffset > possiblePaddingLength {
130
				continue
131
			}
132
			if st.Size()%possiblePaddingLength != 0 {
133
				continue
134
			}
135
			if _, err := imageFile.Seek(-possiblePaddingLength, io.SeekEnd); err != nil {
136
				return fmt.Errorf("checking size of padding at end of file: %w", err)
137
			}
138
			buf := make([]byte, possiblePaddingLength)
139
			n, err := imageFile.Read(buf)
140
			if err != nil {
141
				return fmt.Errorf("reading possible padding at end of file: %w", err)
142
			}
143
			if n != len(buf) {
144
				return fmt.Errorf("short read checking size of padding at end of file: %d != %d", n, len(buf))
145
			}
146
			if bytes.Equal(buf[:possiblePaddingLength-overwriteOffset], make([]byte, possiblePaddingLength-overwriteOffset)) {
147
				// everything up to the configuration was zero bytes, so it was padding
148
				overwriteOffset = possiblePaddingLength
149
				paddingSize = possiblePaddingLength
150
				break
151
			}
152
		}
153
	}
154

155
	// Append the krun configuration to a new buffer.
156
	var formatted bytes.Buffer
157
	nWritten, err := formatted.Write(workloadConfigBytes)
158
	if err != nil {
159
		return fmt.Errorf("building workload config: %w", err)
160
	}
161
	if nWritten != len(workloadConfigBytes) {
162
		return fmt.Errorf("short write appending configuration to buffer: %d != %d", nWritten, len(workloadConfigBytes))
163
	}
164
	// Append the magic string to the buffer.
165
	nWritten, err = formatted.WriteString(krunMagic)
166
	if err != nil {
167
		return fmt.Errorf("building workload config signature: %w", err)
168
	}
169
	if nWritten != len(krunMagic) {
170
		return 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.
173
	workloadConfigLengthBytes := make([]byte, 8)
174
	binary.LittleEndian.PutUint64(workloadConfigLengthBytes, uint64(len(workloadConfigBytes)))
175
	nWritten, err = formatted.Write(workloadConfigLengthBytes)
176
	if err != nil {
177
		return fmt.Errorf("building workload config signature size: %w", err)
178
	}
179
	if nWritten != len(workloadConfigLengthBytes) {
180
		return 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.
184
	var padded bytes.Buffer
185
	if int64(formatted.Len())%paddingSize != 0 {
186
		extra := paddingSize - (int64(formatted.Len()) % paddingSize)
187
		nWritten, err := padded.Write(make([]byte, extra))
188
		if err != nil {
189
			return fmt.Errorf("buffering padding: %w", err)
190
		}
191
		if int64(nWritten) != extra {
192
			return fmt.Errorf("short write buffering padding for disk image: %d != %d", nWritten, extra)
193
		}
194
	}
195
	extra := int64(formatted.Len())
196
	nWritten, err = padded.Write(formatted.Bytes())
197
	if err != nil {
198
		return fmt.Errorf("buffering workload config: %w", err)
199
	}
200
	if int64(nWritten) != extra {
201
		return fmt.Errorf("short write buffering workload config: %d != %d", nWritten, extra)
202
	}
203

204
	// Write the buffer to the file, starting with padding.
205
	if _, err = imageFile.Seek(-overwriteOffset, io.SeekEnd); err != nil {
206
		return fmt.Errorf("preparing to write workload config: %w", err)
207
	}
208
	nWritten, err = imageFile.Write(padded.Bytes())
209
	if err != nil {
210
		return fmt.Errorf("writing workload config: %w", err)
211
	}
212
	if nWritten != padded.Len() {
213
		return fmt.Errorf("short write writing configuration to disk image: %d != %d", nWritten, padded.Len())
214
	}
215
	offset, err := imageFile.Seek(0, io.SeekCurrent)
216
	if err != nil {
217
		return fmt.Errorf("preparing mark end of disk image: %w", err)
218
	}
219
	if err = imageFile.Truncate(offset); err != nil {
220
		return fmt.Errorf("marking end of disk image: %w", err)
221
	}
222
	return nil
223
}
224

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.