Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 # | |
| 5 # This contains common constants and functions for installer scripts. This must | |
| 6 # evaluate properly for both /bin/bash and /bin/sh, since it's used both to | |
| 7 # create the initial image at compile time and to install or upgrade a running | |
| 8 # image. | |
| 9 | |
| 10 # Here are the GUIDs we'll be using to identify various partitions. | |
| 11 STATEFUL_GUID='ebd0a0a2-b9e5-4433-87c0-68b6b72699c7' | |
| 12 KERN_GUID='fe3a2a5d-4f32-41a7-b725-accc3285a309' | |
| 13 ROOTFS_GUID='3cb8e202-3b7e-47dd-8a3c-7ff2a13cfcec' | |
| 14 ESP_GUID='28732ac1-1ff8-d211-ba4b-00a0c93ec93b' | |
| 15 | |
| 16 | |
| 17 # The GPT tables describe things in terms of 512-byte sectors, but some | |
| 18 # filesystems prefer 4096-byte blocks. These functions help with alignment | |
| 19 # issues. | |
| 20 | |
| 21 # This returns the size of a file or device in 512-byte sectors, rounded up if | |
| 22 # needed. | |
| 23 # Invoke as: subshell | |
| 24 # Args: FILENAME | |
| 25 # Return: whole number of sectors needed to fully contain FILENAME | |
| 26 numsectors() { | |
| 27 case $1 in | |
| 28 /dev/*[0-9]) | |
| 29 dnum=${1##*/} | |
| 30 dev=${dnum%%[0-9]*} | |
| 31 cat /sys/block/$dev/$dnum/size | |
| 32 ;; | |
| 33 /dev/*) | |
| 34 dev=${1##*/} | |
| 35 cat /sys/block/$dev/size | |
| 36 ;; | |
| 37 *) | |
| 38 local bytes=$(stat -c%s "$1") | |
| 39 local sectors=$(( $bytes / 512 )) | |
| 40 local rem=$(( $bytes % 512 )) | |
| 41 if [ $rem -ne 0 ]; then | |
| 42 sectors=$(( $sectors + 1 )) | |
| 43 fi | |
| 44 echo $sectors | |
| 45 ;; | |
| 46 esac | |
| 47 } | |
| 48 | |
| 49 # Round a number of 512-byte sectors up to an integral number of 4096-byte | |
| 50 # blocks. | |
| 51 # Invoke as: subshell | |
| 52 # Args: SECTORS | |
| 53 # Return: Next largest multiple-of-8 sectors (ex: 4->8, 33->40, 32->32) | |
| 54 roundup() { | |
| 55 local num=$1 | |
| 56 local rem=$(( $num % 8 )) | |
| 57 | |
| 58 if (( $rem )); then | |
| 59 num=$(($num + 8 - $rem)) | |
| 60 fi | |
| 61 echo $num | |
| 62 } | |
| 63 | |
| 64 # Truncate a number of 512-byte sectors down to an integral number of 4096-byte | |
| 65 # blocks. | |
| 66 # Invoke as: subshell | |
| 67 # Args: SECTORS | |
| 68 # Return: Next smallest multiple-of-8 sectors (ex: 4->0, 33->32, 32->32) | |
| 69 rounddown() { | |
| 70 local num=$1 | |
| 71 local rem=$(( $num % 8 )) | |
| 72 | |
| 73 if (( $rem )); then | |
| 74 num=$(($num - $rem)) | |
| 75 fi | |
| 76 echo $num | |
| 77 } | |
| 78 | |
| 79 | |
| 80 # We need to locate the gpt tool. It should already be installed in the build | |
| 81 # chroot, but some of these functions may be invoked outside the chroot (by | |
| 82 # image_to_usb or similar), so we need to find it. | |
| 83 GPT=$(which gpt 2>/dev/null) || /bin/true | |
| 84 if [ -z "$GPT" ]; then | |
| 85 if [ -x "${DEFAULT_CHROOT_DIR:-}/usr/bin/gpt" ]; then | |
| 86 GPT="${DEFAULT_CHROOT_DIR:-}/usr/bin/gpt" | |
| 87 else | |
| 88 echo "can't find gpt tool" 1>&2 | |
| 89 exit 1 | |
| 90 fi | |
| 91 fi | |
| 92 | |
| 93 | |
| 94 # This installs a GPT into the specified device or file, using the given | |
| 95 # components. If the target is a block device we'll do a full install. | |
| 96 # Otherwise, it'll be just enough to boot. | |
| 97 # Invoke as: command (not subshell) | |
| 98 # Args: TARGET ROOTFS_IMG KERNEL_IMG STATEFUL_IMG PMBRCODE | |
| 99 # Return: nothing | |
| 100 # Side effects: Sets these global variables describing the GPT partitions | |
| 101 # (all untis are 512-byte sectors): | |
| 102 # NUM_KERN_SECTORS | |
| 103 # NUM_ROOTFS_SECTORS | |
| 104 # NUM_STATEFUL_SECTORS | |
| 105 # NUM_RESERVED_SECTORS | |
| 106 # START_KERN_A | |
| 107 # START_STATEFUL | |
| 108 # START_ROOTFS_A | |
| 109 # START_KERN_B | |
| 110 # START_ROOTFS_B | |
| 111 # START_RESERVED | |
| 112 install_gpt() { | |
| 113 local outdev=$1 | |
| 114 local rootfs_img=$2 | |
| 115 local kernel_img=$3 | |
| 116 local stateful_img=$4 | |
| 117 local pmbrcode=$5 | |
| 118 | |
| 119 # The gpt tool requires a fixed-size target to work on, so we may have to | |
| 120 # create a file of the appropriate size. Let's figure out what that size is | |
| 121 # now. The partition layout is gonna look something like this: | |
| 122 # | |
| 123 # PMBR (512 bytes) | |
| 124 # Primary GPT Header (512 bytes) | |
| 125 # Primary GPT Table (16K) | |
| 126 # Kernel A partition 2 | |
| 127 # Kernel B partition 4 | |
| 128 # reserved space for additional partitions partition 6,7,8,... | |
| 129 # Stateful partition (as large as possible) partition 1 | |
| 130 # Rootfs B partition 5 | |
| 131 # Rootfs A partition 3 | |
| 132 # Secondary GPT Table (16K) | |
| 133 # Secondary GPT Header (512 bytes) | |
| 134 # | |
| 135 # Please refer to the official ChromeOS documentation for the details and | |
| 136 # explanation behind the layout and partition numbering scheme. The short | |
| 137 # version is that 1) we want everything above partition 3 to be optional when | |
| 138 # we're creating a bootable USB key, 2) we want to be able to add new | |
| 139 # partitions later without breaking current scripts, and 3) we may need to | |
| 140 # increase the size of the rootfs during an upgrade, which means shrinking | |
| 141 # the size of the stateful partition. | |
| 142 # | |
| 143 # One nonobvious contraint is that the ext2-based filesystems typically use | |
| 144 # 4096-byte blocks. We'll need a little padding at each end of the disk to | |
| 145 # align the useable space to that size boundary. | |
| 146 | |
| 147 # Here are the size limits that we're currently requiring | |
| 148 local max_kern_sectors=32768 # 16M | |
| 149 local max_rootfs_sectors=2097152 # 1G | |
| 150 local max_reserved_sectors=131072 # 64M | |
| 151 local min_stateful_sectors=262144 # 128M, expands to fill available space | |
| 152 | |
| 153 local num_pmbr_sectors=1 | |
| 154 local num_gpt_hdr_sectors=1 | |
| 155 local num_gpt_table_sectors=32 # 16K | |
| 156 local num_footer_sectors=$(($num_gpt_hdr_sectors + $num_gpt_table_sectors)) | |
| 157 local num_header_sectors=$(($num_pmbr_sectors + $num_footer_sectors)) | |
| 158 | |
| 159 local start_useful=$(roundup $num_header_sectors) | |
| 160 | |
| 161 # What are we doing? | |
| 162 if [ -b $outdev ]; then | |
| 163 # Block device, need to be root. | |
| 164 local sudo=sudo | |
| 165 | |
| 166 # Full install, use max sizes and create both A & B images. | |
| 167 NUM_KERN_SECTORS=$max_kern_sectors | |
| 168 NUM_ROOTFS_SECTORS=$max_rootfs_sectors | |
| 169 NUM_RESERVED_SECTORS=$max_reserved_sectors | |
| 170 | |
| 171 # Where do things go? | |
| 172 START_KERN_A=$start_useful | |
| 173 START_KERN_B=$(($START_KERN_A + $NUM_KERN_SECTORS)) | |
| 174 START_RESERVED=$(($START_KERN_B + $NUM_KERN_SECTORS)) | |
| 175 START_STATEFUL=$(($START_RESERVED + $NUM_RESERVED_SECTORS)) | |
| 176 | |
| 177 local total_sectors=$(numsectors $outdev) | |
| 178 local start_gpt_footer=$(($total_sectors - $num_footer_sectors)) | |
| 179 local end_useful=$(rounddown $start_gpt_footer) | |
| 180 | |
| 181 START_ROOTFS_A=$(($end_useful - $NUM_ROOTFS_SECTORS)) | |
| 182 START_ROOTFS_B=$(($START_ROOTFS_A - $NUM_ROOTFS_SECTORS)) | |
| 183 | |
| 184 NUM_STATEFUL_SECTORS=$(($START_ROOTFS_B - $START_STATEFUL)) | |
| 185 else | |
| 186 # Just a local file. | |
| 187 local sudo= | |
| 188 | |
| 189 # We'll only populate partitions 1, 2, 3. Image B isn't required for this, | |
| 190 # and the others are still theoretical. | |
| 191 NUM_KERN_SECTORS=$(roundup $(numsectors $kernel_img)) | |
| 192 NUM_ROOTFS_SECTORS=$(roundup $(numsectors $rootfs_img)) | |
| 193 NUM_STATEFUL_SECTORS=$(roundup $(numsectors $stateful_img)) | |
| 194 NUM_RESERVED_SECTORS=0 | |
| 195 | |
| 196 START_KERN_A=$start_useful | |
| 197 START_STATEFUL=$(($START_KERN_A + $NUM_KERN_SECTORS)) | |
| 198 START_ROOTFS_A=$(($START_STATEFUL + $NUM_STATEFUL_SECTORS)) | |
| 199 START_KERN_B="" | |
| 200 START_ROOTFS_B="" | |
| 201 START_RESERVED="" | |
| 202 | |
| 203 # For minimal install, we're not worried about the secondary GPT header | |
| 204 # being at the end of the device because we're almost always writing to a | |
| 205 # file. If that's not true, the secondary will just be invalid. | |
| 206 local start_gpt_footer=$(($START_ROOTFS_A + $NUM_ROOTFS_SECTORS)) | |
| 207 local end_useful=$start_gpt_footer | |
| 208 | |
| 209 local total_sectors=$(($start_gpt_footer + $num_footer_sectors)) | |
| 210 | |
| 211 # Create the image file if it doesn't exist. | |
| 212 if [ ! -e ${outdev} ]; then | |
| 213 dd if=/dev/zero of=${outdev} bs=512 count=1 seek=$(($total_sectors - 1)) | |
| 214 fi | |
| 215 fi | |
| 216 | |
| 217 echo "Creating partition tables..." | |
| 218 | |
| 219 # Zap any old partitions (otherwise gpt complains). | |
| 220 $sudo dd if=/dev/zero of=${outdev} conv=notrunc bs=512 \ | |
| 221 count=$num_header_sectors | |
| 222 $sudo dd if=/dev/zero of=${outdev} conv=notrunc bs=512 \ | |
| 223 seek=${start_gpt_footer} count=$num_footer_sectors | |
| 224 | |
| 225 # Create the new GPT partitions. The order determines the partition number. | |
| 226 # Note that the partition label is in the GPT only. The filesystem label is | |
| 227 # what's used to populate /dev/disk/by-label/, and this is not that. | |
| 228 $sudo $GPT create ${outdev} | |
| 229 | |
| 230 $sudo $GPT add -b ${START_STATEFUL} -s ${NUM_STATEFUL_SECTORS} \ | |
| 231 -t ${STATEFUL_GUID} ${outdev} | |
| 232 $sudo $GPT label -i 1 -l "STATE" ${outdev} | |
| 233 | |
| 234 $sudo $GPT add -b ${START_KERN_A} -s ${NUM_KERN_SECTORS} \ | |
| 235 -t ${KERN_GUID} ${outdev} | |
| 236 $sudo $GPT label -i 2 -l "KERN-A" ${outdev} | |
| 237 | |
| 238 $sudo $GPT add -b ${START_ROOTFS_A} -s ${NUM_ROOTFS_SECTORS} \ | |
| 239 -t ${ROOTFS_GUID} ${outdev} | |
| 240 $sudo $GPT label -i 3 -l "ROOT-A" ${outdev} | |
| 241 | |
| 242 # add the rest of the partitions for a full install | |
| 243 if [ -n "$START_KERN_B" ]; then | |
| 244 $sudo $GPT add -b ${START_KERN_B} -s ${NUM_KERN_SECTORS} \ | |
| 245 -t ${KERN_GUID} ${outdev} | |
| 246 $sudo $GPT label -i 4 -l "KERN-B" ${outdev} | |
| 247 | |
| 248 $sudo $GPT add -b ${START_ROOTFS_B} -s ${NUM_ROOTFS_SECTORS} \ | |
| 249 -t ${ROOTFS_GUID} ${outdev} | |
| 250 $sudo $GPT label -i 5 -l "ROOT-B" ${outdev} | |
| 251 fi | |
| 252 | |
| 253 # Create the PMBR and instruct it to boot ROOT-A | |
| 254 $sudo $GPT boot -i 3 -b ${pmbrcode} ${outdev} | |
| 255 | |
| 256 # Display what we've got | |
| 257 $sudo $GPT -r show -l ${outdev} | |
| 258 | |
| 259 sync | |
| 260 } | |
| 261 | |
| 262 | |
| 263 # Helper function, please ignore and look below. | |
| 264 _partinfo() { | |
| 265 local device=$1 | |
| 266 local partnum=$2 | |
| 267 local start size part x n | |
| 268 sudo $GPT -r -S show $device \ | |
| 269 | grep 'GPT part -' \ | |
| 270 | while read start size part x x x n x; do \ | |
| 271 if [ "${part}" -eq "${partnum}" ]; then \ | |
| 272 echo $start $size; \ | |
| 273 fi; \ | |
| 274 done | |
| 275 # The 'while' is a subshell, so there's no way to indicate success. | |
| 276 } | |
| 277 | |
| 278 # Read GPT table to find information about a specific partition. | |
| 279 # Invoke as: subshell | |
| 280 # Args: DEVICE PARTNUM | |
| 281 # Returns: offset and size (in sectors) of partition PARTNUM | |
| 282 partinfo() { | |
| 283 # get string | |
| 284 local X=$(_partinfo $1 $2) | |
| 285 # detect success or failure here | |
| 286 [ -n "$X" ] && echo $X | |
| 287 } | |
| 288 | |
| 289 # Read GPT table to find the starting location of a specific partition. | |
| 290 # Invoke as: subshell | |
| 291 # Args: DEVICE PARTNUM | |
| 292 # Returns: offset (in sectors) of partition PARTNUM | |
| 293 partoffset() { | |
| 294 # get string | |
| 295 local X=$(_partinfo $1 $2) | |
| 296 # detect success or failure here | |
| 297 [ -n "$X" ] && echo ${X% *} | |
|
adlr
2010/03/30 19:50:20
same here?
| |
| 298 } | |
| 299 | |
| 300 # Read GPT table to find the size of a specific partition. | |
| 301 # Invoke as: subshell | |
| 302 # Args: DEVICE PARTNUM | |
| 303 # Returns: size (in sectors) of partition PARTNUM | |
| 304 partsize() { | |
| 305 # get string | |
| 306 local X=$(_partinfo $1 $2) | |
| 307 # detect success or failure here | |
| 308 [ -n "$X" ] && echo ${X#* } | |
|
adlr
2010/03/30 19:50:20
same here?
| |
| 309 } | |
| 310 | |
| OLD | NEW |