mirror of
https://github.com/flatcar/scripts.git
synced 2025-08-09 05:56:58 +02:00
We need more flexible image creation tool for various layout, like "RMA with multiple release images" which needs 4 images (install, test, release A, release B). To make the creation of image more flexible, this CL changes make_universal_factory_shim to using a "layout file" that can describe any disk image format we want. Image creation speed is also improved by aligning partition offsets and restricting oflag=dsync applied only when being used for block devices so that make_universal_factory_shim (outputs to a normal temporary file) becomes faster by utilizing system cache. For a system with every images already cached: - Time for building 3 in 1 factory instal shim: Before = 8 seconds, After = 5 seconds. - Time for full RMA shim creation: Before = 1m25s, After = 40-50 seconds. BUG=chrome-os-partner:4108 TEST=./make_universal_factory_shim.sh image1 image2 image3 image4 # boots correctly after being imaged to SD card. ./make_factory_package.sh --usbimg RMA .... # RMA image can install images and works correctly Change-Id: I645196d6d6e0a24d3dfa4c413a338279df4c0d5b Reviewed-on: http://gerrit.chromium.org/gerrit/6893 Tested-by: Hung-Te Lin <hungte@chromium.org> Reviewed-by: Nick Sanders <nsanders@chromium.org>
294 lines
7.7 KiB
Bash
Executable File
294 lines
7.7 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
# Script to generate an universal factory install shim image, by merging
|
|
# multiple images signed by different keys.
|
|
# CAUTION: Recovery shim images are not supported yet because they require the
|
|
# kernel partitions to be laid out in a special way
|
|
|
|
SCRIPT="$0"
|
|
SCRIPT_ROOT=$(dirname "$SCRIPT")
|
|
|
|
# Load functions designed for image processing
|
|
. "${SCRIPT_ROOT}/lib/cros_image_common.sh" || exit 1
|
|
|
|
# CGPT Header: PMBR, header, table; sec_table, sec_header
|
|
CGPT_START_SIZE=$((1 + 1 + 32))
|
|
CGPT_END_SIZE=$((32 + 1))
|
|
CGPT_BS="512"
|
|
|
|
# Alignment of partition sectors
|
|
PARTITION_SECTOR_ALIGNMENT=256
|
|
|
|
LAYOUT_FILE="$(mktemp --tmpdir)"
|
|
|
|
RESERVED_PARTITION="10"
|
|
LEGACY_PARTITIONS="10 11 12" # RESERVED, RWFW, EFI
|
|
MAX_INPUT_SOURCES=4 # (2~9) / 2
|
|
|
|
alert() {
|
|
echo "$*" >&2
|
|
}
|
|
|
|
die() {
|
|
alert "ERROR: $*"
|
|
exit 1
|
|
}
|
|
|
|
on_exit() {
|
|
rm -f "$LAYOUT_FILE"
|
|
}
|
|
|
|
# Returns offset aligned to alignment.
|
|
# If size is given, only align if size >= alignment.
|
|
image_alignment() {
|
|
local offset="$1"
|
|
local alignment="$2"
|
|
local size="$3"
|
|
|
|
# If size is assigned, align only if the new size is larger then alignment.
|
|
if [ "$((offset % alignment))" != "0" ]; then
|
|
if [ -z "$size" -o "$size" -ge "$alignment" ]; then
|
|
offset=$((offset + alignment - (offset % alignment)))
|
|
fi
|
|
fi
|
|
echo "$((offset))"
|
|
}
|
|
|
|
# Processes a logical disk image layout description file.
|
|
# Each entry in layout is a "file:partnum" entry (:partnum is optional),
|
|
# referring to the #partnum partition in file.
|
|
# The index starts at one, referring to the first partition in layout.
|
|
image_process_layout() {
|
|
local layout_file="$1"
|
|
local callback="$2"
|
|
shift
|
|
shift
|
|
local param="$@"
|
|
local index=0
|
|
|
|
while read layout; do
|
|
local image_file="${layout%:*}"
|
|
local part_num="${layout#*:}"
|
|
index="$((index + 1))"
|
|
[ "$image_file" != "$layout" ] || part_num=""
|
|
|
|
"$callback" "$image_file" "$part_num" "$index" "$param"
|
|
done <"$layout_file"
|
|
}
|
|
|
|
# Processes a list of disk geometry sectors into aligned (offset, sectors) form.
|
|
# The index starts at zero, referring to the partition table object itself.
|
|
image_process_geometry() {
|
|
local sectors_list="$1"
|
|
local callback="$2"
|
|
shift
|
|
shift
|
|
local param="$@"
|
|
local offset=0 sectors
|
|
local index=0
|
|
|
|
for sectors in $sectors_list; do
|
|
offset="$(image_alignment $offset $PARTITION_SECTOR_ALIGNMENT $sectors)"
|
|
"$callback" "$offset" "$sectors" "$index" "$param"
|
|
offset="$((offset + sectors))"
|
|
index="$((index + 1))"
|
|
done
|
|
}
|
|
|
|
# Callback of image_process_layout. Returns the size (in sectors) of given
|
|
# object (partition in image or file).
|
|
layout_get_sectors() {
|
|
local image_file="$1"
|
|
local part_num="$2"
|
|
|
|
if [ -n "$part_num" ]; then
|
|
image_part_size "$image_file" "$part_num"
|
|
else
|
|
image_alignment "$(stat -c"%s" "$image_file")" $CGPT_BS ""
|
|
fi
|
|
}
|
|
|
|
# Callback of image_process_layout. Copies an input source object (file or
|
|
# partition) into specified partition on output file.
|
|
layout_copy_partition() {
|
|
local input_file="$1"
|
|
local input_part="$2"
|
|
local output_part="$3"
|
|
local output_file="$4"
|
|
alert "$(basename "$input_file"):$input_part =>" \
|
|
"$(basename "$output_file"):$output_part"
|
|
|
|
if [ -n "$part_num" ]; then
|
|
# TODO(hungte) update partition type if available
|
|
image_partition_copy "$input_file" "$input_part" \
|
|
"$output_file" "$output_part"
|
|
# Update partition type information
|
|
local partition_type="$(cgpt show -q -n -t -i "$input_part" "$input_file")"
|
|
local partition_attr="$(cgpt show -q -n -A -i "$input_part" "$input_file")"
|
|
local partition_label="$(cgpt show -q -n -l -i "$input_part" "$input_file")"
|
|
cgpt add -t "$partition_type" -l "$partition_label" -A "$partition_attr" \
|
|
-i "$output_part" "$output_file"
|
|
else
|
|
image_update_partition "$output_file" "$output_part" <"$input_file"
|
|
fi
|
|
}
|
|
|
|
|
|
# Callback of image_process_geometry. Creates a partition by give offset,
|
|
# size(sectors), and index.
|
|
geometry_create_partition() {
|
|
local offset="$1"
|
|
local sectors="$2"
|
|
local index="$3"
|
|
local output_file="$4"
|
|
|
|
if [ "$offset" = "0" ]; then
|
|
# first entry is CGPT; ignore.
|
|
return
|
|
fi
|
|
cgpt add -b $offset -s $sectors -i $index -t reserved "$output_file"
|
|
}
|
|
|
|
# Callback of image_process_geometry. Prints the proper offset of current
|
|
# partition by give offset and size.
|
|
geometry_get_partition_offset() {
|
|
local offset="$1"
|
|
local sectors="$2"
|
|
local index="$3"
|
|
|
|
image_alignment "$offset" "$PARTITION_SECTOR_ALIGNMENT" "$sectors"
|
|
}
|
|
|
|
build_image_file() {
|
|
local layout_file="$1"
|
|
local output_file="$2"
|
|
local output_file_size=0
|
|
local sectors_list partition_offsets
|
|
|
|
# Check and obtain size information from input sources
|
|
sectors_list="$(image_process_layout "$layout_file" layout_get_sectors)"
|
|
|
|
# Calculate output image file size
|
|
partition_offsets="$(image_process_geometry \
|
|
"$CGPT_START_SIZE $sectors_list $CGPT_END_SIZE 1" \
|
|
geometry_get_partition_offset)"
|
|
output_file_size="$(echo "$partition_offsets" | tail -n 1)"
|
|
|
|
# Create empty image file
|
|
truncate -s "0" "$output_file" # starting with a new file is much faster.
|
|
truncate -s "$((output_file_size * CGPT_BS))" "$output_file"
|
|
|
|
# Initialize partition table (GPT)
|
|
cgpt create "$output_file"
|
|
cgpt boot -p "$output_file" >/dev/null
|
|
|
|
# Create partition tables
|
|
image_process_geometry "$CGPT_START_SIZE $sectors_list" \
|
|
geometry_create_partition \
|
|
"$output_file"
|
|
# Copy partitions content
|
|
image_process_layout "$layout_file" layout_copy_partition "$output_file"
|
|
}
|
|
|
|
# Creates standard multiple image layout
|
|
create_standard_layout() {
|
|
local main_source="$1"
|
|
local layout_file="$2"
|
|
local image index
|
|
shift
|
|
shift
|
|
|
|
for image in "$main_source" "$@"; do
|
|
if [ ! -f "$image" ]; then
|
|
die "Cannot find input file $image."
|
|
fi
|
|
done
|
|
|
|
echo "$main_source:1" >>"$layout_file" # stateful partition
|
|
for index in $(seq 1 $MAX_INPUT_SOURCES); do
|
|
local kernel_source="$main_source:$RESERVED_PARTITION"
|
|
local rootfs_source="$main_source:$RESERVED_PARTITION"
|
|
if [ "$#" -gt 0 ]; then
|
|
# TODO(hungte) detect if input source is a recovery/USB image
|
|
kernel_source="$1:2"
|
|
rootfs_source="$1:3"
|
|
shift
|
|
fi
|
|
echo "$kernel_source" >>"$layout_file"
|
|
echo "$rootfs_source" >>"$layout_file"
|
|
done
|
|
for index in $LEGACY_PARTITIONS; do
|
|
echo "$main_source:$index" >>"$LAYOUT_FILE"
|
|
done
|
|
}
|
|
|
|
usage_die() {
|
|
alert "Usage: $SCRIPT [-m master] [-f] output shim1 [shim2 ... shim4]"
|
|
alert " or $SCRIPT -l layout [-f] output"
|
|
exit 1
|
|
}
|
|
|
|
main() {
|
|
local force=""
|
|
local image=""
|
|
local output=""
|
|
local main_source=""
|
|
local index=""
|
|
local slots="0"
|
|
local layout_mode=""
|
|
|
|
while [ "$#" -gt 1 ]; do
|
|
case "$1" in
|
|
"-f" )
|
|
force="True"
|
|
shift
|
|
;;
|
|
"-m" )
|
|
main_source="$2"
|
|
shift
|
|
shift
|
|
;;
|
|
"-l" )
|
|
cat "$2" >"$LAYOUT_FILE"
|
|
layout_mode="TRUE"
|
|
shift
|
|
shift
|
|
;;
|
|
* )
|
|
break
|
|
esac
|
|
done
|
|
|
|
if [ -n "$layout_mode" ]; then
|
|
[ "$#" = 1 ] || usage_die
|
|
elif [ "$#" -lt 2 -o "$#" -gt "$((MAX_INPUT_SOURCES + 1))" ]; then
|
|
alert "ERROR: invalid number of parameters ($#)."
|
|
usage_die
|
|
fi
|
|
|
|
if [ -z "$main_source" ]; then
|
|
main_source="$2"
|
|
fi
|
|
output="$1"
|
|
shift
|
|
|
|
if [ -f "$output" -a -z "$force" ]; then
|
|
die "Output file $output already exists. To overwrite the file, add -f."
|
|
fi
|
|
|
|
if [ -z "$layout_mode" ]; then
|
|
create_standard_layout "$main_source" "$LAYOUT_FILE" "$@"
|
|
fi
|
|
build_image_file "$LAYOUT_FILE" "$output"
|
|
echo ""
|
|
echo "Image created: $output"
|
|
}
|
|
|
|
set -e
|
|
trap on_exit EXIT
|
|
main "$@"
|