blob: dde65ac9fda55ca37d30d92c0ce8efbde26bd5ff [file] [log] [blame]
#!/bin/bash
set -e -o pipefail
XFSTESTS_FLAVOR=android
t=$(echo ${XFSTESTS_FLAVOR}_xfstests_dir | tr "[:lower:]" "[:upper:]")
eval DIR="\$$t"
if test -z "$DIR"
then
DIR="$(dirname "$(readlink -f "$0")")"
fi
if test ! -f "$DIR/util/get-config"
then
echo "$(basename "$0"): couldn't find $DIR/util/get-config"
exit 1
fi
. "$DIR/util/get-config"
. "$DIR/util/parse_opt_funcs"
. "$DIR/util/arch-funcs"
case "$1" in
install-kconfig)
shift
if test ! -x "$DIR/util/install-kconfig"
then
echo "Kernel configuration not supported in this installation"
exit 1
fi
export GCE_XFSTESTS_DIR="$DIR"
XFSTESTS_FLAVOR=$XFSTESTS_FLAVOR "$DIR/util/install-kconfig" "$@"
exit $?
;;
kbuild)
shift
if test ! -x "$DIR/util/kbuild"
then
echo "kbuild not supported in this installation"
exit 1
fi
export GCE_XFSTESTS_DIR="$DIR"
XFSTESTS_FLAVOR=$XFSTESTS_FLAVOR "$DIR/util/kbuild" "$@"
exit $?
;;
esac
# Chroot directory on device.
# Note: this will be wiped clean when deploying a new chroot tarball.
CHROOT_DIR="/data/xfstests-chroot"
# Results directory.
RESULTS_DIR="/data/xfstests-results"
. "$DIR/util/parse_cli"
if test -n "$SKIP_LOG" ; then
LOGFILE=/tmp/log.$(date +%Y%m%d%H%M)
else
mkdir -p "$DIR/logs"
LOGFILE="$DIR/logs/log.$(date +%Y%m%d%H%M)"
fi
die()
{
echo -e 1>&2 "[ERROR] android-xfstests: $*"
exit 1
}
ask_yesno()
{
local response
echo -n -e "$@ (y/N) "
read response
if [ "$response" != y ]; then
exit 1
fi
}
adb_ready()
{
adb devices | grep -E -q '(device|recovery)$'
}
fastboot_ready()
{
fastboot devices | grep -q 'fastboot$'
}
wait_for_device()
{
local want_adb=false
local want_fastboot=false
local waiting_for=""
local unauthorized=false
local waiting=false
if [[ ,$1, == *,adb,* ]]; then
want_adb=true
waiting_for="adb to be ready"
fi
if [[ ,$1, == *,fastboot,* ]]; then
want_fastboot=true
waiting_for+="${waiting_for:+ or for }device to enter fastboot mode"
fi
: "${waiting_for:=device}"
while true; do
if $want_adb; then
if adb_ready; then
break
fi
if ! $unauthorized && adb devices | grep -q 'unauthorized$'; then
echo "adb is not authorized. Authorize it using the dialog on the device to continue."
unauthorized=true
fi
fi
if $want_fastboot && fastboot_ready; then
return
fi
if ! $waiting && ! $unauthorized; then
echo "Waiting for $waiting_for..."
waiting=true
fi
sleep 0.5
done
# Make sure adbd is running as root and that SELinux is in permissive mode.
if ! adb root > /dev/null ; then
die "Unable to restart adbd as root on the device. Maybe your device is not rooted?"
fi
adb shell "setenforce 0"
}
wait_for_adb()
{
wait_for_device adb
}
wait_for_fastboot()
{
wait_for_device fastboot
}
wait_for_adb_or_fastboot()
{
wait_for_device adb,fastboot
}
reboot_into_fastboot_mode()
{
adb reboot bootloader
wait_for_fastboot
}
# Query the version of the kernel running on the device
query_kernel_version()
{
adb shell "uname -r -v"
}
# Try to extract the version information from the $KERNEL image by grepping for
# the linux_banner[] string. It's a hack, but there doesn't seem to be a better
# way, and scripts elsewhere supposedly have been doing this for a long time...
extract_kernel_version()
{
local decompress
# Note: we use the filename extension rather than the 'file' program to get
# the compression format because old versions of 'file' don't recognize
# LZ4-compressed files.
case "$(basename "$KERNEL")" in
Image.gz*)
decompress="gzip -d -c"
;;
Image.bz2*)
decompress="bzip2 -d -c"
;;
Image.xz*)
decompress="xz -d -c"
;;
Image.lz4*)
decompress="lz4 -d" # no -c option; stdout is assumed when not a tty
;;
*)
decompress="cat"
;;
esac
local banner="$($decompress "$KERNEL" \
| grep -a -m1 'Linux version [0-9]\+\.[0-9]\+.*#.*$')"
if [ -n "$banner" ]; then
local krelease="$(echo "$banner" | awk '{print $3}')"
local kver="#${banner##*#}"
echo "$krelease $kver"
fi
}
# If the specified $KERNEL isn't already running on the device, try to boot it
# using 'fastboot boot'.
boot_kernel()
{
local version actual_version
local have_version=true
if [ ! -f "$KERNEL" ]; then
die "The specified kernel image does not exist: $KERNEL"
fi
version="$(extract_kernel_version "$KERNEL")"
if [ -z "$version" ]; then
cat 1>&2 <<EOF
Warning: unable to extract version information from $KERNEL.
We won't be able to verify that the device has successfully booted the kernel!
EOF
version="(unknown version)"
have_version=false
fi
wait_for_adb_or_fastboot
if adb_ready; then
actual_version="$(query_kernel_version)"
if $have_version && [ "$version" = "$actual_version" ]; then
# Kernel is already running.
return
fi
echo "Rebooting to start new kernel: $version"
stop_existing_tests
reboot_into_fastboot_mode
else
echo "Starting kernel: $version"
fi
fastboot boot "$KERNEL"
wait_for_adb
actual_version="$(query_kernel_version)"
if $have_version && [ "$version" != "$actual_version" ]; then
die "Kernel did not successfully boot!\n" \
"Expected: $version\n" \
"Actual: $actual_version\n"
fi
}
chroot_prepare()
{
cat <<EOF | adb shell
! mountpoint $CHROOT_DIR/sys > /dev/null && mount sysfs -t sysfs $CHROOT_DIR/sys
! mountpoint $CHROOT_DIR/proc > /dev/null && mount proc -t proc $CHROOT_DIR/proc
! mountpoint $CHROOT_DIR/dev > /dev/null && mount --bind /dev $CHROOT_DIR/dev
! mountpoint $CHROOT_DIR/dev/pts > /dev/null && mount --bind /dev/pts $CHROOT_DIR/dev/pts
# Allow xfstests to detect that SELinux is in use.
! mountpoint $CHROOT_DIR/sys/fs/selinux > /dev/null && \
mount selinuxfs -t selinuxfs $CHROOT_DIR/sys/fs/selinux
touch $CHROOT_DIR/etc/selinux/config
if [ ! -e $CHROOT_DIR/usr/sbin/selinuxenabled ]; then
ln $CHROOT_DIR/bin/true $CHROOT_DIR/usr/sbin/selinuxenabled
fi
# 'mountpoint' doesn't work with directory bind mounts; use /proc/mounts instead
if ! cut -d' ' -f2 /proc/mounts 2>/dev/null | grep -q '^$CHROOT_DIR/results$'; then
mkdir -p $RESULTS_DIR
mount --bind $RESULTS_DIR $CHROOT_DIR/results
fi
# /dev/fd needs to exist in order for bash process substitution to work.
if [ ! -e /dev/fd ]; then
ln -s /proc/self/fd /dev/fd
fi
# Android puts loopback device nodes in /dev/block/ instead of /dev/.
# But losetup can only find them in /dev/, so create them there too.
for i in \`seq 0 7\`; do
if [ ! -e /dev/loop\$i ]; then
mknod /dev/loop\$i b 7 \$i
fi
done
EOF
}
chroot_wipe()
{
cat <<EOF | adb shell
umount $CHROOT_DIR/sys/fs/selinux &> /dev/null
umount $CHROOT_DIR/sys &> /dev/null
umount $CHROOT_DIR/proc &> /dev/null
umount $CHROOT_DIR/dev/pts &> /dev/null
umount $CHROOT_DIR/dev &> /dev/null
umount $CHROOT_DIR/results &> /dev/null
rm -rf $CHROOT_DIR
mkdir $CHROOT_DIR
EOF
}
# When entering a login shell, we need to override $PATH to allow commands in
# /etc/profile to run, as they fail with the Android $PATH. The $PATH will,
# however, get overridden again when /root/.bashrc is sourced. (It's fine to do
# this for noninteractive shell commands too.)
CHROOT_CMD="HOME=/root TMPDIR=/tmp PATH=\$PATH:/usr/sbin:/usr/bin:/sbin:/bin chroot $CHROOT_DIR"
chroot_run()
{
adb shell "$CHROOT_CMD /bin/bash -c \"$*\""
}
chroot_interactive_shell()
{
# The -t option ("force PTY allocation") makes the shell interactive even
# though we're passing 'adb shell' a command to run. This allows us to
# start a shell in the chroot.
adb shell -t "$CHROOT_CMD /bin/sh -c \"cd ~; /bin/bash --login\""
}
setup_chroot()
{
if ! [ -f "$ROOT_FS" ]; then
echo "The xfstests chroot tarball does not exist:"
echo " $ROOT_FS"
if [ -z "$ROOT_FS_URL" ]; then
exit 1
fi
ask_yesno "Would you like to download the latest public tarball to that location?"
wget -O "$ROOT_FS" "$ROOT_FS_URL"
echo "Finished downloading chroot tarball."
fi
local old_md5sum="$(adb shell '[ -e '$CHROOT_DIR'/chroot_md5sum ] &&
cat '$CHROOT_DIR'/chroot_md5sum')"
local new_md5sum="$(md5sum "$ROOT_FS" | cut -d' ' -f1)"
if [ "$old_md5sum" = "$new_md5sum" ]; then
chroot_prepare
return 0
fi
echo "Deploying chroot tarball to device (path=$ROOT_FS, md5sum=$new_md5sum)..."
stop_existing_tests
chroot_wipe
# If the chroot tarball is in .tar.xz format, then decompress it host-side,
# since Android devices don't usually include the xz program.
local srcfile="$ROOT_FS"
local decompress="cat"
if file "$ROOT_FS" | grep -q '\<XZ compressed\>'; then
xz -d -c "$ROOT_FS" > "$tmpfile"
srcfile="$tmpfile"
elif file "$ROOT_FS" | grep -q '\<gzip compressed\>'; then
decompress="gzip -d -c"
fi
local destfile=$CHROOT_DIR/"$(basename "$ROOT_FS")"
adb push "$srcfile" "$destfile"
cat <<EOF | adb shell
$decompress "$destfile" | tar -C $CHROOT_DIR -x
mv $CHROOT_DIR/root/test-config $CHROOT_DIR/root/test-config.orig
echo $new_md5sum > $CHROOT_DIR/chroot_md5sum
EOF
adb push "$DIR/test-appliance/android-test-config" "$CHROOT_DIR/root/test-config"
chroot_prepare
}
try_shrink_userdata()
{
local fs_type
cat <<EOF
It seems you haven't run android-xfstests on this device yet, so
there isn't any space for the xfstests partitions. Would you like
to make space for them by reformatting the userdata filesystem with
a smaller size? WARNING: THIS WILL DELETE ALL USER DATA!
EOF
ask_yesno " Erase and reformat userdata with smaller size?"
echo
# The filesystem type (e.g. "ext4" or "f2fs") for 'fastboot format' defaults
# to the value of the bootloader variable "partition-type:userdata". But on
# some devices this is set to "raw" which doesn't work. Instead, just
# specify the filesystem type which the device is already using.
fs_type=$(adb shell "cat /proc/mounts" | awk '$2 == "/data" {print $3}')
reboot_into_fastboot_mode
fastboot "format:$fs_type:0x100000000" userdata # 4 GiB
}
setup_partitions()
{
adb push $DIR/test-appliance/android-setup-partitions \
$CHROOT_DIR/setup-partitions > /dev/null
adb shell "rm -f $CHROOT_DIR/setup-partitions-result"
chroot_run /setup-partitions
echo "unknown" > "$tmpfile"
adb pull $CHROOT_DIR/setup-partitions-result "$tmpfile" &> /dev/null || true
local result="$(<"$tmpfile")"
case "$result" in
ready)
;;
shrink_userdata)
return 1
;;
insufficient_space)
die "This device doesn't have enough space on its internal storage to run android-xfstests."
;;
*)
die "An unexpected problem occurred while setting up the xfstests partitions."
;;
esac
return 0
}
xfstests_running()
{
[ -n "$(adb shell 'pgrep runtests\.sh')" ]
}
# If xfstests is already running, ask the user if they want to terminate it
stop_existing_tests()
{
if ! xfstests_running; then
return 0
fi
ask_yesno "xfstests is already running! Terminate it?"
adb shell "pkill -f \"bash \./check\""
local start=$(date +%s)
while (( $(date +%s) <= start + 10 )); do
local existing=$(adb shell 'pgrep runtests\.sh')
if [ -z "$existing" ]; then
return 0
fi
sleep 1
done
die "Failed to stop existing xfstests instance."
}
tmpfile="$(mktemp)"
trap "rm -f \"$tmpfile\"" EXIT
if ! type -P adb > /dev/null; then
die "adb is not installed"
fi
if ! type -P fastboot > /dev/null ; then
die "fastboot is not installed"
fi
case "$ARG" in
cmd=shell*|cmd=maint*)
want_shell=true
;;
*)
want_shell=false
if adb_ready; then
stop_existing_tests
fi
;;
esac
# Set up the kernel, the chroot, and the xfstests partitions.
tried_to_shrink_userdata=false
while true; do
# Start by booting into the correct kernel.
if [ -n "$KERNEL" ]; then
boot_kernel
elif fastboot_ready; then
fastboot continue
fi
wait_for_adb
# Set up the chroot and xfstests partitions. Note: if an interactive shell
# is requested and tests are currently running, we won't mess around with
# the partitions. However, we'll still try to set up the chroot just in
# case a different ROOT_FS was specified (in which case the existing tests
# will need to be stopped).
setup_chroot
if $want_shell && xfstests_running; then
break
fi
if setup_partitions; then
break
fi
# Need to shrink userdata to make space for the xfstests partitions!
if $tried_to_shrink_userdata; then
die "An unexpected problem occurred when shrinking userdata."
fi
try_shrink_userdata
tried_to_shrink_userdata=true
# 'userdata' has just been formatted and the device is now in fastboot mode.
# Start the configuration over again.
done
if $want_shell; then
chroot_interactive_shell
exit 0
fi
cat > "$tmpfile" <<EOF
#!/bin/bash
cd /root
. test-config
export FSTESTAPI="$(echo $API | sed -e 's/\./ /g')"
export FSTESTCFG="$(echo $FSTESTCFG | sed -e 's/,/ /g')"
export FSTESTSET="$(echo $FSTESTSET | sed -e 's/,/ /g')"
export FSTESTOPT="$(echo $FSTESTOPT | sed -e 's/,/ /g')"
export FSTESTTYP="$PRIMARY_FSTYPE"
export MNTOPTS="$MNTOPTS"
export FSTESTEXC="$(echo $FSTESTEXC | sed -e 's/,/ /g')"
umount \$PRI_TST_MNT &> /dev/null
umount \$SM_TST_MNT &> /dev/null
umount \$SM_SCR_MNT &> /dev/null
umount \$LG_TST_MNT &> /dev/null
umount \$LG_SCR_MNT &> /dev/null
./runtests.sh
EOF
adb push "$tmpfile" $CHROOT_DIR/run-xfstests > /dev/null
adb shell "chmod +x $CHROOT_DIR/run-xfstests"
chroot_run /run-xfstests |& tee $LOGFILE
if test -n "$SKIP_LOG" ; then
rm $LOGFILE
else
sed -i -e '/^-------------------- Summary report/,$d' $LOGFILE
echo "logfile in $LOGFILE"
fi