Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(102)

Side by Side Diff: src/platform/installer/chromeos-install

Issue 1100001: Switch to GPT-format disk images. (Closed)
Patch Set: Final GPT-enabling changeset. I hope. Created 10 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 #!/bin/sh 1 #!/bin/sh -eu
2
3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 4 # found in the LICENSE file.
6 5 #
7 # A script to install from removable media to hard disk. 6 # A script to install from removable media to hard disk.
8 7
9 # This is used to force partitions to be larger than they may be 8 # Load functions and constants for chromeos-install.
10 # On the source install media 9 . "$(dirname "$0")/chromeos-common.sh"
11 INSTALL_ROOT_PART_SIZE=$((1024 * 1024)) # 1024 byte blocks, thus 1 gig
12 10
13 if [ "$USER_ID" -ne 1000 ] 11
12 # You can't be root when you start this, because we want to force you to give
13 # the root password as confirmation that you're allowed to do it. We don't care
14 # what other account you're using; you'll still need to sudo before this works.
15 if [ $(id -u) == "0" ]
14 then 16 then
15 echo "" 17 echo ""
16 echo "Note: You must be the 'chronos' user to run this script." 18 echo "Note: You must be the 'chronos' user to run this script."
17 echo "" 19 echo ""
18 echo "Usage: $0 [destination_device] [skip_source_removable_check]" 20 echo "Usage: $0 [destination_device] [skip_source_removable_check]"
19 echo "" 21 echo ""
20 echo "This will install the usb image to your machine's hard disk." 22 echo "This will install the usb image to your machine's hard disk."
21 echo "By default, it will attempt to install to '/dev/sda'." 23 echo "By default, it will attempt to install to '/dev/sda'."
22 echo "First 'su chronos' and then run the script. It will ask" 24 echo "First 'su chronos' and then run the script. It will ask"
23 echo "for the root password before messing with your hard disk." 25 echo "for the root password before messing with your hard disk."
24 echo "Skips checking whether the source device is removable if second" 26 echo "Skips checking whether the source device is removable if second"
25 echo "argument is 'true'." 27 echo "argument is 'true'."
26 exit 1 28 exit 1
27 fi 29 fi
28 30
29 DST=/dev/sda 31 # Default destination is /dev/sda.
30 if [ -n "$1" ] 32 DST=${1:-/dev/sda}
31 then
32 DST="$1"
33 fi
34 33
35 SKIP_CHECK_SOURCE_REMOVABLE="false" 34 # Check for a removeable source.
36 if [ "$2" = "true" ] 35 SKIP_CHECK_SOURCE_REMOVABLE=${2:-false}
37 then
38 SKIP_CHECK_SOURCE_REMOVABLE="true"
39 fi
40 36
41 # First find the root device that we are installing from and verify it. 37 # First find the root partition that we are installing from and verify it.
42 CMDLINE=`cat /proc/cmdline` 38 ROOTDEV=$(rootdev)
43 SRC_PARTITION=`echo "$CMDLINE" | sed \ 39 # Remove numbers at end of rootfs device.
44 's/.*root=\([-/=\.[:alpha:][:digit:]]*\).*/\1/g'` 40 SRC=${ROOTDEV%%[0-9]*}
45 if [ ! -b "$SRC_PARTITION" ] 41 REMOVABLE=$(cat /sys/block/${SRC#/dev/}/removable)
46 then
47 # Try looking up by label
48 LABEL=`echo "$CMDLINE" | sed \
49 's/.*root=LABEL=\([-\.[:alpha:][:digit:]]*\).*/\1/g'`
50 if [ "$LABEL" = "$CMDLINE" ]
51 then
52 echo "Error: Unable to find root."
53 exit 1
54 fi
55 echo "Using source USB device with label: $LABEL"
56 SRC_PARTITION=`readlink -f /dev/disk/by-label/$LABEL`
57 fi
58 if [ ! -b "$SRC_PARTITION" ]
59 then
60 echo "Error: Unable to find USB partition: $SRC_PARTITION"
61 exit 1
62 fi
63 SRC=`echo "$SRC_PARTITION" | sed 's/\(\/dev\/[[:alpha:]]*\)[[:digit:]]*/\1/g'`
64 if [ ! -b "$SRC" ]
65 then
66 echo "Error: Unable to find USB device: $SRC"
67 exit 1
68 fi
69 SRC_DEV=${SRC#/dev/}
70 REMOVABLE=`cat /sys/block/$SRC_DEV/removable`
71 if [ "$SKIP_CHECK_SOURCE_REMOVABLE" = "false" -a "$REMOVABLE" != "1" ] 42 if [ "$SKIP_CHECK_SOURCE_REMOVABLE" = "false" -a "$REMOVABLE" != "1" ]
72 then 43 then
73 echo "Error: Source does not look like a removable device: $SRC_DEV" 44 echo "Error: Source does not look like a removable device: $SRC"
74 exit 1 45 exit 1
75 fi 46 fi
76 47
77 # Check out the dst device. 48 # Check out the dst device.
78 if [ ! -b "$DST" ] 49 if [ ! -b "$DST" ]
79 then 50 then
80 echo "Error: Unable to find destination device: $DST" 51 echo "Error: Unable to find destination block device: $DST"
81 exit 1 52 exit 1
82 fi 53 fi
83 DST_DEV=${DST#/dev/} 54 REMOVABLE=$(cat /sys/block/${DST#/dev/}/removable)
84 REMOVABLE=`cat /sys/block/$DST_DEV/removable`
85 if [ $? -ne 0 ] 55 if [ $? -ne 0 ]
86 then 56 then
87 echo "Error: Invalid destiation device (must be whole device): $DST" 57 echo "Error: Invalid destination device (must be whole device): $DST"
88 exit 1 58 exit 1
89 fi 59 fi
90 if [ "$REMOVABLE" != "0" ] 60 if [ "$REMOVABLE" != "0" ]
91 then 61 then
92 echo "Error: Attempt to install to a removeable device: $DST" 62 echo "Error: Attempt to install to a removeable device: $DST"
93 exit 1 63 exit 1
94 fi 64 fi
95 if [ "$DST" = "$SRC" ]; then 65 if [ "$DST" = "$SRC" ]; then
96 echo "Error: src and dst the same: $SRC = $DST" 66 echo "Error: src and dst the same: $SRC = $DST"
97 exit 1 67 exit 1
98 fi 68 fi
69
99 # Ask for root password to be sure. 70 # Ask for root password to be sure.
100 echo "This will install from '$SRC' to '$DST'. If you are sure this is " 71 echo "This will install from '$SRC' to '$DST'. If you are sure this is"
101 echo "what you want then feel free to enter the root password to proceed." 72 echo "what you want then feel free to enter the root password to proceed."
102 sudo -K # Force them to enter a password 73 sudo -K
103 74
104 # Verify sizes. Since we use sfdisk, this needs to be done after we start 75 echo "This will erase all data at this destination: $DST"
105 # asking for the root password. 76 read -p "Are you sure (y/N)? " SURE
106 SIZE=`sudo sfdisk -l "$SRC" 2>/dev/null | grep "$SRC_PARTITION" | grep '*' | awk '{ print $6 }'` 77 if [ "$SURE" != "y" ] ; then
107 if [ $INSTALL_ROOT_PART_SIZE -lt $SIZE ]; then 78 echo "Ok, better safe than sorry; you answered '$SURE'."
108 echo "Install partition size too small: $INSTALL_ROOT_PART_SIZE vs $SIZE"
109 echo "(1024 byte blocks)"
110 exit 1
111 fi
112 SIZE=$INSTALL_ROOT_PART_SIZE
113 SIZE=$((SIZE * 1024))
114 if [ $SIZE -eq 0 ]
115 then
116 echo "Error: Unable to get system partition size."
117 exit 1
118 fi
119 SIZE_NEEDED=$((SIZE * 4)) # Assume 2 system images, swap, and user space.
120 DST_SIZE=`cat /sys/block/$DST_DEV/size`
121 DST_SIZE=$((DST_SIZE * 512))
122 if [ $DST_SIZE -lt $SIZE_NEEDED ]
123 then
124 echo "Error: Destination device is too small ($DST_SIZE vs $SIZE_NEEDED)"
125 exit 1 79 exit 1
126 fi 80 fi
127 81
128 # Location to temporarily mount the new system image to fix things up. 82 ##############################################################################
129 ROOTFS_DIR="/tmp/chromeos_hd_install" 83 # Helpful constants and functions.
130 84
131 # From now on we die on error. We set up a failure handler and continue. 85 PMBRCODE=/tmp/gptmbr.bin
132 error_handler() { 86 TMPFILE=/tmp/install-temp-file
133 ! sudo umount "$ROOTFS_DIR" 87 TMPMNT=/tmp/install-mount-point
88 mkdir -p ${TMPMNT}
134 89
135 echo "Sorry, an error occurred after we messed with the hard disk." 90 # Create a loop device on the given file at a specified (sector) offset.
136 echo "The chromeos image was NOT installed successfully and there is " 91 # Remember the loop device using the global variable LOOP_DEV.
137 echo "a chance that you will not be able to boot off the netbook hard disk." 92 # Invoke as: command
93 # Args: FILE OFFSET
94 loop_offset_setup() {
95 local filename=$1
96 local offset=$2
97
98 LOOP_DEV=$(sudo losetup -f)
99 if [ -z "$LOOP_DEV" ]
100 then
101 echo "No free loop device. Free up a loop device or reboot. Exiting."
102 exit 1
103 fi
104
105 sudo losetup -o $(($offset * 512)) ${LOOP_DEV} ${filename}
138 } 106 }
139 set -e
140 trap error_handler EXIT
141 107
142 # Set up the partition table. This is different from the USB partition table 108 # Delete the current loop device.
143 # because the user paritition expands to fill the space on the disk. 109 loop_offset_cleanup() {
144 # NOTE: We currently create an EFI partition rather than swap since the 110 # losetup -a doesn't always show every active device, so we'll always try to
145 # eeepc can take advantage of this to speed POST time. 111 # delete what we think is the active one without checking first. Report
146 sudo dd if="$SRC" of="$DST" bs=512 count=1 112 # success no matter what.
113 sudo losetup -d ${LOOP_DEV} || /bin/true
114 }
147 115
148 PARTITION_NUM_SECTORS=$((SIZE / 512)) 116 # Mount the existing loop device at the mountpoint in $TMPMNT.
149 # size of the device in 512 bytes sectors: 117 # Args: none
150 DEVICE_NUM_SECTORS=$(cat /sys/block/${DST#/dev/}/size) 118 mount_on_loop_dev() {
119 sudo mount ${LOOP_DEV} ${TMPMNT}
120 }
151 121
152 sudo sfdisk --force -uS "$DST" <<EOF 122 # Unmount loop-mounted device.
153 ,$(($DEVICE_NUM_SECTORS - (3 * $PARTITION_NUM_SECTORS) - 1)),L,-, 123 umount_from_loop_dev() {
154 ,$PARTITION_NUM_SECTORS,ef,-, 124 mount | grep -q " on ${TMPMNT} " && sudo umount ${TMPMNT}
155 ,$PARTITION_NUM_SECTORS,L,*, 125 }
156 ,$PARTITION_NUM_SECTORS,L,-,
157 ;
158 EOF
159 sync
160 126
161 # Tell kernel to update partition devices based on the new MBR: 127 # Undo both mount and loop.
162 sudo sfdisk -R "$DST" 128 my_cleanup() {
129 umount_from_loop_dev
130 loop_offset_cleanup
131 }
163 132
164 # Wait a bit and make sure that the partition device shows up. 133 # Copy the SRC_PARTITION onto the DST_DEV device at START_OFFSET and label it,
165 DST_PARTITION="${DST}3" 134 # assuming it's an ext2-based filesystem.
166 sleep 5 135 # Invoke as: command
167 if [ ! -b "$DST_PARTITION" ] 136 # Args: SRC_PARTITION DST_DEV START_OFFSET LABEL
168 then 137 install_rootfs() {
169 echo "Error: Destination system partition was not created: $DST_PARTITION" 138 local src_partition=$1
170 exit 1 139 local dst_dev=$2
171 fi 140 local start_offset=$3
141 local label=$4
142
143 # We copy the system image a bit strangely here because we have observed
144 # cases in installing to hard disk where people ctrl-c the operation and then
145 # are not longer able to properly boot a USB image. This is because when
146 # booting from USB we set the root device by label, so if you partially copy
147 # the FS here without updating the label then a subsequent USB boot may try
148 # to use the wrong device and fail.
149 sync
172 150
173 # Copy the system image. We sync first to try to make the copy as safe as we 151 echo "image $label..."
174 # can. We skip the first block which contains the filesystem label (aka
175 # volume name). It's 16 bytes in length at 120 bytes in the superblock,
176 # which is itself at offset 1024 of the device.
177 echo ""
178 echo "Copying the system image. This might take a while..."
179 sync
180 sudo dd if="$SRC_PARTITION" of="$DST_PARTITION" bs=1M seek=1 skip=1
181 # Copy all but the first 2 kibibytes now
182 sudo dd if="$SRC_PARTITION" of="$DST_PARTITION" bs=1024 seek=2 skip=2 count=1022
183 152
184 LABEL=$(sudo /sbin/e2label "$SRC_PARTITION") 153 # Copy first 2 kilobytes of the root image to a temp file, set the label,
154 # and then copy it to the dest.
155 # NOTE: This hack won't work if we stop using an ext-based rootfs.
156 local superblock_offset=1024
157 local label_field_offset=120
158 local label_field_length=16
159 sudo dd if=${src_partition} of=${TMPFILE} bs=512 count=4
160 sudo dd if=/dev/zero of=${TMPFILE} bs=1 \
161 seek=$((superblock_offset + label_field_offset)) \
162 count=$label_field_length conv=notrunc
163 echo -n "${label}" | sudo dd of=${TMPFILE} \
164 seek=$((superblock_offset + label_field_offset)) \
165 bs=1 count=$((label_field_length - 1)) conv=notrunc
166 # Copy the new label superblock to the dest.
167 sudo dd if=${TMPFILE} of=${dst_dev} bs=512 seek=${start_offset} conv=notrunc
185 168
186 # Check new file system label. We skip the first char and prefix with 'H' 169 # Copy the rest of the source to the dest.
187 NEW_LABEL=`expr substr "$LABEL" 2 ${#LABEL}` 170 sudo dd if=${src_partition} of=${dst_dev} conv=notrunc \
188 NEW_LABEL="H${NEW_LABEL}" 171 bs=512 skip=4 seek=$((${start_offset} + 4))
189 if [ ${#NEW_LABEL} -gt 15 ]
190 then
191 echo "New label " "$NEW_LABEL is too long (greater than 15 bytes)"
192 fi
193 172
194 # Copy first 2 kibibytes of the partition to a temp file 173 sync
195 TEMP_FILE=/tmp/install_part_head 174 }
196 sudo dd if="$SRC_PARTITION" of="$TEMP_FILE" bs=1024 count=2
197 175
198 # Manually update the label. First zero it, then write the new label. 176 ##############################################################################
199 SUPERBLOCK_OFFSET=1024
200 LABEL_FIELD_OFFSET=120
201 LABEL_FIELD_LENGTH=16
202 sudo dd if=/dev/zero of="$TEMP_FILE" bs=1 \
203 seek=$(( $SUPERBLOCK_OFFSET + $LABEL_FIELD_OFFSET )) \
204 count=$LABEL_FIELD_LENGTH conv=notrunc
205 echo -n "$NEW_LABEL" | sudo dd of="$TEMP_FILE" \
206 seek=$(( $SUPERBLOCK_OFFSET + $LABEL_FIELD_OFFSET )) \
207 bs=1 count=15 conv=notrunc
208 177
209 # Copy the temp file into the new partition 178 # What do we expect & require to have on the source device?
210 sudo dd if="$TEMP_FILE" of="$DST_PARTITION" 179 STATEFUL_IMG=${SRC}1
211 sudo rm -f "$TEMP_FILE" 180 KERNEL_IMG=${SRC}2
212 sync 181 ROOTFS_IMG=${SRC}3
213 182
214 # Mount root partition 183 # Steal the PMBR code from the source MBR to put on the dest MBR, for booting
215 mkdir -p "$ROOTFS_DIR" 184 # on legacy-BIOS devices.
216 sudo mount "$DST_PARTITION" "$ROOTFS_DIR" 185 sudo dd if=$SRC of=$PMBRCODE bs=512 count=1
217 # run postinst script
218 sudo "$ROOTFS_DIR"/postinst "$DST_PARTITION"
219 sudo umount "$ROOTFS_DIR"
220 186
221 # set up stateful partition 187 # Create the GPT.
222 STATEFUL_PARTITION="${DST}1" 188 install_gpt $DST $ROOTFS_IMG $KERNEL_IMG $STATEFUL_IMG $PMBRCODE
223 sudo mkfs.ext3 "$STATEFUL_PARTITION" 189
224 sudo tune2fs -L "H-STATE" "$STATEFUL_PARTITION" 190 # Install the content.
191 echo "Copying kernel..."
192 sudo dd if=${KERNEL_IMG} of=${DST} conv=notrunc bs=512 seek=${START_KERN_A}
193 sudo dd if=${KERNEL_IMG} of=${DST} conv=notrunc bs=512 seek=${START_KERN_B}
194
195 echo "Copying rootfs..."
196 install_rootfs ${ROOTFS_IMG} ${DST} ${START_ROOTFS_A} "H-ROOT-A"
197 install_rootfs ${ROOTFS_IMG} ${DST} ${START_ROOTFS_B} "H-ROOT-B"
198
199 # We can't guarantee that the kernel will see the new partition table, so we
200 # can't use it directly. We could force the kernel to reload it with an ioctl,
201 # but then we might have the UI mounting and displaying any old filesystems
202 # left over from the last install, and we don't want that either. So any access
203 # that we need to do to the destination partitions will have to go through loop
204 # devices.
205
206 # Now run the postinstall script in each new rootfs. Note that even though
207 # we're passing the new destination partition number as an arg, the postinst
208 # script had better not try to access it, for the reasons we just gave.
209 loop_offset_setup ${DST} ${START_ROOTFS_A}
210 trap loop_offset_cleanup EXIT
211 mount_on_loop_dev
212 trap my_cleanup EXIT
213 sudo ${TMPMNT}/postinst ${DST}3
214 umount_from_loop_dev
215 trap loop_offset_cleanup EXIT
216 loop_offset_cleanup
217 trap - EXIT
218
219 loop_offset_setup ${DST} ${START_ROOTFS_B}
220 trap loop_offset_cleanup EXIT
221 mount_on_loop_dev
222 trap my_cleanup EXIT
223 sudo ${TMPMNT}/postinst ${DST}5
224 umount_from_loop_dev
225 trap loop_offset_cleanup EXIT
226 loop_offset_cleanup
227 trap - EXIT
228
229 echo "Installing the stateful partition..."
230 loop_offset_setup $DST $START_STATEFUL
231 trap loop_offset_cleanup EXIT
232 sudo mkfs.ext3 -F -b 4096 -L "H-STATE" ${LOOP_DEV} \
233 $(($NUM_STATEFUL_SECTORS / 8))
225 234
226 # Install dev image into the stateful partition 235 # Install dev image into the stateful partition
227 # TODO(sosa@chromium.org) - Remove old autotest support 236 # TODO(sosa@chromium.org) - Remove old autotest support
228 if [ -f /root/.dev_mode ] || [ -d /mnt/stateful_partition/dev_image ] ; then 237 if [ -f /root/.dev_mode ] || [ -d /mnt/stateful_partition/dev_image ] ; then
229 STATEFUL_DIR=/tmp/stateful_partition_on_hd 238 mount_on_loop_dev
230 mkdir -p "$STATEFUL_DIR" 239 trap my_cleanup EXIT
231 sudo mount "$STATEFUL_PARTITION" "$STATEFUL_DIR" 240 sudo cp -fpru /mnt/stateful_partition/dev_image "$TMPMNT/dev_image"
232 sudo cp -fpru /mnt/stateful_partition/dev_image "$STATEFUL_DIR/dev_image" 241 umount_from_loop_dev
233 sudo umount "$STATEFUL_DIR" 242 trap loop_offset_cleanup EXIT
234 fi 243 fi
244 loop_offset_cleanup
245 trap - EXIT
235 246
236 # Force data to disk before we declare done. 247 # Force data to disk before we declare done.
237 sync 248 sync
238 249
239 echo "------------------------------------------------------------" 250 echo "------------------------------------------------------------"
240 echo "" 251 echo ""
241 echo "Installation to '$DST' complete." 252 echo "Installation to '$DST' complete."
242 echo "Please shutdown, remove the USB device, cross your fingers, and reboot." 253 echo "Please shutdown, remove the USB device, cross your fingers, and reboot."
243
244 trap - EXIT
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698