blob: 1b8bd4fe42517f966a60cc334c4f842996ec2480 [file] [log] [blame]
Nick Sandersf11aca62011-01-28 17:39:55 -08001#!/bin/sh -x
Colin Choweb584db2010-07-08 16:02:56 -07002
3# 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
5# found in the LICENSE file.
6#
7# /init script for use in factory install shim. Requires busybox in
8# /bin/busybox, and a symlink from /bin/sh -> busybox.
9
Nick Sandersf11aca62011-01-28 17:39:55 -080010# USB card partition and mount point.
11USB_DEVS="sdb3 sdc3 mmcblk1p3"
Nick Sandersf11aca62011-01-28 17:39:55 -080012USB_MNT=/usb
13REAL_USB_DEV=
14DM_NAME=
15
16DST=
17
18STATEFUL_MNT=/stateful
19STATE_DEV=
20
21LOG_DEV=
22LOG_FILE="/log/recovery.log"
23
24TPM_B_LOCKED=
25TPM_PP_LOCKED=
26
Nick Sandersf11aca62011-01-28 17:39:55 -080027KERN_B_VBLOCK="$STATEFUL_MNT/vmlinuz_hd.vblock"
28REAL_KERN_B_HASH=
29
30MOVE_MOUNTS="/sys /proc /dev"
31
32# To be updated to keep logging after move_mounts.
33TTY_PATH="/dev/tty"
34TAIL_PID=
35
36# Used to ensure the factory check only occurs with
37# a properly matched root and kernel.
38UNOFFICIAL_ROOT=0
39
Colin Choweb584db2010-07-08 16:02:56 -070040# Size of the root ramdisk.
41TMPFS_SIZE=300M
42
Nick Sandersf11aca62011-01-28 17:39:55 -080043on_error() {
44 # Exit, thus causing a kernel panic. We don't want to do anything else (like
45 # start a shell) because it would be trivially easy to get here (just unplug
46 # the USB drive after the kernel starts but before the USB drives are probed
47 # by the kernel) and starting a shell here would be a BIG security hole.
48 log
49 log
50 log "An unrecoverable error occurred during recovery!"
51 log
52 log "Please try again or try a newer recovery image."
53 save_log_file
54 sleep 1d
55 exit 1
56}
57
Colin Choweb584db2010-07-08 16:02:56 -070058initial_mounts() {
Nick Sandersf11aca62011-01-28 17:39:55 -080059 mkdir -p /var/lock
60 mount -t proc -o nodev,noexec,nosuid none /proc
61 mount -t sysfs -o nodev,noexec,nosuid none /sys
62 if ! mount -t devtmpfs -o mode=0755 none /dev; then
63 mount -t tmpfs -o mode=0755 none /dev
64 mknod -m 0600 /dev/console c 5 1
65 mknod -m 0601 /dev/tty1 c 4 1
66 mknod -m 0601 /dev/tty2 c 4 2
67 mknod -m 0601 /dev/tty3 c 4 3
68 mknod -m 0600 /dev/tpm0 c 10 224
69 mknod /dev/null c 1 3
70 fi
71 mkdir -p /dev/pts
72 mount -t devpts -o noexec,nosuid none /dev/pts || true
Colin Choweb584db2010-07-08 16:02:56 -070073}
74
Nick Sandersf11aca62011-01-28 17:39:55 -080075# Look for a device with our GPT ID.
76wait_for_gpt_root() {
77 [ -z "$KERN_ARG_KERN_GUID" ] && return 1
78 dlog -n "Looking for rootfs using kern_guid..."
79 for try in $(seq 20); do
80 plog " ."
81 kern=$(cgpt find -1 -u $KERN_ARG_KERN_GUID)
82 # We always try ROOT-A in recovery.
83 newroot="${kern%[0-9]*}3"
84 if [ -b "$newroot" ]; then
85 USB_DEV="$newroot"
86 dlog "Found $USB_DEV"
87 return 0
88 fi
89 sleep 1
90 done
91 dlog "Failed waiting for kern_guid"
92 return 1
Colin Choweb584db2010-07-08 16:02:56 -070093}
94
Nick Sandersf11aca62011-01-28 17:39:55 -080095# Look for any USB device.
Colin Choweb584db2010-07-08 16:02:56 -070096wait_for_root() {
Nick Sandersf11aca62011-01-28 17:39:55 -080097 dlog -n "Waiting for $USB_DEVS to appear"
Colin Choweb584db2010-07-08 16:02:56 -070098 for try in $(seq 20); do
Nick Sandersf11aca62011-01-28 17:39:55 -080099 plog " ."
100 for dev in $USB_DEVS; do
101 if [ -b "/dev/${dev}" ]; then
102 USB_DEV="/dev/${dev}"
103 dlog "Found $USB_DEV"
104 return 0
105 fi
106 done
Colin Choweb584db2010-07-08 16:02:56 -0700107 sleep 1
108 done
Nick Sandersf11aca62011-01-28 17:39:55 -0800109 dlog "Failed waiting for root!"
Colin Choweb584db2010-07-08 16:02:56 -0700110 return 1
111}
112
Nick Sandersf11aca62011-01-28 17:39:55 -0800113# Wait for dm-0 to come up.
114wait_for_dm_control() {
115 MAPPER_CONTROL=/dev/mapper/control
116 dlog -n "Waiting for $MAPPER_CONTROL to appear"
Colin Choweb584db2010-07-08 16:02:56 -0700117 for try in $(seq 20); do
Nick Sandersf11aca62011-01-28 17:39:55 -0800118 plog " ."
119 if [ -c "$MAPPER_CONTROL" ]; then
Colin Choweb584db2010-07-08 16:02:56 -0700120 return 0
121 fi
122 sleep 1
123 done
Nick Sandersf11aca62011-01-28 17:39:55 -0800124 dlog "Failed waiting for $MAPPER_CONTROL!"
Colin Choweb584db2010-07-08 16:02:56 -0700125 return 1
126}
127
Nick Sandersf11aca62011-01-28 17:39:55 -0800128check_if_dm_root() {
129 [ "$KERN_ARG_ROOT" = "/dev/dm-0" ]
130}
131
132# Attempt to find the root defined in the signed recovery
133# kernel we're booted into to. Exports REAL_USB_DEV if there
134# is a root partition that may be used - on succes or failure.
135find_official_root() {
136 plog "Checking for an official recovery image . . ."
137
138 # Check for a kernel selected root device or one in a well known location.
139 wait_for_gpt_root || wait_for_root || return 1
140
141 # Now see if it has a Chrome OS rootfs partition.
142 cgpt find -t rootfs "$(strip_partition "$USB_DEV")" || return 1
143 REAL_USB_DEV="$USB_DEV"
144
145 LOG_DEV="$(strip_partition "$USB_DEV")"1 # Default to stateful.
146
147 # Now see if the root should be integrity checked.
148 if check_if_dm_root; then
149 setup_dm_root || return 1
150 fi
151
152 mount_usb || return 1
153 return 0
154}
155
156find_developer_root() {
157 is_developer_mode || return 1
158 # Lock the TPM prior to using an untrusted root.
159 lock_tpm || return 1
160 plog "\nSearching for developer root . . ."
161 # If an official root could not be mounted, free up the underlying device
162 # if it is claimed by verity.
163 dmsetup remove "$DM_NAME"
164
165 # If we found a valid rootfs earlier, then we're done.
166 USB_DEV="$REAL_USB_DEV"
167 [ -z "$USB_DEV" ] && return 1
168 set_unofficial_root || return 1
169 mount_usb || return 1
170 return 0
171}
172
Nick Sandersf11aca62011-01-28 17:39:55 -0800173# If we have a verified recovery root, ensure all blocks are valid before
174# handing it off to the installer.
175validate_recovery_root() {
176 # Allow test recovery roots that are unverified.
177 [ "$USB_DEV" = "/dev/dm-0" ] || return 0
178 is_unofficial_root && return 0
179
180 plog "Validating official recovery image . . . "
181 # Ensure the verified rootfs is fully intact or fail with no USB_DEV.
182 # REAL_USB_DEV is left intact.
183 # Correctness wins over speed for this.
184 if ! dd if="$USB_DEV" of=/dev/null bs=$((16 * 1024 * 1024)); then
185 dlog "Included root filesystem could not be verified."
186 log " failed!"
187 dmsetup remove "$DM_NAME" # Free up the real root for use.
188 USB_DEV=
189 return 1
190 fi
191 log " completed."
192 return 0
193}
194
195setup_dm_root() {
196 dlog -n "Extracting the device mapper configuration..."
197 # export_args can't handle dm="..." at present.
198 DMARG=$(cat /proc/cmdline | sed -e 's/.*dm="\([^"\]*\)".*/\1/g')
199 DM_NAME=$(echo "$DMARG" | cut -f1 -d' ')
200 # We override the reboot-to-recovery error behavior so that we can fail
201 # gracefully on invalid rootfs.
202 DM_TABLE="$(echo "$DMARG" | cut -f2 -d,) eio"
203
204 # Don't attempt to call dmsetup if the root device isn't one that was
205 # discovered as the creation process will hang.
Will Drewrye7bc1dc2011-02-28 16:28:34 -0600206 # TODO(wad) extract the UUID and use it with cgpt find instead.
207 if [ -n "$KERN_ARG_KERN_GUID" ]; then
208 [ "${DM_TABLE%$KERN_ARG_KERN_GUID*}" = "${DM_TABLE}" ] && return 1
209 elif [ -n "${USB_DEV}" ]; then
Nick Sandersf11aca62011-01-28 17:39:55 -0800210 [ "${DM_TABLE%$USB_DEV*}" = "${DM_TABLE}" ] && return 1
211 fi
212
213 if ! dmsetup create -r "$DM_NAME" --major 254 --minor 0 --table "$DM_TABLE"
214 then
215 dlog "Failed to configure device mapper root"
216 return 1
217 fi
218 USB_DEV="/dev/dm-0"
219 if [ ! -b "$USB_DEV" ]; then
220 mknod -m 0600 "$USB_DEV" b 254 0
221 fi
222 dlog "Created device mapper root $DM_NAME."
223 return 0
224}
225
226mount_usb() {
227 dlog -n "Mounting usb"
228 for try in $(seq 20); do
229 plog " ."
230 if mount -n -o ro "$USB_DEV" "$USB_MNT"; then
231 dlog "ok"
232 return 0
233 fi
234 sleep 1
235 done
236 dlog "Failed to mount usb!"
237 return 1
238}
239
240check_if_factory_install() {
241 if is_unofficial_root; then
242 dlog "Skipping factory install check."
243 return 1
244 fi
245
246 if [ -e "${USB_MNT}/root/.factory_installer" ]; then
247 log "Detected factory install."
248 return 0
249 fi
250 return 1
251}
252
253get_stateful_dev() {
254 STATE_DEV=${REAL_USB_DEV%[0-9]*}1
255 if [ ! -b "$STATE_DEV" ]; then
256 dlog "Failed to determine stateful device"
257 return 1
258 fi
259 return 0
260}
261
Colin Choweb584db2010-07-08 16:02:56 -0700262mount_tmpfs() {
Nick Sandersf11aca62011-01-28 17:39:55 -0800263 dlog "Mounting tmpfs..."
264 mount -n -t tmpfs tmpfs "$NEWROOT_MNT" -o "size=$TMPFS_SIZE"
Colin Choweb584db2010-07-08 16:02:56 -0700265 return $?
266}
267
268copy_contents() {
Nick Sandersf11aca62011-01-28 17:39:55 -0800269 log "Copying usb->tmpfs..."
270 (cd "${USB_MNT}" ; tar cf - . | (cd "${NEWROOT_MNT}" && tar xf -))
271 RES=$?
272 log "Copy returned with result $RES"
273 return $RES
274}
275
276copy_lsb() {
277 STATEFUL_LSB="dev_image/etc/lsb-factory"
278 mkdir -p "${NEWROOT_MNT}/mnt/stateful_partition/dev_image/etc"
279 # Mounting ext3 as ext2 since the journal is unneeded in ro.
Nick Sanders4fc3f462011-03-04 21:31:25 -0800280 if ! mount -n "${STATE_DEV}" "${STATEFUL_MNT}"; then
Nick Sandersee7e6ae2011-03-03 11:21:43 -0800281 log "Failed to mount ${STATE_DEV}!! Failing."
282 return 1
283 fi
Nick Sandersf11aca62011-01-28 17:39:55 -0800284 if [ -f "${STATEFUL_MNT}/${STATEFUL_LSB}" ]; then
285 log "Found ${STATEFUL_MNT}/${STATEFUL_LSB}"
286 cp -a "${STATEFUL_MNT}/${STATEFUL_LSB}" \
287 "${NEWROOT_MNT}/mnt/stateful_partition/${STATEFUL_LSB}"
Nick Sandersee7e6ae2011-03-03 11:21:43 -0800288 else
289 log "Failed to find ${STATEFUL_MNT}/${STATEFUL_LSB}!! Failing."
290 umount "$STATEFUL_MNT"
291 return 1
Nick Sandersf11aca62011-01-28 17:39:55 -0800292 fi
293 umount "$STATEFUL_MNT"
294 rmdir "$STATEFUL_MNT"
Colin Choweb584db2010-07-08 16:02:56 -0700295}
296
297move_mounts() {
Nick Sandersf11aca62011-01-28 17:39:55 -0800298 dlog "Moving sys. proc, dev to $NEWROOT_MNT"
299 for mnt in $MOVE_MOUNTS; do
300 mkdir -p "$NEWROOT_MNT$mnt"
301 mount -n -o move "$mnt" "$NEWROOT_MNT$mnt"
Colin Choweb584db2010-07-08 16:02:56 -0700302 done
Nick Sandersf11aca62011-01-28 17:39:55 -0800303 TTY_PATH="$NEWROOT_MNT/dev/tty"
304 dlog "Done."
Colin Choweb584db2010-07-08 16:02:56 -0700305 return 0
306}
307
Nick Sandersf11aca62011-01-28 17:39:55 -0800308unmove_mounts() {
309 dlog "Moving sys. proc, dev to $NEWROOT_MNT"
310 for mnt in $MOVE_MOUNTS; do
311 mount -n -o move "$NEWROOT_MNT$mnt" "$mnt"
312 done
313 TTY_PATH="/dev/tty"
314 dlog "Done."
315 return 0
Colin Choweb584db2010-07-08 16:02:56 -0700316}
317
Nick Sandersf11aca62011-01-28 17:39:55 -0800318unmount_usb() {
319 dlog "Unmounting $USB_MNT"
320 umount "$USB_MNT"
321 # Make sure we clean up a device-mapper root.
322 if [ "$USB_DEV" = "/dev/dm-0" ]; then
323 dlog "Removing dm-verity target"
324 dmsetup remove "$DM_NAME"
325 fi
326 dlog
327 dlog "$REAL_USB_DEV can now be safely removed"
328 dlog
329 return 0
330}
331
332strip_partition() {
333 local dev="${1%[0-9]*}"
334 # handle mmcblk0p case as well
335 echo "${dev%p*}"
336}
337
338# Accomodate sd* or mmcblk* devices
339get_dst() {
340 DST=$(echo ${REAL_USB_DEV%[0-9]*} | \
341 tr -s '[0-9]' '0' | \
342 sed -e 's/sd[b-z]/sda/g')
343}
344
345dev_wait_or_error() {
346 is_developer_mode || on_error # terminal if we get here in regular mode.
347 log ""
348 log "A developer key change is required to proceed."
349 plog "Please wait 300 seconds . . ."
350 # TODO(wad) Divvy the total up into a few different prompts.
351 make_user_wait 300
352 log ""
353}
354
355recovery_wait() {
356 log ""
357 log "Preparing to recover your system image."
358 plog "If you do not wish to proceed, please reboot in the next 10 seconds ."
359 make_user_wait 10
360 log ""
361 log "System recovery is beginning."
362 log "Please do not disconnect from a power source or power down."
363 log ""
364}
365
366make_user_wait() {
367 local delay_in_sec="${1-300}"
368 while [ $delay_in_sec -gt 0 ]; do
369 plog " ."
370 sleep 1
371 delay_in_sec=$((delay_in_sec - 1))
372 done
373 log ""
374}
375
376check_key_or_wait() {
377 plog "Searching the system disk for a matching kernel key . . ."
378 if ! cgpt find -t kernel -M "$1" "$DST"; then
379 log " failed."
380 dev_wait_or_error
381 return 0
382 fi
383 log " found."
384
385 plog "Validating matching signature(s) . . ."
386 # If we found a keyblock, at the right offset, make sure it actually signed
387 # the subsequent payload.
388 local kdev=
389 for kdev in $(cgpt find -t kernel -M "$1" "$DST"); do
390 plog " ."
391 verify_kernel_signature "$kdev" "/tmp/kern.keyblock" || continue
392 log " done."
393 return 0
394 done
395
396 log " failed."
397 dev_wait_or_error
398 return 0
399}
400
Nick Sandersf11aca62011-01-28 17:39:55 -0800401get_kern_b_device() {
402 # TODO(wad) By changing boot priority, could we end up
403 # checking the recovery image or the recovery image could not
404 # be in slot A. In that case, it should fail in normal mode.
405 KERN_B_DEV=${REAL_USB_DEV%[0-9]*}4
406 if [ ! -b "${KERN_B_DEV}" ]; then
407 return 1
408 fi
409 return 0
410}
411
412get_real_kern_b_hash() {
413 REAL_KERN_B_HASH=$(dd if="${KERN_B_DEV}" | \
414 sha1sum | \
415 cut -f1 -d' ')
416 [ -n "$REAL_KERN_B_HASH" ]
417}
418
419verify_kernel_signature() {
420 local kern_dev="$1"
421 local keyblock="$2"
422
423 if ! dd if="$kern_dev" of="/tmp/kern.bin"; then
424 return 1
425 fi
426
427 # Validates the signature and outputs a keyblock.
428 if ! vbutil_kernel --verify "/tmp/kern.bin" \
429 --keyblock "$keyblock"; then
430 return 1
431 fi
432 return 0
433}
434
435verify_install_kernel() {
436 get_kern_b_device || return 1
437 get_real_kern_b_hash || return 1
438
439 # TODO(wad) check signatures from stateful on kern b using the
440 # root of trust instead of using a baked in cmdline.
441 if [ "$REAL_KERN_B_HASH" != "$KERN_ARG_KERN_B_HASH" ]; then
442 if ! is_developer_mode; then
443 log "The recovery image cannot be verified."
444 return 1
445 fi
446
447 # Extract the kernel so that vbutil_kernel will happily consume it.
448 log "Checking the install kernel for a valid developer signature . . ."
449 verify_kernel_signature "$KERN_B_DEV" "/tmp/kern_b.keyblock" || return 1
450 check_key_or_wait /tmp/kern_b.keyblock
451 fi
452 return 0
453}
454
455touch_developer_mode_file() {
456 is_developer_mode || return 1
457 mount -n -o rw -t ext3 "$DST"1 "$STATEFUL_MNT" || return 1
458 touch "$STATEFUL_MNT/.developer_mode" || return 1
459 umount "$STATEFUL_MNT" || return 1
460 return 0
461}
462
463call_image_recovery_script() {
464 mount -t tmpfs -o mode=1777 none "$USB_MNT/tmp" || return 1
465 move_mounts || return 1
466
467 # Start the copy.
468 log ""
469 log "Beginning system image copy. This will take some time . . ."
470 log ""
471 # Until images are built with the installer keyblock in KERN-B by
472 # default, we keep copying over the installer vblock from the
473 # stateful partition.
474 # TODO(wad) http://crosbug/8378
475 export IS_RECOVERY_INSTALL=1
476 chroot "$USB_MNT" \
477 /usr/sbin/chromeos-install --run_as_root --yes \
478 --payload_image="$1" \
479 --use_payload_kern_b
480 if [ $? -eq 0 ]; then
481 log "System copy completed."
482 else
483 log "Error performing system recovery!"
484 fi
485
486 # Clean up doesn't need to be successful.
487 umount "$USB_MNT/tmp"
488 unmove_mounts
489
490 # If we're in developer mode, touch the .developer_mode file on stateful
491 # to avoid a bonus wait period on reboot.
492 # Failure here is non-terminal and it may not succeed just because the
493 # partition table of the destination has not been synchronized.
494 dlog "Prepping destination stateful to avoid a secondary delay."
495 touch_developer_mode_file && log "Prepped image for developer use."
496
497 return 0
498}
499
500clear_tpm() {
501 plog "Resetting security device . . ."
502 # TODO(wad) should we fail on this?
503 tpmc ppon || dlog "tpmc ppon error: $?"
504 tpmc clear || dlog "tpmc clear error: $?"
505 tpmc enable || dlog "tpmc enable error: $?"
506 tpmc activate || dlog "tpmc activate error: $?"
507 tpmc pplock || dlog "tpmc pplock error: $?"
508 log " done."
509 return 0
510}
511
512save_log_file() {
513 local log_dev="${1:-$LOG_DEV}"
514 [ -z "$log_dev" ] && return 0
515 # The recovery stateful is usually too small for ext3.
516 # TODO(wad) We could also just write the data raw if needed.
517 # Should this also try to save
518 dlog "Attempting to save log file: $LOG_FILE -> $log_dev"
519 mount -n -o sync,rw -t ext2 "$log_dev" /tmp
520 cp "$LOG_FILE" /tmp
521 umount -n /tmp
522}
523
524stop_log_file() {
525 # Drop logging
526 exec &> "$TTY_PATH"3
527 [ -n "$TAIL_PID" ] && kill $TAIL_PID
528}
529
530is_unofficial_root() {
531 [ $UNOFFICIAL_ROOT -eq 1 ]
532}
533
534set_unofficial_root() {
535 UNOFFICIAL_ROOT=1
536 return 0
537}
538
539recover_system() {
540 local source=$(strip_partition "$REAL_USB_DEV")
541 dlog "Beginning system recovery from $source"
542
543 recovery_wait
Gaurav Shah3b6e4812011-03-14 11:38:38 -0700544
Nick Sandersf11aca62011-01-28 17:39:55 -0800545 # If we're not running a developer script then we're either
546 # installing a developer image or an official one. If we're
547 # in normal recovery mode, then we require that the KERN-B
548 # on the recovery image matches the hash on the command line.
549 # In developer mode, we will just check the keys.
550 verify_install_kernel || return 1
551
552 # Only clear on full installs. Shim scripts can call tpmc if they
553 # like. Only bGlobalLock will be in place in advance.
554 clear_tpm || return 1
555
556 call_image_recovery_script "$source" || return 1
557
558 return 0
559}
560
561use_new_root() {
562 move_mounts || on_error
563
564 # Chroot into newroot, erase the contents of the old /, and exec real init.
565 log "About to switch root"
566 stop_log_file
567 exec switch_root -c /dev/console "$NEWROOT_MNT" /sbin/init
568
569 # This should not really happen.
570 log "Failed to switch root."
571 save_log_file
572 return 1
573}
574
Will Drewry28966b02011-03-23 13:46:10 -0500575is_nonchrome() {
576 crossystem mainfw_type?nonchrome
577}
578
Nick Sandersf11aca62011-01-28 17:39:55 -0800579is_developer_mode() {
Will Drewry28966b02011-03-23 13:46:10 -0500580 # Legacy/unsupported systems are mapped to developer mode.
581 is_nonchrome && return 0
582 # Otherwise the exit status will be accurate.
583 crossystem devsw_boot?1
Nick Sandersf11aca62011-01-28 17:39:55 -0800584}
585
586lock_tpm() {
587 if [ -z "$TPM_B_LOCKED" ]; then
588 # Depending on the system, the tpm may need to be started.
589 # Don't fail if it doesn't work though.
590 tpmc startup
591 tpmc ctest
592 if ! tpmc block; then
Will Drewry28966b02011-03-23 13:46:10 -0500593 if is_nonchrome; then
594 log "No security chip appears to exist in this non-Chrome device."
595 log "The security of your experience will suffer."
596 # Forge onward.
597 else
598 log "An unrecoverable error occurred with your security device"
599 log "Please power down and try again."
600 dlog "Failed to lock bGlobalLock."
601 on_error
602 return 1 # Never reached.
603 fi
Nick Sandersf11aca62011-01-28 17:39:55 -0800604 fi
605 TPM_B_LOCKED=y
606 fi
607 if [ -z "$TPM_PP_LOCKED" ]; then
608 # TODO: tpmc pplock if appropriate
609 TPM_PP_LOCKED=y
610 fi
611 return 0
612}
613
614# Extract and export kernel arguments
615export_args() {
616 # We trust out kernel command line explicitly.
617 local arg=
618 local key=
619 local val=
620 local acceptable_set='[A-Za-z0-9]_'
621 for arg in "$@"; do
622 key=$(echo "${arg%=*}" | tr 'a-z' 'A-Z' | \
623 tr -dc "$acceptable_set" '_')
624 val="${arg#*=}"
625 export "KERN_ARG_$key"="$val"
626 dlog "Exporting kernel argument $key as KERN_ARG_$key"
627 done
628}
629
630dlog() {
631 echo "$@" | tee -a "$TTY_PATH"2 "$TTY_PATH"3
632}
633
634# User visible
635log() {
636 echo "$@" | tee -a "$TTY_PATH"1 "$TTY_PATH"2
637}
638
639plog() {
640 # plog doesn't go to /dev/tty3 or log file.
641 printf "$@" | tee "$TTY_PATH"1 "$TTY_PATH"2
642}
643
644main() {
645 exec &> "$LOG_FILE"
646
647 # Set up basic mounts, console.
648 initial_mounts
649
650 # Send all verbose output to tty3
651 (tail -f "$LOG_FILE" > "$TTY_PATH"3) &
652 TAIL_PID=$!
653
654 log "Recovery image booting . . ."
655 log ""
656 log "Press Ctrl + Alt + F1 - F3 for more detailed information."
657 log ""
658
659 # Export the kernel command line as a parsed blob prepending KERN_ARG_ to each
660 # argument.
661 export_args $(cat /proc/cmdline | sed -e 's/"[^"]*"/DROPPED/g')
662
663 if is_developer_mode; then
664 log "! Your computer's developer mode switch is in the ENABLED position."
665 log "!"
666 log "! If this is unintentional, you should power off and toggle it back "
667 log "! after recovery is completed."
668 log ""
669 fi
670
Gaurav Shah3b6e4812011-03-14 11:38:38 -0700671 if find_official_root || find_developer_root; then
Nick Sandersf11aca62011-01-28 17:39:55 -0800672 log " found."
673 else
674 log " not found."
675 on_error
676 fi
677
678 # Extract the real boot source which may be masked by dm-verity.
679 get_stateful_dev || on_error
680
681 # Check if we want to run from RAM, in the factory.
682 if check_if_factory_install; then
683 is_developer_mode || on_error # factory install requires it.
684 # Copy rootfs contents to tmpfs, then unmount USB device.
685 NEWROOT_MNT=/newroot
686 mount_tmpfs || on_error
687 copy_contents || on_error
688 copy_lsb || on_error
689 # USB device is unmounted, we can remove it now.
690 unmount_usb || on_error
691 # Switch to the new root
692 use_new_root || on_error
693 on_error # !! Never reached. !!
694 fi
695
696 # If not, we must be a recovery kernel.
697 NEWROOT_MNT="$USB_MNT"
698
Will Drewry28966b02011-03-23 13:46:10 -0500699 if is_nonchrome; then
700 log " "
701 log "Your computer does not appear to a Chrome computer!"
702 log " "
703 log "Your experience with Chromium OS will be suboptimal."
704 log " "
705 fi
706
Nick Sandersf11aca62011-01-28 17:39:55 -0800707 # Always lock the TPM. If a NVRAM reset is ever needed, we can change it.
708 lock_tpm || on_error
709
710 # Perform a full device mapper root validation to avoid any unexpected
711 # failures during postinst. It also allows us to detect if the root is
712 # intentionally mismatched - such as during Chromium OS recovery with a
713 # Chrome OS recovery kernel.
714 if ! validate_recovery_root; then
715 is_developer_mode || on_error
Gaurav Shah3b6e4812011-03-14 11:38:38 -0700716 find_developer_root || on_error
Nick Sandersf11aca62011-01-28 17:39:55 -0800717 log " found."
718 # This logic is duplicated to avoid double validating factory media. It
719 # will only be hit if a verified root can be mounted but is actually not
720 # intact.
721 get_stateful_dev || on_error
722 fi
723
724 get_dst || on_error
725
726 recover_system || on_error
727
728 log "System recovery is complete!"
729 log "Please remove the recovery device and reboot."
730
731 stop_log_file
732 # Save the recovery log to the target on success and the USB.
733 save_log_file "$DST"1
734 save_log_file
735
736 unmount_usb
737
738 log ""
739 log ""
740 plog "The system will automatically reboot in 2 minutes"
741 make_user_wait 120
742 reboot -f
743 exit 0
744}
745
746# Make this source-able for testing.
747if [ "$0" = "/init" ]; then
748 main "$@"
749 # Should never reach here.
750 exit 1
751fi
752