Skip to content

Commit

Permalink
test
Browse files Browse the repository at this point in the history
  • Loading branch information
andreacorbellini committed Apr 19, 2024
1 parent 20be0a0 commit 34eb2b0
Show file tree
Hide file tree
Showing 5 changed files with 290 additions and 0 deletions.
51 changes: 51 additions & 0 deletions .github/workflows/kernel-panic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Kernel Panic

on:
push:
pull_request:
workflow_dispatch:

env:
CARGO_TERM_COLOR: always

jobs:
kernel-panic:
name: Kernel Panic
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
fstype:
- btrfs
- ext3
- ext4
- f2fs
- hfs
- hfsplus
- xfs
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install qemu
run: |
sudo apt-get update
sudo apt-get install -y qemu-system-x86
- name: Install ${{ matrix.fstype }} tools
run: |
sudo apt-get update
case ${{ matrix.fstype }} in
btrfs) sudo apt-get install -y btrfs-progs ;;
ext*) sudo apt-get install -y e2fsprogs ;;
f2fs) sudo apt-get install -y linux-modules-extra-$(uname -r) f2fs-tools ;;
hfs*) sudo apt-get install -y linux-modules-extra-$(uname -r) hfsprogs ;;
xfs) sudo apt-get install -y xfsprogs ;;
esac
- name: Copy current kernel
run: |
sudo cp "/boot/vmlinuz-$(uname -r)" .
sudo chmod a+r "vmlinuz-$(uname -r)"
- name: Run kernel panic test
run: |
tests/linux-kernel-panic/run-test.sh --kernel "vmlinuz-$(uname -r)" --filesystem-type ${{ matrix.fstype }}
10 changes: 10 additions & 0 deletions tests/linux-kernel-panic/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "atomic-write-file-test"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
atomic-write-file = { path = "../.." }
nix = "0.28.0"
22 changes: 22 additions & 0 deletions tests/linux-kernel-panic/init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/sh -e

echo 'Mounting /proc'
mount -t proc none /proc
echo 'Mounting /sys'
mount -t sysfs none /sys
echo 'Mounting /test'
fstype=$(grep -Eo 'test.fs=\w+' /proc/cmdline | cut -d= -f2)
modprobe "$fstype" || true
mount -t "$fstype" /dev/sdb /test

if grep -q test.verify /proc/cmdline; then
echo 'Verifying test file contents'
echo '-----'
xxd /test/file
echo '-----'
fi

echo 'Running test binary'
atomic-write-file-test

echo 'Done'
184 changes: 184 additions & 0 deletions tests/linux-kernel-panic/run-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#!/bin/bash

set -o errexit
set -o nounset
set -o pipefail

status=$'\e[1;32m'
error=$'\e[1;31m'
reset=$'\e[0m'

qemu=qemu-system-$(uname -m)
kernel=/boot/vmlinuz-$(uname -r)
modules=/usr/lib/modules/$(uname -r)

busybox=$(which busybox)
init_script=init.sh
test_bin=target/release/atomic-write-file-test

filesystem_type=ext4

build_dir=build
initramfs_build_dir=$build_dir/rootfs
initramfs=$build_dir/rootfs.img
testfs=$build_dir/testfs.img
output=$build_dir/output.txt

options=$(getopt --name "$0" --options k:m:f: --long kernel:,modules:,filesystem-type: -- "$@")

eval set -- "$options"

while true; do
case "$1" in
-k|--kernel)
kernel=$(readlink -f "$2")
shift 2
;;
-m|--modules)
modules=$(readlink -f "$2")
shift 2
;;
-f|--filesystem-type)
filesystem_type=$2
shift 2
;;
--)
shift
break
;;
esac
done

