diff --git a/bin/cros_make_image_bootable b/bin/cros_make_image_bootable index 7179b89a6b..3517c85ec8 100755 --- a/bin/cros_make_image_bootable +++ b/bin/cros_make_image_bootable @@ -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 diff --git a/build_image b/build_image index 0865243b0a..8050d01612 100755 --- a/build_image +++ b/build_image @@ -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. diff --git a/common.sh b/common.sh index b562e0f4d6..24862aa407 100644 --- a/common.sh +++ b/common.sh @@ -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) diff --git a/mount_gpt_image.sh b/mount_gpt_image.sh index ee7b3cc6f6..be95dc5dcf 100755 --- a/mount_gpt_image.sh +++ b/mount_gpt_image.sh @@ -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}"