-
Notifications
You must be signed in to change notification settings - Fork 246
/
280_create_bootable_disk_image.sh
245 lines (180 loc) · 11.6 KB
/
280_create_bootable_disk_image.sh
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
# Create the raw disk image
#
# The disk image will be created as a bootable disk image supporting booting via EFI and/or Legacy BIOS if the
# respective bootloaders are available. This script expects an EFI bootloader to be prepared in advance in a
# staging directory.
#
# If an EFI bootloader has been prepared, $RAWDISK_BOOT_EFI_STAGING_ROOT must point to the EFI staging directory.
#
### Check prerequisites
local use_syslinux_legacy=no
if has_binary syslinux && [[ -z "$SECURE_BOOT_BOOTLOADER" ]]; then
if is_true "${RAWDISK_BOOT_EXCLUDE_SYSLINUX_LEGACY:-no}"; then
LogPrint "DISABLED: Using syslinux to create a BIOS Legacy bootloader"
else
use_syslinux_legacy=yes
fi
fi
if [[ -z "$RAWDISK_BOOT_EFI_STAGING_ROOT" ]] && ! is_true $use_syslinux_legacy; then
Error "Creating a raw disk image requires an EFI bootloader or syslinux"
fi
### Create an initial disk image
# Wait for file systems to settle before trying to determine partition content size
sync
sleep 3 # should not be strictly necessary as Linux sync(2) waits until data is written
# Determine the appropriate size (adding 1 MiB for plus 7% for file system overhead/reserve)
local staged_boot_partition_contents=( "$KERNEL_FILE" "$TMP_DIR/$REAR_INITRD_FILENAME" )
[[ -n "$RAWDISK_BOOT_EFI_STAGING_ROOT" ]] && staged_boot_partition_contents+=( "$RAWDISK_BOOT_EFI_STAGING_ROOT" )
local disk_image_size_MiB="$(du -m --summarize --total "${staged_boot_partition_contents[@]}" | awk '$2 == "total" { printf("%.0f\n", 1 + ($1 * 1.07) + .5); }')"
local full_disk_name="${RAWDISK_IMAGE_NAME:-rear-$HOSTNAME}.raw"
local disk_image="$TMP_DIR/$full_disk_name"
LogPrint "Creating $disk_image_size_MiB MiB raw disk image \"$full_disk_name\""
dd if=/dev/zero of="$disk_image" bs=1M count="$disk_image_size_MiB"
[ -f "$disk_image" ] || Error "Could not create initial disk image file $disk_image"
### Create a GPT partition table
# Determine the configuration for the boot partition
local typecode="8300" # Linux partition for non-EFI booting
[[ -n "$RAWDISK_BOOT_EFI_STAGING_ROOT" ]] && typecode="ef00" # EFI System partition if an EFI bootloader has been prepared
local legacy_boot_option=""
is_true $use_syslinux_legacy && legacy_boot_option="--attributes=1:set:2" # mark partition as Legacy BIOS-bootable
local guid_option=""
[[ -n "$RAWDISK_PTUUID" ]] && guid_option="--disk-guid=$RAWDISK_PTUUID" # Use a pre-determined partition UUID
sgdisk $guid_option --new 1::0 --typecode=1:"$typecode" --change-name=1:"${RAWDISK_GPT_PARTITION_NAME:-Rescue System}" $legacy_boot_option "$disk_image"
StopIfError "Could not create GPT partition table on $disk_image"
Log "Raw disk image partition table:"
gdisk -l "$disk_image" >>"$RUNTIME_LOGFILE"
### Create block devices representing the raw disk image
local disk_device # separate 'local' statement to avoid losing $(...) exit status - cf. https://stackoverflow.com/a/10397996
# Set up the loop device, trying the '--partscan' option first (introduced in util-linux v2.21)
# Trying '--partscan' here first avoids a situation where all of the methods below fail to make the kernel recognize
# partitions on the loop device (cf. https://github.com/rear/rear/pull/2071).
disk_device="$(losetup --show --find "$disk_image" --partscan || losetup --show --find "$disk_image")"
StopIfError "Could not create loop device on $disk_image"
AddExitTask "losetup -d $disk_device >&2"
local boot_partition="${disk_device}p1"
# Try several methods to make the kernel recognize partitions on the loop device, if not already done automatically:
# 1. partprobe
if [[ ! -b "$boot_partition" ]] && has_binary partprobe; then
partprobe "$disk_device" || LogPrintError "partprobe failed to make the kernel recognize loop device partitions"
fi
# 2. Heuristic: wait
[[ ! -b "$boot_partition" ]] && sleep 5
# 3. kpartx
local use_kpartx=no
if [[ ! -b "$boot_partition" ]]; then
if has_binary kpartx; then
kpartx -as "$disk_device" # detect partitions, create device nodes - synchronously (wait until done)
StopIfError "kpartx could not create loop partition device nodes from loop device $disk_device"
AddExitTask "kpartx -d $disk_device >&2"
if [[ ! -b "$boot_partition" ]]; then
# Some versions of kpartx (as on CentOS 6) do not create partition device nodes under /dev, but leave
# them under /dev/mapper instead. This kludge will pick those up.
local alternate_boot_partition="/dev/mapper/"$(basename "$boot_partition")
[[ -b "$alternate_boot_partition" ]] && boot_partition="$alternate_boot_partition"
fi
use_kpartx=yes
else
LogPrintError "It seems that the current linux kernel does not support loop device partitions."
LogPrintError "TIP: You might achieve loop device partition support by installing the 'kpartx' tool, if available."
fi
fi
# If unsuccessful, say so.
[[ -b "$boot_partition" ]] || Error "Cannot prepare boot file system for the RAWDISK image: Missing OS support for loop device partitions."
### Create and populate the boot file system
# Note: Having a small EFI System Partition (ESP) might introduce problems:
# - The UEFI spec seems to require a FAT32 EFI System Partition (ESP).
# - syslinux/Legacy BIOS fails to install on small FAT32 partitions with "syslinux: zero FAT sectors (FAT12/16)".
# - Some firmwares fail to boot from small FAT32 partitions.
# - Some firmwares fail to boot from FAT16 partitions.
# See:
# - http://www.rodsbooks.com/efi-bootloaders/principles.html
# - http://lists.openembedded.org/pipermail/openembedded-core/2012-January/055999.html
# As there seems to be no silver bullet, let mkfs.vfat choose the 'right' FAT partition type based on partition size
# (i.e. do not use the '-F 32' option) and hope for the best...
mkfs.vfat $v "$boot_partition" -n "${RAWDISK_FAT_VOLUME_LABEL:-RESCUE SYS}" 2>>"$RUNTIME_LOGFILE" || Error "Could not create boot file system"
local boot_partition_root="$TMP_DIR/boot"
mkdir -p "$boot_partition_root" 2>>"$RUNTIME_LOGFILE" || Error "Could not create boot file system mount point"
mount "$boot_partition" "$boot_partition_root" 2>>"$RUNTIME_LOGFILE" || Error "Could not mount boot file system"
AddExitTask "umount $boot_partition_root >&2"
# Populate the boot file system with kernel, initrd and possibly EFI bootloader
cp -rL $v "${staged_boot_partition_contents[@]}" "$boot_partition_root" >&2 || Error "Could not populate boot partition"
### Install syslinux stuff as required
# Note: This may add files to the boot file system *and* modify the boot sector directly within the raw disk image.
# After installing a Legacy BIOS bootloader, files on the boot partition should not change: The bootloader file is
# patched during installation with a list of its exact on-disk block locations.
if is_true "$RAWDISK_BOOT_USING_SYSLINUX" || is_true $use_syslinux_legacy; then
# Install syslinux configuration, which may be shared between syslinux/EFI and syslinux/Legacy bootloaders.
local syslinux_installation_dir="$boot_partition_root/syslinux"
mkdir -p "$syslinux_installation_dir" 2>>"$RUNTIME_LOGFILE" || Error "Could not create syslinux bootloader directory"
cat > "$syslinux_installation_dir/syslinux.cfg" << EOF
DEFAULT rescue
LABEL rescue
SAY
SAY ${RAWDISK_BOOT_SYSLINUX_START_INFORMATION:-Starting the rescue system...}
KERNEL ../$(basename "$KERNEL_FILE")
APPEND $KERNEL_CMDLINE
INITRD ../$REAR_INITRD_FILENAME
EOF
StopIfError "Could not write syslinux bootloader configuration"
if is_true $use_syslinux_legacy; then
LogPrint "Using syslinux to install a Legacy BIOS bootloader"
# Install syslinux as Legacy BIOS bootloader
syslinux --directory "/syslinux" --install "$boot_partition"
StopIfError "Could not install Legacy BIOS bootloader (syslinux)"
# Install the BIOS boot sector
dd if="$(find_syslinux_file gptmbr.bin)" of="$disk_device" bs=440 count=1 oflag=sync
StopIfError "Could not install Legacy BIOS boot sector (syslinux)"
fi
fi
Log "Raw disk boot partition capacity after copying:"
df -h "$boot_partition_root" >>"$RUNTIME_LOGFILE"
### Allow examining the boot file system and loop device for debugging
if is_true ${RAWDISK_DEBUG:-no}; then
LogUserOutput "Entering shell to examine the raw disk's boot file system, exit to continue:"
(cd "$boot_partition_root"; bash) <&6 >&7 2>&8
fi
### Unmount the boot partition
# Note: Unmounting before possibly copying the boot partition to local disk partitions ensures that
# the file system contents are completely flushed from its caches and the boot partition is synced.
umount "$boot_partition_root" 2>>"$RUNTIME_LOGFILE" || Error "Could not unmount boot file system"
RemoveExitTask "umount $boot_partition_root >&2"
### Copy the EFI boot partition to local disk partitions named "$RAWDISK_INSTALL_GPT_PARTITION_NAME" if configured
if [[ -n "$RAWDISK_BOOT_EFI_STAGING_ROOT" && -n "$RAWDISK_INSTALL_GPT_PARTITION_NAME" ]]; then
local detected_fs_type detected_mount_point install_partition install_partition_count=0
LogPrint "Installing the EFI rescue system to partitions labeled '$RAWDISK_INSTALL_GPT_PARTITION_NAME'"
for install_partition in $(lsblk --paths --list --noheadings --output=NAME,PARTLABEL | awk "/ $RAWDISK_INSTALL_GPT_PARTITION_NAME"'$/ { print $1; }'); do
local cannot_install_partition="Cannot install the EFI rescue system to partition '$install_partition'"
# The loop device partition we have just created will most probably have the required partition name: Ignore it.
[[ "$install_partition" == "$boot_partition" ]] && continue
detected_fs_type="$(lsblk --output FSTYPE --noheadings "$install_partition")" 2>>"$RUNTIME_LOGFILE" || Error "Could not analyze the file system type on '$install_partition'"
if [[ "$detected_fs_type" != "" && "$detected_fs_type" != "vfat" ]]; then
LogPrintError "$cannot_install_partition with type '$detected_fs_type' (must be 'vfat' or empty)"
continue
fi
detected_mount_point="$(lsblk --output MOUNTPOINT --noheadings "$install_partition")" 2>>"$RUNTIME_LOGFILE" || Error "Could not detect whether '$install_partition' is mounted"
if [[ -n "$detected_mount_point" ]]; then
LogPrintError "$cannot_install_partition as it is currently mounted at '$detected_mount_point'"
continue
fi
LogPrint "Installing the EFI rescue system to partition '$install_partition'"
dd if="$boot_partition" bs=1024 of="$install_partition" 2>>"$RUNTIME_LOGFILE" || Error "Could not copy the EFI rescue system to partition '$install_partition'"
install_partition_count=$((install_partition_count + 1))
done
((install_partition_count == 0)) && LogPrint "Could not detect suitable partitions labeled '$RAWDISK_INSTALL_GPT_PARTITION_NAME'"
fi
### Release the loop device
if is_true $use_kpartx; then
kpartx -d "$disk_device" 2>>"$RUNTIME_LOGFILE" || Error "Could not delete partition device nodes from loop device $disk_device"
RemoveExitTask "kpartx -d $disk_device >&2"
fi
losetup -d "$disk_device" 2>>"$RUNTIME_LOGFILE" || Error "Could not delete loop device"
RemoveExitTask "losetup -d $disk_device >&2"
### Compress the disk image on request
if [[ -n "$RAWDISK_IMAGE_COMPRESSION_COMMAND" ]]; then
$RAWDISK_IMAGE_COMPRESSION_COMMAND "$disk_image"
StopIfError "Could not compress disk image using <<$RAWDISK_IMAGE_COMPRESSION_COMMAND \"$disk_image\">>"
disk_image="$(echo "$disk_image"*)"
[[ -f "$disk_image" ]] || Error "Could not find compressed disk image ${disk_image}*"
fi
### Add disk the image to the result files
RESULT_FILES+=( "$disk_image" )