blob: 997cfd1cfc459988339dc4cb2a5ad4737a68bc7d [file] [log] [blame]
# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
. "${BUILD_LIBRARY_DIR}/../common.sh" || exit 1
# Usage: fs_parse_option <mount_options> <option_key> [default_value]
#
# Print the value associated with the option_key in the passed mount_options,
# or the optional default_value if it wasn't specified.
#
# Args:
# mount_options: Options that could be passed to the "mount" command, for
# example "loop,ro".
# option_key: The key you are looking for.
# default_value: An optional default value used if the option key is not
# found.
fs_parse_option() {
local mount_options="$1"
local option_key="$2"
local default_value="${3:-}"
# offset= interacts with dirty pages in the file in a very poor manner. See
# crbug.com/954188. Use device partitions on the loop device instead.
case "${option_key}" in
offset|size)
local msg="Support for ${option_key} dropped from fs_parse_option."
msg="${msg} See crbug.com/954188."
die "${msg}"
# unittests cause die to return to us, so make sure we return the default.
option_key='$'
;;
esac
local option_value
if option_value=$(echo "${mount_options}" | tr , '\n' | \
grep -E "^${option_key}"'(=|$)'); then
echo "${option_value}" | cut --fields=2 --delimiter== --only-delimited
else
echo "${default_value}"
fi
}
# Usage: fs_mount <part_dev> <mount_point> <fs_format> [ro_rw] [mount_options]
#
# Mount the passed partition device in the mount point. The partition is mounted
# as the fs_format filesystem (if fs_format is not empty). If the filesystem
# doesn't support to be mounted as read-write, like for example squashfs or
# ubifs, and "rw" mount is requested the contents are copied instead. When
# unmounted, the contents will be copied back to the partition, but you need to
# unmount the filesystem calling fs_umount.
#
# Args:
# part_dev: A block device with the partition to mount.
# mount_point: A directory where to mount the filesystem.
# fs_format: The filesystem format, such as for example "ext2" or "squashfs".
# ro_rw: The ro_rw parameter should be "ro" or "rw" (the default if empty).
# mount_options: Extra mount options passed to the command "mount" when used.
fs_mount() {
local part_dev="$1"
local mount_point="$2"
local fs_format="$3"
local ro_rw="${4:-rw}"
local mount_options="${5:-}"
if [[ "${ro_rw}" != "ro" && "${ro_rw}" != "rw" ]]; then
die "ro_rw must be \"ro\" or \"rw\", not \"${ro_rw}\"."
fi
# Explicitly deny offset= in options.
if echo ${mount_options} | grep -qE '^(.*,)?offset='; then
die "Support for offset= dropped from fs_mount. See crbug.com/954188."
fi
local all_options="${ro_rw}"
[[ -n "${mount_options}" ]] && all_options="${ro_rw},${mount_options}"
# TODO: move this to layout file.(crbug.com/710929)
case ${fs_format} in
btrfs) all_options+=",compress=zlib";;
esac
case ${fs_format} in
ext[234]|fat12|fat16|fat32|fat|vfat|btrfs|"")
local extra_flags=()
if [[ -n "${fs_format}" ]]; then
case ${fs_format} in
fat12|fat16|fat32|fat|vfat)
extra_flags=( -t "vfat" )
;;
*)
extra_flags=( -t "${fs_format}" )
;;
esac
fi
sudo mount "${part_dev}" "${mount_point}" -o "${all_options}" \
"${extra_flags[@]}"
;;
squashfs)
if [[ "${ro_rw}" == "ro" ]]; then
sudo mount "${part_dev}" "${mount_point}" -o "${all_options}" \
-t "${fs_format}"
else
local sizelimit=$(fs_parse_option "${mount_options}" sizelimit)
if [[ -n "${sizelimit}" ]]; then
local losetup_opts=( --show --read-only --sizelimit "${sizelimit}" )
part_dev=$(sudo losetup "${losetup_opts[@]}" -f "${part_dev}")
fi
sudo unsquashfs -dest "${mount_point}" -no-progress -force "${part_dev}"
if [[ -n "${sizelimit}" ]]; then
# Cleanup the loop device used to unsquash the filesystem.
sudo losetup -d "${part_dev}"
fi
sudo unsquashfs -dest "${mount_point}" -no-progress -force "${part_dev}"
fi
;;
*)
die "Unknown fs format '${fs_format}'";;
esac
}
# Usage: fs_create <fs_uuid> <fs_label> <fs_bytes> <fs_block_size> <fs_format>
# <fs_options> <part_dev>
fs_create() {
local fs_uuid="$1"
local fs_label="$2"
local fs_bytes=$3
local fs_block_size=$4
local fs_format="$5"
local fs_options="$6"
local part_dev="$7"
# Split the fs_options into an array.
local fs_options_arr=(${fs_options})
case ${fs_format} in
ext[234])
# When mke2fs supports the same values for -U as tune2fs does, the
# following conditionals can be removed and ${fs_uuid} can be used
# as the value of the -U option as-is.
local uuid_option=()
if [[ "${fs_uuid}" == "clear" ]]; then
fs_uuid="00000000-0000-0000-0000-000000000000"
fi
if [[ "${fs_uuid}" != "random" ]]; then
uuid_option=( -U "${fs_uuid}" )
fi
sudo mkfs.${fs_format} -F -q -O ext_attr \
"${uuid_option[@]}" \
-E lazy_itable_init=0 \
-b ${fs_block_size} \
"${fs_options_arr[@]}" \
"${part_dev}" "$((fs_bytes / fs_block_size))"
# We need to redirect from stdin and clear the prompt variable to make
# sure tune2fs doesn't throw up random prompts on us. We know that the
# command below is what we want and is safe (it's a new FS).
unset TUNE2FS_FORCE_PROMPT
sudo tune2fs -L "${fs_label}" \
-c 0 \
-i 0 \
-T 20091119110000 \
-m 0 \
-r 0 \
-e remount-ro \
"${part_dev}" </dev/null
;;
fat12|fat16|fat32)
sudo mkfs.vfat -F ${fs_format#fat} -n "${fs_label}" "${part_dev}" \
"${fs_options_arr[@]}"
;;
fat|vfat)
# -I flag is needed to ignore a (we think) false error about formatting
# a device that already has partitions on it
sudo mkfs.vfat -I -n "${fs_label}" "${part_dev}" "${fs_options_arr[@]}"
;;
squashfs)
# Creates an empty squashfs filesystem so unsquashfs works.
local squash_dir="$(mktemp -d --suffix=.squashfs)"
local squash_file="$(mktemp --suffix=.squashfs)"
# Make sure / has the right permission. "-all-root" will change the uid/gid.
chmod 0755 "${squash_dir}"
# If there are errors in mkquashfs they are sent to stderr, but in the
# normal case a lot of useless information is sent to stdout.
mksquashfs "${squash_dir}" "${squash_file}" -noappend -all-root \
-no-progress -no-recovery "${fs_options_arr[@]}" >/dev/null
rmdir "${squash_dir}"
sudo dd if="${squash_file}" of="${part_dev}" bs=4096 status=none
rm "${squash_file}"
;;
btrfs)
sudo mkfs.${fs_format} -b "$((fs_bytes))" -d single -m single -M \
-L "${fs_label}" -O "${fs_options_arr[@]}" "${part_dev}"
;;
*)
die "Unknown fs format '${fs_format}' for part ${part_dev}";;
esac
}
# Usage: fs_umount <part_dev> <mount_point> <fs_format> <fs_options> \
# [mount_options]
#
# Unmount the partition mounted with fs_mount.
#
# Args:
# part_dev: The block device with the partition that was mounted.
# mount_point: The directory where the partition was mounted.
# fs_format: The filesystem format, such as for example "ext2" or "squashfs".
# fs_options: The options used when creating the filesystem. These options are
# used when we need to recreate the fs.
# mount_options: Extra mount options passed to the command "mount" when used.
# Only the "offset=" options is considering while unmounting.
fs_umount() {
local part_dev="$1"
local mount_point="$2"
local fs_format="$3"
local fs_options="$4"
local mount_options="${5:-}"
if mountpoint -q "${mount_point}"; then
# First use safe_umount_tree for the general case. This also unmounts
# mount points created with "mount --bind" in the filesystem.
safe_umount_tree "${mount_point}"
return
fi
case ${fs_format} in
ext[234]|fat12|fat16|fat32|fat|vfat|"")
# Nothing else to do for these filesystems.
;;
squashfs)
# Unmount anything else that could be mounted in the filesystem before
# re-squashing.
safe_umount_tree "${mount_point}"
# Re-squash the filesystem to a temporary file.
local squash_file="$(mktemp --suffix=.squashfs)"
local fs_options_arr=(${fs_options})
# If there are errors in mkquashfs they are sent to stderr, but in the
# normal case a lot of useless information is sent to stdout.
sudo mksquashfs "${mount_point}" "${squash_file}" -noappend \
-no-progress -no-recovery "${fs_options_arr[@]}" >/dev/null
local sizelimit=$(fs_parse_option "${mount_options}" sizelimit)
local squashed_size=$(stat -c%s "${squash_file}")
if [[ -n "${sizelimit}" && "${sizelimit}" -lt "${squashed_size}" ]]; then
sudo rm -f "${squash_file}"
die "The squashfs filesystem mounted at ${mount_point} is "\
"${squashed_size} bytes but the sizelimit is ${sizelimit} bytes, about "\
"$(( (squashed_size - sizelimit) / 1024 )) KiB smaller. Please increase the "\
"size of your filesystem or remove some files from it."
fi
# mksquashfs pads the filesystem up to 4kB, but we can use a bigger block
# size to improve speed.
sudo dd if="${squash_file}" of="${part_dev}" bs=8M \
oflag=seek_bytes conv=notrunc status=none
sudo rm -f "${squash_file}"
;;
*)
die "Unknown fs format '${fs_format}'";;
esac
}
# Usage: fs_remove_mountpoint <mount_point>
#
# fs_umount will unmount the filesystem but will keep the mount point
# directory. When using squashfs in rw mode, the contents of the filesystem
# will remain in the mount point directory.
# This function removes the mountpoint directory as long as it is not mounted.
# Returns whether it was successfully removed.
fs_remove_mountpoint() {
local mount_point="$1"
safe_umount_tree "${mount_point}" || return
if ! mountpoint -q "${mount_point}"; then
sudo rm -rf "${mount_point}"
fi
}