cros_make_image_bootable, mount_gpt_image, common.sh: root filesystem changes: ext2, ro by default

This change makes more of the root filesyste metadata static across builds, but
more can be done there.  It also changes the root filesystem to use ext2 as
we don't need journaling in normal mode.  Optionally we could use ext3 for
non-verified if desired (it's an easy change).

In particular, this change cleans up the following:
- clears the rootfs uuid
- labels it C-ROOT (instead of C-KEYFOB)
- removes reserved inodes and blocks

The major feature of this change, however, is that it adds two simple
helpers to common.sh: disable_rw_mount and enable_rw_mount.  They will
set high order byte (le) in the ro compat field to be 0xff.  This will
tell the kernel that the filesystem uses features R24-R31 which are safe
for use on the running kernel iff the filesystem is mounted read-only.

These functions are called in cros_make_image_bootable and
mount_gpt_image, respectively.  mount_gpt_image will always
enable_rw_mount and cros_make_image_bootable will disable_rw_mount if
--enable_rootfs_verification is true.

The approach is ugly but reasonably well contained.  If ext2 ever gets a
new revision and new features in the same range are introduced, then we
would be getting inconsistent behavior.  That said, it is unlikely that
that churmn will happen and if the impact is negative, it will ideally
show up during testing.

N.B., this will likely result in changes needing to be made to the
signing scripts in vboot_reference to ensure that rw mounting is
enabled/disabled in the same way (E.g., during stamping).

BUG=chromium-os:7468
TEST=- built x86-generic, imaged to usb stick, attempted to mount rw /dev/sdc3 on the host and was properly bounced.
     - booted to the image just fine on a dogfood device.
     - mod'd for recovery, then installed and booted.
     - mod_image_for_test runs with no errors; booted the resulting image as well
     - booted a factory_install with the pending dm changes
     - BVT passed with build_image x86-generic (vboot enabled)
     - [in progress] autotest that checks if the rootdev = /dev/dm-0 and
       then does a dumpe2fs | grep -q FEATURE_R31

Review URL: http://codereview.chromium.org/3916002

Change-Id: If4dcba7568a110f4e32627c916d9e5741e5e5414
This commit is contained in:
Will Drewry 2010-10-20 15:44:11 -05:00
parent 6304adc473
commit 55b42c94ca
4 changed files with 73 additions and 9 deletions

View File

@ -148,6 +148,15 @@ make_image_bootable() {
root_dev=$(mount | grep -- "on ${FLAGS_rootfs_mountpoint} type" |
cut -f1 -d' ' | tail -1)
# Make the filesystem un-mountable as read-write.
# mount_gpt_image.sh will undo this as needed.
# TODO(wad) make sure there is parity in the signing scripts.
if [ ${FLAGS_enable_rootfs_verification} -eq ${FLAGS_TRUE} ]; then
# TODO(wad) this would be a good place to reset any other ext2 metadata.
warn "Disabling r/w mount of the root filesystem"
disable_rw_mount "$root_dev"
fi
if [ ${FLAGS_use_dev_keys} -eq ${FLAGS_TRUE} ]; then
use_dev_keys="--use_dev_keys"
fi

View File

@ -488,8 +488,6 @@ create_base_image() {
trap "cleanup && delete_prompt" EXIT
UUID=$(uuidgen)
# Create and format the root file system.
# Check for loop device before creating image.
@ -510,13 +508,22 @@ create_base_image() {
seek=$((ROOT_SIZE_BYTES + ROOT_HASH_PAD - 1))
sudo losetup "${LOOP_DEV}" "${ROOT_FS_IMG}"
# Specify a block size and block count to avoid using the hash pad.
sudo mkfs.ext3 -b 4096 "${LOOP_DEV}" "$((ROOT_SIZE_BYTES / 4096))"
sudo mkfs.ext2 -b 4096 "${LOOP_DEV}" "$((ROOT_SIZE_BYTES / 4096))"
# Tune and mount rootfs.
# TODO(wad) rename the disk label to match the GPT since we
# can't change it later.
DISK_LABEL="C-KEYFOB"
sudo tune2fs -L "${DISK_LABEL}" -U "${UUID}" -c 0 -i 0 "${LOOP_DEV}"
DISK_LABEL="C-ROOT"
# Disable checking and minimize metadata differences across builds
# and wasted reserved space.
sudo tune2fs -L "${DISK_LABEL}" \
-U clear \
-T 20091119110000 \
-c 0 \
-i 0 \
-m 0 \
-r 0 \
-e remount-ro \
"${LOOP_DEV}"
# TODO(wad) call tune2fs prior to finalization to set the mount count to 0.
sudo mount "${LOOP_DEV}" "${ROOT_FS_DIR}"
# Create stateful partition of the same size as the rootfs.

View File

@ -323,7 +323,7 @@ V_BOLD_YELLOW="\e[1;33m"
function info {
echo -e >&2 "${V_BOLD_GREEN}INFO : $1${V_VIDOFF}"
}
}
function warn {
echo -e >&2 "${V_BOLD_YELLOW}WARNING: $1${V_VIDOFF}"
@ -449,6 +449,47 @@ setup_symlinks_on_root() {
sudo ln -s "${var_target}" "${dev_image_root}/var"
}
# These two helpers clobber the ro compat value in our root filesystem.
#
# When the system is built with --enable_rootfs_verification, bit-precise
# integrity checking is performed. That precision poses a usability issue on
# systems that automount partitions with recognizable filesystems, such as
# ext2/3/4. When the filesystem is mounted 'rw', ext2 metadata will be
# automatically updated even if no other writes are performed to the
# filesystem. In addition, ext2+ does not support a "read-only" flag for a
# given filesystem. That said, forward and backward compatibility of
# filesystem features are supported by tracking if a new feature breaks r/w or
# just write compatibility. We abuse the read-only compatibility flag[1] in
# the filesystem header by setting the high order byte (le) to FF. This tells
# the kernel that features R24-R31 are all enabled. Since those features are
# undefined on all ext-based filesystem, all standard kernels will refuse to
# mount the filesystem as read-write -- only read-only[2].
#
# [1] 32-bit flag we are modifying:
# http://git.chromium.org/cgi-bin/gitweb.cgi?p=kernel.git;a=blob;f=include/linux/ext2_fs.h#l417
# [2] Mount behavior is enforced here:
# http://git.chromium.org/cgi-bin/gitweb.cgi?p=kernel.git;a=blob;f=fs/ext2/super.c#l857
#
# N.B., if the high order feature bits are used in the future, we will need to
# revisit this technique.
disable_rw_mount() {
local rootfs="$1"
local offset="${2-0}" # in bytes
local ro_compat_offset=$((0x467 + 3)) # Set 'highest' byte
echo -ne '\xff' |
sudo dd of="$rootfs" seek=$((offset + ro_compat_offset)) \
conv=notrunc count=1 bs=1
}
enable_rw_mount() {
local rootfs="$1"
local offset="${2-0}"
local ro_compat_offset=$((0x467 + 3)) # Set 'highest' byte
echo -ne '\x00' |
sudo dd of="$rootfs" seek=$((offset + ro_compat_offset)) \
conv=notrunc count=1 bs=1
}
# Get current timestamp. Assumes common.sh runs at startup.
start_time=$(date +%s)

View File

@ -79,7 +79,14 @@ function get_gpt_partitions() {
# Mount the rootfs partition using a loopback device.
local offset=$(partoffset "${FLAGS_from}/${filename}" 3)
local ro_flag=""
[ ${FLAGS_read_only} -eq ${FLAGS_TRUE} ] && ro_flag="-o ro"
if [ ${FLAGS_read_only} -eq ${FLAGS_TRUE} ]; then
ro_flag="-o ro"
else
# Make sure any callers can actually mount and modify the fs
# if desired.
# cros_make_image_bootable should restore the bit if needed.
enable_rw_mount "${FLAGS_from}/${filename}" "$(( offset * 512 ))"
fi
sudo mount ${ro_flag} -o loop,offset=$(( offset * 512 )) \
"${FLAGS_from}/${filename}" "${FLAGS_rootfs_mountpt}"