| #!/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 |