update_partition_table: dynamically handle resize

When changing the size of the stateful partition, we must be able to
operate on any image, with any arrangement of partitions. The static
templates cannot be used because we don't know what we're starting with.
As such, this change makes the update function walk the list of
partitions, duplicating all their details, and moves any located after
stateful by the change in size, and copies in the new stateful contents.

BUG=chromium-os:37080
TEST=link build and decryption recovery tested

Change-Id: I1131dd8ee91e5db2556bdf8f7ca12b08f35da32b
Signed-off-by: Kees Cook <keescook@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/39424
Reviewed-by: Will Drewry <wad@chromium.org>
Reviewed-by: Liam McLoughlin <lmcloughlin@chromium.org>
Tested-by: Liam McLoughlin <lmcloughlin@chromium.org>
This commit is contained in:
Kees Cook 2012-12-07 17:02:30 -08:00 committed by Gerrit
parent 48b5200b92
commit 9ea1347ed9

View File

@ -224,59 +224,75 @@ build_gpt() {
# mod_image_for_recovery.sh. # mod_image_for_recovery.sh.
update_partition_table() { update_partition_table() {
local src_img=$1 # source image local src_img=$1 # source image
local temp_state=$2 # stateful partition image local src_state=$2 # stateful partition image
local resized_sectors=$3 # number of sectors in resized stateful partition local dst_stateful_blocks=$3 # number of blocks in resized stateful partition
local temp_img=$4 local dst_img=$4
local temp_pmbr=$(mktemp "/tmp/pmbr.XXXXXX") rm -f "${dst_img}"
dd if="${src_img}" of="${temp_pmbr}" bs=512 count=1 &>/dev/null
trap "rm -rf \"${temp_pmbr}\"" EXIT
local stateful_bytes=$(( resized_sectors * 512 )) # Calculate change in image size.
local src_stateful_blocks=$(cgpt show -i 1 -s ${src_img})
local delta_blocks=$(( dst_stateful_blocks - src_stateful_blocks ))
local dst_stateful_bytes=$(( dst_stateful_blocks * 512 ))
local src_stateful_bytes=$(( src_stateful_blocks * 512 ))
local src_size=$(stat -c %s ${src_img})
local dst_size=$(( src_size - src_stateful_bytes + dst_stateful_bytes ))
truncate -s ${dst_size} ${dst_img}
# Set up a new partition table # Copy MBR, initialize GPT.
rm -f "${temp_img}" dd if="${src_img}" of="${dst_img}" conv=notrunc bs=512 count=1
PARTITION_SCRIPT_PATH=$( tempfile ) cgpt create ${dst_img}
FLAGS_adjust_part="STATE:=${stateful_bytes}"
write_partition_script "recovery" "${PARTITION_SCRIPT_PATH}"
. "${PARTITION_SCRIPT_PATH}"
write_partition_table "${temp_img}" "${temp_pmbr}"
echo "${PARTITION_SCRIPT_PATH}"
local kern_a_dst_offset=$(partoffset ${temp_img} 2) # Find partition number of STATE (really should always be "1")
local kern_a_src_offset=$(partoffset ${src_img} 2) local part=0
local kern_a_count=$(partsize ${src_img} 2) local label=""
while [ "${label}" != "STATE" ]; do
part=$(( part + 1 ))
local label=$(cgpt show -i ${part} -l ${src_img})
local src_start=$(cgpt show -i ${part} -b ${src_img})
if [ ${src_start} -eq 0 ]; then
echo "Could not find 'STATE' partition" >&2
return 1
fi
done
local src_state_start=$(cgpt show -i ${part} -b ${src_img})
local kern_b_dst_offset=$(partoffset ${temp_img} 4) # Duplicate each partition entry.
local kern_b_src_offset=$(partoffset ${src_img} 4) part=0
local kern_b_count=$(partsize ${src_img} 4) while :; do
part=$(( part + 1 ))
local rootfs_dst_offset=$(partoffset ${temp_img} 3) local src_start=$(cgpt show -i ${part} -b ${src_img})
local rootfs_src_offset=$(partoffset ${src_img} 3) if [ ${src_start} -eq 0 ]; then
local rootfs_count=$(partsize ${src_img} 3) # No more partitions to copy.
break
local oem_dst_offset=$(partoffset ${temp_img} 8) fi
local oem_src_offset=$(partoffset ${src_img} 8) local dst_start=${src_start}
local oem_count=$(partsize ${src_img} 8) # Load source partition details.
local size=$(cgpt show -i ${part} -s ${src_img})
local esp_dst_offset=$(partoffset ${temp_img} 12) local label=$(cgpt show -i ${part} -l ${src_img})
local esp_src_offset=$(partoffset ${src_img} 12) local attr=$(cgpt show -i ${part} -A ${src_img})
local esp_count=$(partsize ${src_img} 12) local tguid=$(cgpt show -i ${part} -t ${src_img})
local uguid=$(cgpt show -i ${part} -u ${src_img})
local state_dst_offset=$(partoffset ${temp_img} 1) # Change size of stateful.
if [ "${label}" = "STATE" ]; then
# Copy into the partition parts of the file size=${dst_stateful_blocks}
dd if="${src_img}" of="${temp_img}" conv=notrunc bs=512 \ fi
seek=${rootfs_dst_offset} skip=${rootfs_src_offset} count=${rootfs_count} # Partitions located after STATE need to have their start moved.
dd if="${temp_state}" of="${temp_img}" conv=notrunc bs=512 \ if [ ${src_start} -gt ${src_state_start} ]; then
seek=${state_dst_offset} dst_start=$(( dst_start + delta_blocks ))
# Copy the full kernel (i.e. with vboot sections) fi
dd if="${src_img}" of="${temp_img}" conv=notrunc bs=512 \ # Add this partition to the destination.
seek=${kern_a_dst_offset} skip=${kern_a_src_offset} count=${kern_a_count} cgpt add -i ${part} -b ${dst_start} -s ${size} -l "${label}" -A ${attr} \
dd if="${src_img}" of="${temp_img}" conv=notrunc bs=512 \ -t ${tguid} -u ${uguid} ${dst_img}
seek=${kern_b_dst_offset} skip=${kern_b_src_offset} count=${kern_b_count} if [ "${label}" != "STATE" ]; then
dd if="${src_img}" of="${temp_img}" conv=notrunc bs=512 \ # Copy source partition as-is.
seek=${oem_dst_offset} skip=${oem_src_offset} count=${oem_count} dd if="${src_img}" of="${dst_img}" conv=notrunc bs=512 \
dd if="${src_img}" of="${temp_img}" conv=notrunc bs=512 \ skip=${src_start} seek=${dst_start} count=${size}
seek=${esp_dst_offset} skip=${esp_src_offset} count=${esp_count} else
# Copy new stateful partition into place.
dd if="${src_state}" of="${dst_img}" conv=notrunc bs=512 \
seek=${dst_start}
fi
done
return 0
} }