Chromium Code Reviews| Index: src/platform/installer/chromeos-common.sh | 
| diff --git a/src/platform/installer/chromeos-common.sh b/src/platform/installer/chromeos-common.sh | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..c9776a0edfaed8306863cb2a84d79d072baf5e39 | 
| --- /dev/null | 
| +++ b/src/platform/installer/chromeos-common.sh | 
| @@ -0,0 +1,310 @@ | 
| +# Copyright (c) 2010 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. | 
| +# | 
| +# This contains common constants and functions for installer scripts. This must | 
| +# evaluate properly for both /bin/bash and /bin/sh, since it's used both to | 
| +# create the initial image at compile time and to install or upgrade a running | 
| +# image. | 
| + | 
| +# Here are the GUIDs we'll be using to identify various partitions. | 
| +STATEFUL_GUID='ebd0a0a2-b9e5-4433-87c0-68b6b72699c7' | 
| +KERN_GUID='fe3a2a5d-4f32-41a7-b725-accc3285a309' | 
| +ROOTFS_GUID='3cb8e202-3b7e-47dd-8a3c-7ff2a13cfcec' | 
| +ESP_GUID='28732ac1-1ff8-d211-ba4b-00a0c93ec93b' | 
| + | 
| + | 
| +# The GPT tables describe things in terms of 512-byte sectors, but some | 
| +# filesystems prefer 4096-byte blocks. These functions help with alignment | 
| +# issues. | 
| + | 
| +# This returns the size of a file or device in 512-byte sectors, rounded up if | 
| +# needed. | 
| +# Invoke as: subshell | 
| +# Args: FILENAME | 
| +# Return: whole number of sectors needed to fully contain FILENAME | 
| +numsectors() { | 
| + case $1 in | 
| + /dev/*[0-9]) | 
| + dnum=${1##*/} | 
| + dev=${dnum%%[0-9]*} | 
| + cat /sys/block/$dev/$dnum/size | 
| + ;; | 
| + /dev/*) | 
| + dev=${1##*/} | 
| + cat /sys/block/$dev/size | 
| + ;; | 
| + *) | 
| + local bytes=$(stat -c%s "$1") | 
| + local sectors=$(( $bytes / 512 )) | 
| + local rem=$(( $bytes % 512 )) | 
| + if [ $rem -ne 0 ]; then | 
| + sectors=$(( $sectors + 1 )) | 
| + fi | 
| + echo $sectors | 
| + ;; | 
| + esac | 
| +} | 
| + | 
| +# Round a number of 512-byte sectors up to an integral number of 4096-byte | 
| +# blocks. | 
| +# Invoke as: subshell | 
| +# Args: SECTORS | 
| +# Return: Next largest multiple-of-8 sectors (ex: 4->8, 33->40, 32->32) | 
| +roundup() { | 
| + local num=$1 | 
| + local rem=$(( $num % 8 )) | 
| + | 
| + if (( $rem )); then | 
| + num=$(($num + 8 - $rem)) | 
| + fi | 
| + echo $num | 
| +} | 
| + | 
| +# Truncate a number of 512-byte sectors down to an integral number of 4096-byte | 
| +# blocks. | 
| +# Invoke as: subshell | 
| +# Args: SECTORS | 
| +# Return: Next smallest multiple-of-8 sectors (ex: 4->0, 33->32, 32->32) | 
| +rounddown() { | 
| + local num=$1 | 
| + local rem=$(( $num % 8 )) | 
| + | 
| + if (( $rem )); then | 
| + num=$(($num - $rem)) | 
| + fi | 
| + echo $num | 
| +} | 
| + | 
| + | 
| +# We need to locate the gpt tool. It should already be installed in the build | 
| +# chroot, but some of these functions may be invoked outside the chroot (by | 
| +# image_to_usb or similar), so we need to find it. | 
| +GPT=$(which gpt 2>/dev/null) || /bin/true | 
| +if [ -z "$GPT" ]; then | 
| + if [ -x "${DEFAULT_CHROOT_DIR:-}/usr/bin/gpt" ]; then | 
| + GPT="${DEFAULT_CHROOT_DIR:-}/usr/bin/gpt" | 
| + else | 
| + echo "can't find gpt tool" 1>&2 | 
| + exit 1 | 
| + fi | 
| +fi | 
| + | 
| + | 
| +# This installs a GPT into the specified device or file, using the given | 
| +# components. If the target is a block device we'll do a full install. | 
| +# Otherwise, it'll be just enough to boot. | 
| +# Invoke as: command (not subshell) | 
| +# Args: TARGET ROOTFS_IMG KERNEL_IMG STATEFUL_IMG PMBRCODE | 
| +# Return: nothing | 
| +# Side effects: Sets these global variables describing the GPT partitions | 
| +# (all untis are 512-byte sectors): | 
| +# NUM_KERN_SECTORS | 
| +# NUM_ROOTFS_SECTORS | 
| +# NUM_STATEFUL_SECTORS | 
| +# NUM_RESERVED_SECTORS | 
| +# START_KERN_A | 
| +# START_STATEFUL | 
| +# START_ROOTFS_A | 
| +# START_KERN_B | 
| +# START_ROOTFS_B | 
| +# START_RESERVED | 
| +install_gpt() { | 
| + local outdev=$1 | 
| + local rootfs_img=$2 | 
| + local kernel_img=$3 | 
| + local stateful_img=$4 | 
| + local pmbrcode=$5 | 
| + | 
| + # The gpt tool requires a fixed-size target to work on, so we may have to | 
| + # create a file of the appropriate size. Let's figure out what that size is | 
| + # now. The partition layout is gonna look something like this: | 
| + # | 
| + # PMBR (512 bytes) | 
| + # Primary GPT Header (512 bytes) | 
| + # Primary GPT Table (16K) | 
| + # Kernel A partition 2 | 
| + # Kernel B partition 4 | 
| + # reserved space for additional partitions partition 6,7,8,... | 
| + # Stateful partition (as large as possible) partition 1 | 
| + # Rootfs B partition 5 | 
| + # Rootfs A partition 3 | 
| + # Secondary GPT Table (16K) | 
| + # Secondary GPT Header (512 bytes) | 
| + # | 
| + # Please refer to the official ChromeOS documentation for the details and | 
| + # explanation behind the layout and partition numbering scheme. The short | 
| + # version is that 1) we want everything above partition 3 to be optional when | 
| + # we're creating a bootable USB key, 2) we want to be able to add new | 
| + # partitions later without breaking current scripts, and 3) we may need to | 
| + # increase the size of the rootfs during an upgrade, which means shrinking | 
| + # the size of the stateful partition. | 
| + # | 
| + # One nonobvious contraint is that the ext2-based filesystems typically use | 
| + # 4096-byte blocks. We'll need a little padding at each end of the disk to | 
| + # align the useable space to that size boundary. | 
| + | 
| + # Here are the size limits that we're currently requiring | 
| + local max_kern_sectors=32768 # 16M | 
| + local max_rootfs_sectors=2097152 # 1G | 
| + local max_reserved_sectors=131072 # 64M | 
| + local min_stateful_sectors=262144 # 128M, expands to fill available space | 
| + | 
| + local num_pmbr_sectors=1 | 
| + local num_gpt_hdr_sectors=1 | 
| + local num_gpt_table_sectors=32 # 16K | 
| + local num_footer_sectors=$(($num_gpt_hdr_sectors + $num_gpt_table_sectors)) | 
| + local num_header_sectors=$(($num_pmbr_sectors + $num_footer_sectors)) | 
| + | 
| + local start_useful=$(roundup $num_header_sectors) | 
| + | 
| + # What are we doing? | 
| + if [ -b $outdev ]; then | 
| + # Block device, need to be root. | 
| + local sudo=sudo | 
| + | 
| + # Full install, use max sizes and create both A & B images. | 
| + NUM_KERN_SECTORS=$max_kern_sectors | 
| + NUM_ROOTFS_SECTORS=$max_rootfs_sectors | 
| + NUM_RESERVED_SECTORS=$max_reserved_sectors | 
| + | 
| + # Where do things go? | 
| + START_KERN_A=$start_useful | 
| + START_KERN_B=$(($START_KERN_A + $NUM_KERN_SECTORS)) | 
| + START_RESERVED=$(($START_KERN_B + $NUM_KERN_SECTORS)) | 
| + START_STATEFUL=$(($START_RESERVED + $NUM_RESERVED_SECTORS)) | 
| + | 
| + local total_sectors=$(numsectors $outdev) | 
| + local start_gpt_footer=$(($total_sectors - $num_footer_sectors)) | 
| + local end_useful=$(rounddown $start_gpt_footer) | 
| + | 
| + START_ROOTFS_A=$(($end_useful - $NUM_ROOTFS_SECTORS)) | 
| + START_ROOTFS_B=$(($START_ROOTFS_A - $NUM_ROOTFS_SECTORS)) | 
| + | 
| + NUM_STATEFUL_SECTORS=$(($START_ROOTFS_B - $START_STATEFUL)) | 
| + else | 
| + # Just a local file. | 
| + local sudo= | 
| + | 
| + # We'll only populate partitions 1, 2, 3. Image B isn't required for this, | 
| + # and the others are still theoretical. | 
| + NUM_KERN_SECTORS=$(roundup $(numsectors $kernel_img)) | 
| + NUM_ROOTFS_SECTORS=$(roundup $(numsectors $rootfs_img)) | 
| + NUM_STATEFUL_SECTORS=$(roundup $(numsectors $stateful_img)) | 
| + NUM_RESERVED_SECTORS=0 | 
| + | 
| + START_KERN_A=$start_useful | 
| + START_STATEFUL=$(($START_KERN_A + $NUM_KERN_SECTORS)) | 
| + START_ROOTFS_A=$(($START_STATEFUL + $NUM_STATEFUL_SECTORS)) | 
| + START_KERN_B="" | 
| + START_ROOTFS_B="" | 
| + START_RESERVED="" | 
| + | 
| + # For minimal install, we're not worried about the secondary GPT header | 
| + # being at the end of the device because we're almost always writing to a | 
| + # file. If that's not true, the secondary will just be invalid. | 
| + local start_gpt_footer=$(($START_ROOTFS_A + $NUM_ROOTFS_SECTORS)) | 
| + local end_useful=$start_gpt_footer | 
| + | 
| + local total_sectors=$(($start_gpt_footer + $num_footer_sectors)) | 
| + | 
| + # Create the image file if it doesn't exist. | 
| + if [ ! -e ${outdev} ]; then | 
| + dd if=/dev/zero of=${outdev} bs=512 count=1 seek=$(($total_sectors - 1)) | 
| + fi | 
| + fi | 
| + | 
| + echo "Creating partition tables..." | 
| + | 
| + # Zap any old partitions (otherwise gpt complains). | 
| + $sudo dd if=/dev/zero of=${outdev} conv=notrunc bs=512 \ | 
| + count=$num_header_sectors | 
| + $sudo dd if=/dev/zero of=${outdev} conv=notrunc bs=512 \ | 
| + seek=${start_gpt_footer} count=$num_footer_sectors | 
| + | 
| + # Create the new GPT partitions. The order determines the partition number. | 
| + # Note that the partition label is in the GPT only. The filesystem label is | 
| + # what's used to populate /dev/disk/by-label/, and this is not that. | 
| + $sudo $GPT create ${outdev} | 
| + | 
| + $sudo $GPT add -b ${START_STATEFUL} -s ${NUM_STATEFUL_SECTORS} \ | 
| + -t ${STATEFUL_GUID} ${outdev} | 
| + $sudo $GPT label -i 1 -l "STATE" ${outdev} | 
| + | 
| + $sudo $GPT add -b ${START_KERN_A} -s ${NUM_KERN_SECTORS} \ | 
| + -t ${KERN_GUID} ${outdev} | 
| + $sudo $GPT label -i 2 -l "KERN-A" ${outdev} | 
| + | 
| + $sudo $GPT add -b ${START_ROOTFS_A} -s ${NUM_ROOTFS_SECTORS} \ | 
| + -t ${ROOTFS_GUID} ${outdev} | 
| + $sudo $GPT label -i 3 -l "ROOT-A" ${outdev} | 
| + | 
| + # add the rest of the partitions for a full install | 
| + if [ -n "$START_KERN_B" ]; then | 
| + $sudo $GPT add -b ${START_KERN_B} -s ${NUM_KERN_SECTORS} \ | 
| + -t ${KERN_GUID} ${outdev} | 
| + $sudo $GPT label -i 4 -l "KERN-B" ${outdev} | 
| + | 
| + $sudo $GPT add -b ${START_ROOTFS_B} -s ${NUM_ROOTFS_SECTORS} \ | 
| + -t ${ROOTFS_GUID} ${outdev} | 
| + $sudo $GPT label -i 5 -l "ROOT-B" ${outdev} | 
| + fi | 
| + | 
| + # Create the PMBR and instruct it to boot ROOT-A | 
| + $sudo $GPT boot -i 3 -b ${pmbrcode} ${outdev} | 
| + | 
| + # Display what we've got | 
| + $sudo $GPT -r show -l ${outdev} | 
| + | 
| + sync | 
| +} | 
| + | 
| + | 
| +# Helper function, please ignore and look below. | 
| +_partinfo() { | 
| + local device=$1 | 
| + local partnum=$2 | 
| + local start size part x n | 
| + sudo $GPT -r -S show $device \ | 
| + | grep 'GPT part -' \ | 
| + | while read start size part x x x n x; do \ | 
| + if [ "${part}" -eq "${partnum}" ]; then \ | 
| + echo $start $size; \ | 
| + fi; \ | 
| + done | 
| + # The 'while' is a subshell, so there's no way to indicate success. | 
| +} | 
| + | 
| +# Read GPT table to find information about a specific partition. | 
| +# Invoke as: subshell | 
| +# Args: DEVICE PARTNUM | 
| +# Returns: offset and size (in sectors) of partition PARTNUM | 
| +partinfo() { | 
| + # get string | 
| + local X=$(_partinfo $1 $2) | 
| + # detect success or failure here | 
| + [ -n "$X" ] && echo $X | 
| +} | 
| + | 
| +# Read GPT table to find the starting location of a specific partition. | 
| +# Invoke as: subshell | 
| +# Args: DEVICE PARTNUM | 
| +# Returns: offset (in sectors) of partition PARTNUM | 
| +partoffset() { | 
| + # get string | 
| + local X=$(_partinfo $1 $2) | 
| + # detect success or failure here | 
| + [ -n "$X" ] && echo ${X% *} | 
| 
 
adlr
2010/03/30 19:50:20
same here?
 
 | 
| +} | 
| + | 
| +# Read GPT table to find the size of a specific partition. | 
| +# Invoke as: subshell | 
| +# Args: DEVICE PARTNUM | 
| +# Returns: size (in sectors) of partition PARTNUM | 
| +partsize() { | 
| + # get string | 
| + local X=$(_partinfo $1 $2) | 
| + # detect success or failure here | 
| + [ -n "$X" ] && echo ${X#* } | 
| 
 
adlr
2010/03/30 19:50:20
same here?
 
 | 
| +} | 
| + |