|
| 1 | +#!/bin/bash |
| 2 | +# Run GNU SMACK tests in QEMU with SMACK-enabled kernel |
| 3 | +# Usage: run-gnu-tests-smack-ci.sh [GNU_DIR] [OUTPUT_DIR] |
| 4 | +# spell-checker:ignore rootfs zstd unzstd cpio newc nographic smackfs devtmpfs tmpfs poweroff libm libgcc libpthread libdl librt sysfs rwxat |
| 5 | +set -e |
| 6 | + |
| 7 | +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" |
| 8 | +REPO_DIR="$(dirname "$SCRIPT_DIR")" |
| 9 | +GNU_DIR="${1:-$REPO_DIR/../gnu}" |
| 10 | +OUTPUT_DIR="${2:-$REPO_DIR/target/smack-test-results}" |
| 11 | +SMACK_DIR="$REPO_DIR/target/smack-test" |
| 12 | + |
| 13 | +echo "Setting up SMACK test environment..." |
| 14 | +rm -rf "$SMACK_DIR" |
| 15 | +mkdir -p "$SMACK_DIR"/{rootfs/{bin,lib64,proc,sys,dev,tmp,etc,gnu},kernel} |
| 16 | + |
| 17 | +# Download Arch Linux kernel (has SMACK built-in) |
| 18 | +if [ ! -f /tmp/arch-vmlinuz ]; then |
| 19 | + echo "Downloading Arch Linux kernel..." |
| 20 | + MIRROR="https://geo.mirror.pkgbuild.com/core/os/x86_64" |
| 21 | + KERNEL_PKG=$(curl -sL "$MIRROR/" | grep -oP 'linux-[0-9][^"]*-x86_64\.pkg\.tar\.zst' | grep -v headers | sort -V | tail -1) |
| 22 | + [ -z "$KERNEL_PKG" ] && { echo "Error: Could not find kernel package"; exit 1; } |
| 23 | + curl -sL -o /tmp/arch-kernel.pkg.tar.zst "$MIRROR/$KERNEL_PKG" |
| 24 | + zstd -d /tmp/arch-kernel.pkg.tar.zst -o /tmp/arch-kernel.pkg.tar 2>/dev/null || unzstd /tmp/arch-kernel.pkg.tar.zst -o /tmp/arch-kernel.pkg.tar |
| 25 | + VMLINUZ_PATH=$(tar -tf /tmp/arch-kernel.pkg.tar | grep 'vmlinuz$' | head -1) |
| 26 | + tar -xf /tmp/arch-kernel.pkg.tar -C /tmp "$VMLINUZ_PATH" |
| 27 | + mv "/tmp/$VMLINUZ_PATH" /tmp/arch-vmlinuz |
| 28 | + rm -rf /tmp/usr /tmp/arch-kernel.pkg.tar /tmp/arch-kernel.pkg.tar.zst |
| 29 | +fi |
| 30 | +cp /tmp/arch-vmlinuz "$SMACK_DIR/kernel/vmlinuz" |
| 31 | + |
| 32 | +# Setup busybox |
| 33 | +BUSYBOX=/tmp/busybox |
| 34 | +[ -f "$BUSYBOX" ] || curl -sL -o "$BUSYBOX" https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox |
| 35 | +chmod +x "$BUSYBOX" |
| 36 | +cp "$BUSYBOX" "$SMACK_DIR/rootfs/bin/" |
| 37 | +(cd "$SMACK_DIR/rootfs/bin" && "$BUSYBOX" --list | xargs -I{} ln -sf busybox {} 2>/dev/null) |
| 38 | + |
| 39 | +# Copy required libraries |
| 40 | +for lib in ld-linux-x86-64.so.2 libc.so.6 libm.so.6 libgcc_s.so.1 libpthread.so.0 libdl.so.2 librt.so.1; do |
| 41 | + path=$(ldconfig -p | grep "$lib" | head -1 | awk '{print $NF}') |
| 42 | + [ -n "$path" ] && [ -f "$path" ] && cp -L "$path" "$SMACK_DIR/rootfs/lib64/" 2>/dev/null || true |
| 43 | +done |
| 44 | + |
| 45 | +# Create minimal config files |
| 46 | +echo -e "root:x:0:0:root:/root:/bin/sh\nnobody:x:65534:65534:nobody:/nonexistent:/bin/sh" > "$SMACK_DIR/rootfs/etc/passwd" |
| 47 | +echo -e "root:x:0:\nnobody:x:65534:" > "$SMACK_DIR/rootfs/etc/group" |
| 48 | +touch "$SMACK_DIR/rootfs/etc/mtab" |
| 49 | + |
| 50 | +# Copy GNU tests |
| 51 | +cp -r "$GNU_DIR/tests" "$SMACK_DIR/rootfs/gnu/" |
| 52 | + |
| 53 | +# Create init script |
| 54 | +cat > "$SMACK_DIR/rootfs/init" << 'INIT' |
| 55 | +#!/bin/sh |
| 56 | +mount -t proc proc /proc |
| 57 | +mount -t sysfs sys /sys |
| 58 | +mount -t smackfs smackfs /sys/fs/smackfs 2>/dev/null || true |
| 59 | +if [ -d /sys/fs/smackfs ]; then |
| 60 | + echo "_" > /proc/self/attr/current 2>/dev/null || true |
| 61 | + echo "_ _ rwxat" > /sys/fs/smackfs/load 2>/dev/null || true |
| 62 | +fi |
| 63 | +mount -t devtmpfs devtmpfs /dev 2>/dev/null || true |
| 64 | +ln -sf /proc/mounts /etc/mtab |
| 65 | +mkdir -p /tmp && mount -t tmpfs tmpfs /tmp |
| 66 | +chmod 1777 /tmp |
| 67 | +export PATH="/bin:$PATH" srcdir="/gnu" LD_LIBRARY_PATH="/lib64" |
| 68 | +cd /gnu/tests |
| 69 | +sh "$TEST_SCRIPT" |
| 70 | +echo "EXIT:$?" |
| 71 | +poweroff -f |
| 72 | +INIT |
| 73 | +chmod +x "$SMACK_DIR/rootfs/init" |
| 74 | + |
| 75 | +# Build utilities with SMACK support (only ls has SMACK support for now) |
| 76 | +# TODO: When other utilities have SMACK support, build: ls id mkdir mknod mkfifo |
| 77 | +echo "Building utilities with SMACK support..." |
| 78 | +cargo build --release --manifest-path="$REPO_DIR/Cargo.toml" --package uu_ls --bin ls --features uu_ls/smack |
| 79 | + |
| 80 | +# Find SMACK tests |
| 81 | +SMACK_TESTS=$(grep -l 'require_smack_' -r "$GNU_DIR/tests/" 2>/dev/null || true) |
| 82 | +[ -z "$SMACK_TESTS" ] && { echo "No SMACK tests found"; exit 0; } |
| 83 | + |
| 84 | +echo "Found $(echo "$SMACK_TESTS" | wc -l) SMACK tests" |
| 85 | + |
| 86 | +# Create output directory |
| 87 | +rm -rf "$OUTPUT_DIR" |
| 88 | +mkdir -p "$OUTPUT_DIR" |
| 89 | + |
| 90 | +# Run each test |
| 91 | +for TEST_PATH in $SMACK_TESTS; do |
| 92 | + TEST_REL="${TEST_PATH#"$GNU_DIR"/tests/}" |
| 93 | + TEST_DIR=$(dirname "$TEST_REL") |
| 94 | + TEST_NAME=$(basename "$TEST_REL" .sh) |
| 95 | + |
| 96 | + echo "Running: $TEST_REL" |
| 97 | + |
| 98 | + # Create working copy |
| 99 | + WORK="/tmp/smack-test-$$" |
| 100 | + rm -rf "$WORK" "$WORK.gz" |
| 101 | + cp -a "$SMACK_DIR/rootfs" "$WORK" |
| 102 | + |
| 103 | + # Copy built utilities (only ls has SMACK support for now) |
| 104 | + # TODO: When other utilities have SMACK support, use: |
| 105 | + # for U in ls id mkdir mknod mkfifo; do cp "$REPO_DIR/target/release/$U" "$WORK/bin/$U"; done |
| 106 | + rm -f "$WORK/bin/ls" |
| 107 | + cp "$REPO_DIR/target/release/ls" "$WORK/bin/ls" |
| 108 | + |
| 109 | + # Set test script path |
| 110 | + sed -i "s|\$TEST_SCRIPT|$TEST_REL|g" "$WORK/init" |
| 111 | + |
| 112 | + # Build initramfs and run |
| 113 | + (cd "$WORK" && find . | cpio -o -H newc 2>/dev/null | gzip > "$WORK.gz") |
| 114 | + |
| 115 | + OUTPUT=$(timeout 120 qemu-system-x86_64 \ |
| 116 | + -kernel "$SMACK_DIR/kernel/vmlinuz" \ |
| 117 | + -initrd "$WORK.gz" \ |
| 118 | + -append "console=ttyS0 quiet panic=-1 security=smack lsm=smack" \ |
| 119 | + -nographic -m 256M -no-reboot 2>&1) || true |
| 120 | + |
| 121 | + # Determine result |
| 122 | + if echo "$OUTPUT" | grep -q "EXIT:0"; then |
| 123 | + RESULT="PASS"; EXIT_STATUS=0 |
| 124 | + elif echo "$OUTPUT" | grep -q "EXIT:77"; then |
| 125 | + RESULT="SKIP"; EXIT_STATUS=77 |
| 126 | + else |
| 127 | + RESULT="FAIL"; EXIT_STATUS=1 |
| 128 | + fi |
| 129 | + |
| 130 | + echo " $RESULT: $TEST_REL" |
| 131 | + |
| 132 | + # Create log file for gnu-json-result.py |
| 133 | + mkdir -p "$OUTPUT_DIR/$TEST_DIR" |
| 134 | + echo "$OUTPUT" > "$OUTPUT_DIR/$TEST_DIR/$TEST_NAME.log" |
| 135 | + echo "" >> "$OUTPUT_DIR/$TEST_DIR/$TEST_NAME.log" |
| 136 | + echo "$RESULT $TEST_NAME.sh (exit status: $EXIT_STATUS)" >> "$OUTPUT_DIR/$TEST_DIR/$TEST_NAME.log" |
| 137 | + |
| 138 | + rm -rf "$WORK" "$WORK.gz" |
| 139 | +done |
| 140 | + |
| 141 | +echo "Done. Results in $OUTPUT_DIR" |
0 commit comments