-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathimageutils.go
More file actions
297 lines (242 loc) · 9.93 KB
/
imageutils.go
File metadata and controls
297 lines (242 loc) · 9.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
package imagecustomizerlib
import (
"fmt"
"path/filepath"
"sort"
"github.com/microsoft/azurelinux/toolkit/tools/imagecustomizerapi"
"github.com/microsoft/azurelinux/toolkit/tools/imagegen/configuration"
"github.com/microsoft/azurelinux/toolkit/tools/imagegen/diskutils"
"github.com/microsoft/azurelinux/toolkit/tools/imagegen/installutils"
"github.com/microsoft/azurelinux/toolkit/tools/internal/file"
"github.com/microsoft/azurelinux/toolkit/tools/internal/safechroot"
"github.com/microsoft/azurelinux/toolkit/tools/internal/sliceutils"
"github.com/microsoft/azurelinux/toolkit/tools/internal/targetos"
)
type installOSFunc func(imageChroot *safechroot.Chroot) error
func connectToExistingImage(imageFilePath string, buildDir string, chrootDirName string, includeDefaultMounts bool,
) (*ImageConnection, map[string]diskutils.FstabEntry, []verityDeviceMetadata, []OsPackage, error) {
imageConnection := NewImageConnection()
partUuidToMountPath, verityMetadata, err := connectToExistingImageHelper(imageConnection, imageFilePath, buildDir,
chrootDirName, includeDefaultMounts)
if err != nil {
imageConnection.Close()
return nil, nil, nil, nil, err
}
packages, err := getAllPackagesFromChroot(imageConnection)
if err != nil {
return nil, nil, nil, nil, err
}
return imageConnection, partUuidToMountPath, verityMetadata, packages, nil
}
func connectToExistingImageHelper(imageConnection *ImageConnection, imageFilePath string,
buildDir string, chrootDirName string, includeDefaultMounts bool,
) (map[string]diskutils.FstabEntry, []verityDeviceMetadata, error) {
// Connect to image file using loopback device.
err := imageConnection.ConnectLoopback(imageFilePath)
if err != nil {
return nil, nil, err
}
partitions, err := diskutils.GetDiskPartitions(imageConnection.Loopback().DevicePath())
if err != nil {
return nil, nil, err
}
rootfsPartition, err := findRootfsPartition(partitions, buildDir)
if err != nil {
return nil, nil, fmt.Errorf("failed to find rootfs partition:\n%w", err)
}
fstabEntries, err := readFstabEntriesFromRootfs(rootfsPartition, partitions, buildDir)
if err != nil {
return nil, nil, fmt.Errorf("failed to read fstab entries from rootfs partition:\n%w", err)
}
mountPoints, partUuidToFstabEntry, verityMetadata, err := fstabEntriesToMountPoints(fstabEntries, partitions,
buildDir)
if err != nil {
return nil, nil, fmt.Errorf("failed to find mount info for fstab file entries:\n%w", err)
}
// Create chroot environment.
imageChrootDir := filepath.Join(buildDir, chrootDirName)
err = imageConnection.ConnectChroot(imageChrootDir, false, []string(nil), mountPoints, includeDefaultMounts)
if err != nil {
return nil, nil, err
}
return partUuidToFstabEntry, verityMetadata, nil
}
func createNewImage(targetOs targetos.TargetOs, filename string, diskConfig imagecustomizerapi.Disk,
fileSystems []imagecustomizerapi.FileSystem, buildDir string, chrootDirName string,
installOS installOSFunc,
) (map[string]string, error) {
imageConnection := NewImageConnection()
defer imageConnection.Close()
partIdToPartUuid, err := createNewImageHelper(targetOs, imageConnection, filename, diskConfig, fileSystems,
buildDir, chrootDirName, installOS)
if err != nil {
return nil, fmt.Errorf("failed to create new image:\n%w", err)
}
// Close image.
err = imageConnection.CleanClose()
if err != nil {
return nil, err
}
return partIdToPartUuid, nil
}
func createNewImageHelper(targetOs targetos.TargetOs, imageConnection *ImageConnection, filename string,
diskConfig imagecustomizerapi.Disk, fileSystems []imagecustomizerapi.FileSystem, buildDir string,
chrootDirName string, installOS installOSFunc,
) (map[string]string, error) {
// Convert config to image config types, so that the imager's utils can be used.
imagerDiskConfig, err := diskConfigToImager(diskConfig, fileSystems)
if err != nil {
return nil, err
}
imagerPartitionSettings, err := partitionSettingsToImager(fileSystems)
if err != nil {
return nil, err
}
// Create imager boilerplate.
partIdToPartUuid, tmpFstabFile, err := createImageBoilerplate(targetOs, imageConnection, filename, buildDir,
chrootDirName, imagerDiskConfig, imagerPartitionSettings)
if err != nil {
return nil, err
}
// Install the OS.
err = installOS(imageConnection.Chroot())
if err != nil {
return nil, err
}
// Move the fstab file into the image.
imageFstabFilePath := filepath.Join(imageConnection.Chroot().RootDir(), "etc/fstab")
err = file.Move(tmpFstabFile, imageFstabFilePath)
if err != nil {
return nil, fmt.Errorf("failed to move fstab into new image:\n%w", err)
}
return partIdToPartUuid, nil
}
func configureDiskBootLoader(imageConnection *ImageConnection, rootMountIdType imagecustomizerapi.MountIdentifierType,
bootType imagecustomizerapi.BootType, selinuxConfig imagecustomizerapi.SELinux,
kernelCommandLine imagecustomizerapi.KernelCommandLine, currentSELinuxMode imagecustomizerapi.SELinuxMode,
) error {
imagerBootType, err := bootTypeToImager(bootType)
if err != nil {
return err
}
imagerKernelCommandLine, err := kernelCommandLineToImager(kernelCommandLine, selinuxConfig, currentSELinuxMode)
if err != nil {
return err
}
imagerRootMountIdType, err := mountIdentifierTypeToImager(rootMountIdType)
if err != nil {
return err
}
grubMkconfigEnabled, err := isGrubMkconfigEnabled(imageConnection.Chroot())
if err != nil {
return err
}
mountPointMap := make(map[string]string)
for _, mountPoint := range imageConnection.Chroot().GetMountPoints() {
mountPointMap[mountPoint.GetTarget()] = mountPoint.GetSource()
}
// Configure the boot loader.
err = installutils.ConfigureDiskBootloaderWithRootMountIdType(imagerBootType, false, imagerRootMountIdType,
imagerKernelCommandLine, imageConnection.Chroot(), imageConnection.Loopback().DevicePath(),
mountPointMap, diskutils.EncryptedRootDevice{}, grubMkconfigEnabled,
!grubMkconfigEnabled)
if err != nil {
return fmt.Errorf("failed to install bootloader:\n%w", err)
}
return nil
}
func createImageBoilerplate(targetOs targetos.TargetOs, imageConnection *ImageConnection, filename string,
buildDir string, chrootDirName string, imagerDiskConfig configuration.Disk,
imagerPartitionSettings []configuration.PartitionSetting,
) (map[string]string, string, error) {
// Create raw disk image file.
err := diskutils.CreateSparseDisk(filename, imagerDiskConfig.MaxSize, 0o644)
if err != nil {
return nil, "", fmt.Errorf("failed to create empty disk file (%s):\n%w", filename, err)
}
// Connect raw disk image file.
err = imageConnection.ConnectLoopback(filename)
if err != nil {
return nil, "", err
}
// Set up partitions.
partIDToDevPathMap, partIDToFsTypeMap, _, err := diskutils.CreatePartitions(
targetOs, imageConnection.Loopback().DevicePath(), imagerDiskConfig, configuration.RootEncryption{})
if err != nil {
return nil, "", fmt.Errorf("failed to create partitions on disk (%s):\n%w", imageConnection.Loopback().DevicePath(), err)
}
// Read the disk partitions.
diskPartitions, err := diskutils.GetDiskPartitions(imageConnection.Loopback().DevicePath())
if err != nil {
return nil, "", err
}
// Create mapping from partition ID to partition UUID.
partIdToPartUuid, err := createPartIdToPartUuidMap(partIDToDevPathMap, diskPartitions)
if err != nil {
return nil, "", err
}
// Create the fstab file.
// This is done so that we can read back the file using findmnt, which conveniently splits the vfs and fs mount
// options for us. If we wanted to handle this more directly, we could create a golang wrapper around libmount
// (which is what findmnt uses). But we are already using the findmnt in other places.
tmpFstabFile := filepath.Join(buildDir, chrootDirName+"_fstab")
err = file.RemoveFileIfExists(tmpFstabFile)
if err != nil {
return nil, "", err
}
mountPointMap, mountPointToFsTypeMap, mountPointToMountArgsMap, _ := installutils.CreateMountPointPartitionMap(
partIDToDevPathMap, partIDToFsTypeMap, imagerPartitionSettings,
)
mountList := sliceutils.MapToSlice(mountPointMap)
// Sort the mounts so that they are mounted in the correct oder.
sort.Slice(mountList, func(i, j int) bool {
return mountList[i] < mountList[j]
})
err = installutils.UpdateFstabFile(tmpFstabFile, imagerPartitionSettings, mountList, mountPointMap,
mountPointToFsTypeMap, mountPointToMountArgsMap, partIDToDevPathMap, partIDToFsTypeMap,
false, /*hidepidEnabled*/
)
if err != nil {
return nil, "", fmt.Errorf("failed to write temp fstab file:\n%w", err)
}
// Read back the fstab file.
fstabEntries, err := diskutils.ReadFstabFile(tmpFstabFile)
if err != nil {
return nil, "", err
}
mountPoints, _, _, err := fstabEntriesToMountPoints(fstabEntries, diskPartitions, buildDir)
if err != nil {
return nil, "", fmt.Errorf("failed to find mount info for fstab file entries:\n%w", err)
}
// Create chroot environment.
imageChrootDir := filepath.Join(buildDir, chrootDirName)
err = imageConnection.ConnectChroot(imageChrootDir, false, nil, mountPoints, false)
if err != nil {
return nil, "", err
}
return partIdToPartUuid, tmpFstabFile, nil
}
func createPartIdToPartUuidMap(partIDToDevPathMap map[string]string, diskPartitions []diskutils.PartitionInfo,
) (map[string]string, error) {
partIdToPartUuid := make(map[string]string)
for partId, devPath := range partIDToDevPathMap {
partition, found := sliceutils.FindValueFunc(diskPartitions, func(partition diskutils.PartitionInfo) bool {
return devPath == partition.Path
})
if !found {
return nil, fmt.Errorf("failed to find partition for device path (%s)", devPath)
}
partIdToPartUuid[partId] = partition.PartUuid
}
return partIdToPartUuid, nil
}
func extractOSRelease(imageConnection *ImageConnection) (string, error) {
osReleasePath := filepath.Join(imageConnection.Chroot().RootDir(), "etc/os-release")
data, err := file.Read(osReleasePath)
if err != nil {
return "", fmt.Errorf("failed to read /etc/os-release:\n%w", err)
}
return string(data), nil
}