if [[ $# -ne 0 ]]; then
echo "$0: extra arguments: $*" >&2
exit 1
fi

cd "$(dirname "$(readlink -f "$0")")"

#
# Compile the test binary running atomic-write-file
#
# This needs to be a statically-linked binary because the root filesystem won't
# ship with libc
#

echo "${status}Compiling${reset} static binary at $test_bin"
RUSTFLAGS='-C target-feature=+crt-static' cargo build --release

#
# Create the root filesystem and put it into an initramfs
#
# The root filesystem contains:
# - busybox (to provide /bin/sh and other basic tools)
# - the test binary
# - an init script
# - kernel modules (to support uncommon filesystems like btrfs)
#

echo "${status}Building${reset} initramfs at $initramfs"
echo " Using busybox at $busybox"
echo " Using modules at $modules"
echo " Using $init_script as /sbin/init"

rm -rf "$initramfs" "$initramfs_build_dir"

echo " ${status}Creating${reset} filesystem"

mkdir -p \
"$initramfs_build_dir/bin" \
"$initramfs_build_dir/dev" \
"$initramfs_build_dir/lib" \
"$initramfs_build_dir/proc" \
"$initramfs_build_dir/sbin" \
"$initramfs_build_dir/sys" \
"$initramfs_build_dir/test" \
"$initramfs_build_dir/usr"

ln -s ../bin "$initramfs_build_dir/usr/bin"
ln -s ../lib "$initramfs_build_dir/usr/lib"

cp "$init_script" -T "$initramfs_build_dir/sbin/init"
cp "$test_bin" -t "$initramfs_build_dir/bin"
cp "$busybox" -t "$initramfs_build_dir/bin"
"$busybox" --install -s "$initramfs_build_dir/bin"

echo " ${status}Adding${reset} uncompressed kernel modules"

mkdir -p "$initramfs_build_dir/lib/modules"
cp -a "$modules" -t "$initramfs_build_dir/lib/modules"

include_mods=( "$filesystem_type" )

for mod in "${include_mods[@]}"; do
while read -r mod_path; do
if [[ "$mod_path" = *.ko.gz ]]; then
gunzip "$mod_path" -o "$mod_path.uncompressed"
elif [[ "$mod_path" = *.ko.xz ]]; then
unxz "$mod_path" -o "$mod_path.uncompressed"
elif [[ "$mod_path" = *.ko.zst ]]; then
unzstd "$mod_path" -o "$mod_path.uncompressed"
else
continue
fi
mv "$mod_path.uncompressed" "$mod_path"
done < <(modprobe --dirname "$initramfs_build_dir" --show-depends "$mod" | grep ^insmod | cut -d' ' -f2)
done

echo " ${status}Creating${reset} squashfs file"

mksquashfs "$initramfs_build_dir" "$initramfs"

#
# Create the filesystem used for testing
#

echo "${status}Creating${reset} test $filesystem_type filesystem at $testfs"

dd if=/dev/zero of="$testfs" bs=1M count=200
"mkfs.$filesystem_type" "$testfs"

#
# Run the virtual machine
#
# This will create a file with atomic-write-file and then trigger a kernel
# panic
#

echo "${status}Running${reset} virtual machine to write test file"
echo " Using qemu at $qemu"
echo " Using kernel at $kernel"
echo " Using initramfs at $initramfs"

"$qemu" \
-kernel "$kernel" \
-append "root=/dev/sda ro panic=-1 console=ttyS0 quiet test.fs=$filesystem_type" \
-drive "index=0,media=disk,format=raw,file=$initramfs" \
-drive "index=1,media=disk,format=raw,file=$testfs" \
-no-reboot \
-nographic

#
# Run the virtual machine, again, to verify the file contents
#

echo "${status}Re-running${reset} virtual machine to verify test file contents"
echo " Using qemu at $qemu"
echo " Using kernel at $kernel"
echo " Using initramfs at $initramfs"

"$qemu" \
-kernel "$kernel" \
-append "root=/dev/sda ro panic=-1 console=ttyS0 quiet test.fs=$filesystem_type test.verify" \
-drive "index=0,media=disk,format=raw,file=$initramfs" \
-drive "index=1,media=disk,format=raw,file=$testfs" \
-no-reboot \
-nographic \
| tee "$output"

if [[ $(sed -n '/-----/, /-----/p' "$output" | xxd -r) = hello ]]; then
echo "${status}Success${reset}"
else
echo "${error}Failure${reset}"
exit 1
fi
23 changes: 23 additions & 0 deletions tests/linux-kernel-panic/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use atomic_write_file::AtomicWriteFile;
use nix::fcntl::open;
use nix::fcntl::OFlag;
use nix::sys::stat::Mode;
use nix::unistd::close;
use nix::unistd::fsync;
use std::io::Write;

fn main() {
let mut file = AtomicWriteFile::open("/test/file").expect("open failed");
file.write_all(b"hello").expect("write failed");
file.commit().expect("commit failed");

let dir = open(
"/test",
OFlag::O_DIRECTORY | OFlag::O_CLOEXEC,
Mode::empty(),
)
.expect("open directory failed");

fsync(dir).expect("directory sync failed");
close(dir).expect("closing directory failed");
}

0 comments on commit 34eb2b0

Please sign in to comment.