Volt VMM (Neutron Stardust): source-available under AGPSL v5.0
KVM-based microVMM for the Volt platform: - Sub-second VM boot times - Minimal memory footprint - Landlock LSM + seccomp security - Virtio device support - Custom kernel management Copyright (c) Armored Gates LLC. All rights reserved. Licensed under AGPSL v5.0
This commit is contained in:
291
scripts/build-rootfs.sh
Executable file
291
scripts/build-rootfs.sh
Executable file
@@ -0,0 +1,291 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# build-rootfs.sh - Create a minimal Alpine rootfs for Volt testing
|
||||
#
|
||||
# This script creates a small, fast-booting root filesystem suitable
|
||||
# for microVM testing. Uses Alpine Linux for its minimal footprint.
|
||||
#
|
||||
# Requirements:
|
||||
# - curl, tar
|
||||
# - e2fsprogs (mkfs.ext4) or squashfs-tools (mksquashfs)
|
||||
# - Optional: sudo (for proper permissions)
|
||||
#
|
||||
# Output: images/alpine-rootfs.ext4 (or .squashfs)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
BUILD_DIR="${PROJECT_DIR}/.build/rootfs"
|
||||
OUTPUT_DIR="${PROJECT_DIR}/images"
|
||||
|
||||
# Alpine version
|
||||
ALPINE_VERSION="${ALPINE_VERSION:-3.19}"
|
||||
ALPINE_RELEASE="${ALPINE_RELEASE:-3.19.1}"
|
||||
ALPINE_ARCH="x86_64"
|
||||
ALPINE_URL="https://dl-cdn.alpinelinux.org/alpine/v${ALPINE_VERSION}/releases/${ALPINE_ARCH}/alpine-minirootfs-${ALPINE_RELEASE}-${ALPINE_ARCH}.tar.gz"
|
||||
|
||||
# Image settings
|
||||
IMAGE_FORMAT="${IMAGE_FORMAT:-ext4}" # ext4 or squashfs
|
||||
IMAGE_SIZE_MB="${IMAGE_SIZE_MB:-64}" # Size for ext4 images
|
||||
IMAGE_NAME="alpine-rootfs"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${GREEN}[+]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||||
error() { echo -e "${RED}[✗]${NC} $*"; exit 1; }
|
||||
|
||||
check_dependencies() {
|
||||
log "Checking dependencies..."
|
||||
|
||||
local deps=(curl tar)
|
||||
|
||||
case "$IMAGE_FORMAT" in
|
||||
ext4) deps+=(mkfs.ext4) ;;
|
||||
squashfs) deps+=(mksquashfs) ;;
|
||||
*) error "Unknown format: $IMAGE_FORMAT" ;;
|
||||
esac
|
||||
|
||||
for dep in "${deps[@]}"; do
|
||||
if ! command -v "$dep" &>/dev/null; then
|
||||
error "Missing dependency: $dep"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
download_alpine() {
|
||||
log "Downloading Alpine minirootfs ${ALPINE_RELEASE}..."
|
||||
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
local tarball="${BUILD_DIR}/alpine-minirootfs.tar.gz"
|
||||
if [[ ! -f "$tarball" ]]; then
|
||||
curl -L -o "$tarball" "$ALPINE_URL"
|
||||
else
|
||||
log "Using cached download"
|
||||
fi
|
||||
}
|
||||
|
||||
extract_rootfs() {
|
||||
log "Extracting rootfs..."
|
||||
|
||||
local rootfs="${BUILD_DIR}/rootfs"
|
||||
rm -rf "$rootfs"
|
||||
mkdir -p "$rootfs"
|
||||
|
||||
# Extract (needs root for proper permissions, but works without)
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
tar xzf "${BUILD_DIR}/alpine-minirootfs.tar.gz" -C "$rootfs"
|
||||
else
|
||||
# Fakeroot alternative or just extract
|
||||
tar xzf "${BUILD_DIR}/alpine-minirootfs.tar.gz" -C "$rootfs" 2>/dev/null || \
|
||||
tar xzf "${BUILD_DIR}/alpine-minirootfs.tar.gz" -C "$rootfs" --no-same-owner
|
||||
warn "Extracted without root - some permissions may be incorrect"
|
||||
fi
|
||||
}
|
||||
|
||||
customize_rootfs() {
|
||||
log "Customizing rootfs for microVM boot..."
|
||||
|
||||
local rootfs="${BUILD_DIR}/rootfs"
|
||||
|
||||
# Create init script for fast boot
|
||||
cat > "${rootfs}/init" << 'INIT'
|
||||
#!/bin/sh
|
||||
# Volt microVM init
|
||||
|
||||
# Mount essential filesystems
|
||||
mount -t proc proc /proc
|
||||
mount -t sysfs sys /sys
|
||||
mount -t devtmpfs dev /dev
|
||||
|
||||
# Set hostname
|
||||
hostname volt-vmm-vm
|
||||
|
||||
# Print boot message
|
||||
echo ""
|
||||
echo "======================================"
|
||||
echo " Volt microVM booted!"
|
||||
echo " Alpine Linux $(cat /etc/alpine-release)"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
|
||||
# Show boot time if available
|
||||
if [ -f /proc/uptime ]; then
|
||||
uptime=$(cut -d' ' -f1 /proc/uptime)
|
||||
echo "Boot time: ${uptime}s"
|
||||
fi
|
||||
|
||||
# Start shell
|
||||
exec /bin/sh
|
||||
INIT
|
||||
chmod +x "${rootfs}/init"
|
||||
|
||||
# Create minimal inittab
|
||||
cat > "${rootfs}/etc/inittab" << 'EOF'
|
||||
::sysinit:/etc/init.d/rcS
|
||||
::respawn:-/bin/sh
|
||||
ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100
|
||||
::shutdown:/bin/umount -a -r
|
||||
EOF
|
||||
|
||||
# Configure serial console
|
||||
mkdir -p "${rootfs}/etc/init.d"
|
||||
cat > "${rootfs}/etc/init.d/rcS" << 'EOF'
|
||||
#!/bin/sh
|
||||
mount -t proc proc /proc
|
||||
mount -t sysfs sys /sys
|
||||
mount -t devtmpfs dev /dev
|
||||
hostname volt-vmm-vm
|
||||
EOF
|
||||
chmod +x "${rootfs}/etc/init.d/rcS"
|
||||
|
||||
# Set up basic networking config
|
||||
mkdir -p "${rootfs}/etc/network"
|
||||
cat > "${rootfs}/etc/network/interfaces" << 'EOF'
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
auto eth0
|
||||
iface eth0 inet dhcp
|
||||
EOF
|
||||
|
||||
# Disable unnecessary services
|
||||
rm -f "${rootfs}/etc/init.d/hwclock"
|
||||
rm -f "${rootfs}/etc/init.d/hwdrivers"
|
||||
|
||||
# Create fstab
|
||||
cat > "${rootfs}/etc/fstab" << 'EOF'
|
||||
/dev/vda / ext4 defaults,noatime 0 1
|
||||
proc /proc proc defaults 0 0
|
||||
sys /sys sysfs defaults 0 0
|
||||
devpts /dev/pts devpts defaults 0 0
|
||||
EOF
|
||||
|
||||
log "Rootfs customized for fast boot"
|
||||
}
|
||||
|
||||
create_ext4_image() {
|
||||
log "Creating ext4 image (${IMAGE_SIZE_MB}MB)..."
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
local image="${OUTPUT_DIR}/${IMAGE_NAME}.ext4"
|
||||
local rootfs="${BUILD_DIR}/rootfs"
|
||||
|
||||
# Create sparse file
|
||||
dd if=/dev/zero of="$image" bs=1M count=0 seek="$IMAGE_SIZE_MB" 2>/dev/null
|
||||
|
||||
# Format
|
||||
mkfs.ext4 -F -L rootfs -O ^metadata_csum "$image" >/dev/null
|
||||
|
||||
# Mount and copy (requires root)
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
local mnt="${BUILD_DIR}/mnt"
|
||||
mkdir -p "$mnt"
|
||||
mount -o loop "$image" "$mnt"
|
||||
cp -a "${rootfs}/." "$mnt/"
|
||||
umount "$mnt"
|
||||
else
|
||||
# Use debugfs to copy files (limited but works without root)
|
||||
warn "Creating image without root - using alternative method"
|
||||
|
||||
# Create a tar and extract into image using e2tools or fuse
|
||||
if command -v e2cp &>/dev/null; then
|
||||
# Use e2tools
|
||||
find "$rootfs" -type f | while read -r file; do
|
||||
local dest="${file#$rootfs}"
|
||||
e2cp "$file" "$image:$dest" 2>/dev/null || true
|
||||
done
|
||||
else
|
||||
warn "e2fsprogs-extra not available - image will be empty"
|
||||
warn "Install e2fsprogs-extra or run as root for full rootfs"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "$image"
|
||||
}
|
||||
|
||||
create_squashfs_image() {
|
||||
log "Creating squashfs image..."
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
local image="${OUTPUT_DIR}/${IMAGE_NAME}.squashfs"
|
||||
local rootfs="${BUILD_DIR}/rootfs"
|
||||
|
||||
mksquashfs "$rootfs" "$image" \
|
||||
-comp zstd \
|
||||
-Xcompression-level 19 \
|
||||
-noappend \
|
||||
-quiet
|
||||
|
||||
echo "$image"
|
||||
}
|
||||
|
||||
create_image() {
|
||||
local image
|
||||
|
||||
case "$IMAGE_FORMAT" in
|
||||
ext4) image=$(create_ext4_image) ;;
|
||||
squashfs) image=$(create_squashfs_image) ;;
|
||||
esac
|
||||
|
||||
echo "$image"
|
||||
}
|
||||
|
||||
show_stats() {
|
||||
local image="$1"
|
||||
|
||||
log "Rootfs image created successfully!"
|
||||
echo ""
|
||||
echo " Path: $image"
|
||||
echo " Size: $(du -h "$image" | cut -f1)"
|
||||
echo " Format: $IMAGE_FORMAT"
|
||||
echo " Base: Alpine Linux ${ALPINE_RELEASE}"
|
||||
echo ""
|
||||
echo "To use with Volt:"
|
||||
echo " volt-vmm --kernel kernels/vmlinux --rootfs $image"
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--format)
|
||||
IMAGE_FORMAT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--size)
|
||||
IMAGE_SIZE_MB="$2"
|
||||
shift 2
|
||||
;;
|
||||
--help)
|
||||
echo "Usage: $0 [--format ext4|squashfs] [--size MB]"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
error "Unknown option: $1"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Main
|
||||
main() {
|
||||
log "Building Volt test rootfs"
|
||||
echo ""
|
||||
|
||||
check_dependencies
|
||||
download_alpine
|
||||
extract_rootfs
|
||||
customize_rootfs
|
||||
|
||||
local image
|
||||
image=$(create_image)
|
||||
|
||||
show_stats "$image"
|
||||
}
|
||||
|
||||
main
|
||||
Reference in New Issue
Block a user