107 lines
3.5 KiB
Bash
Executable File
107 lines
3.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# Copyright © 2026 Apple Inc. and the Containerization project authors.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# https://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
# Builds the guest init filesystem (initfs.ext4) — and optionally a rootfs
|
|
# tar.gz for OCI image creation — from the compiled vminitd/vmexec binaries.
|
|
#
|
|
# Runs INSIDE the Linux dev container (invoked by the root Makefile's `init`
|
|
# target via linux_run on macOS, or directly on Linux). A real loop mount is
|
|
# preferred; when loop devices aren't available (an unprivileged CI container,
|
|
# or a dev VM without loop support) it falls back to `mke2fs -d`, which
|
|
# populates the filesystem without mounting. Both paths yield an equivalent
|
|
# ext4, so the build works whether or not the container is privileged.
|
|
#
|
|
# The rootfs layout below MUST stay in sync with cctl's InitImage rootfs
|
|
# (Sources/cctl/RootfsCommand.swift): directories bin/ sbin/ dev/ sys/
|
|
# proc/self/ run/ tmp/ mnt/ var/, sbin/vminitd + sbin/vmexec at mode 0755, and
|
|
# a proc/self/exe -> sbin/vminitd symlink ("hack for swift init's booting").
|
|
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
echo "usage: $0 --vminitd PATH --vmexec PATH --ext4 OUT.ext4 [--tar OUT.tar.gz] [--size 512M]" >&2
|
|
exit 2
|
|
}
|
|
|
|
VMINITD=
|
|
VMEXEC=
|
|
EXT4=
|
|
TAR=
|
|
SIZE=512M
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--vminitd) VMINITD=$2; shift 2 ;;
|
|
--vmexec) VMEXEC=$2; shift 2 ;;
|
|
--ext4) EXT4=$2; shift 2 ;;
|
|
--tar) TAR=$2; shift 2 ;;
|
|
--size) SIZE=$2; shift 2 ;;
|
|
*) usage ;;
|
|
esac
|
|
done
|
|
|
|
[ -n "$VMINITD" ] && [ -n "$VMEXEC" ] && [ -n "$EXT4" ] || usage
|
|
[ -f "$VMINITD" ] || { echo "ERROR: vminitd not found: $VMINITD" >&2; exit 1; }
|
|
[ -f "$VMEXEC" ] || { echo "ERROR: vmexec not found: $VMEXEC" >&2; exit 1; }
|
|
|
|
umask 022
|
|
STAGING=$(mktemp -d)
|
|
MNT=
|
|
cleanup() {
|
|
if [ -n "$MNT" ]; then
|
|
mountpoint -q "$MNT" 2>/dev/null && umount "$MNT" 2>/dev/null || true
|
|
rmdir "$MNT" 2>/dev/null || true
|
|
fi
|
|
rm -rf "$STAGING"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# Directory structure — keep in sync with RootfsCommand.directories.
|
|
for d in bin sbin dev sys proc/self run tmp mnt var; do
|
|
mkdir -p "$STAGING/$d"
|
|
done
|
|
install -m 0755 "$VMINITD" "$STAGING/sbin/vminitd"
|
|
install -m 0755 "$VMEXEC" "$STAGING/sbin/vmexec"
|
|
ln -sf sbin/vminitd "$STAGING/proc/self/exe"
|
|
|
|
mkdir -p "$(dirname "$EXT4")"
|
|
rm -f "$EXT4"
|
|
truncate -s "$SIZE" "$EXT4"
|
|
|
|
# Prefer a real loop mount; fall back to `mke2fs -d` when it isn't available.
|
|
if mkfs.ext4 -F -q "$EXT4" \
|
|
&& MNT=$(mktemp -d) \
|
|
&& mount -o loop "$EXT4" "$MNT" 2>/dev/null; then
|
|
echo "==> populating $EXT4 via loop mount"
|
|
cp -a "$STAGING"/. "$MNT"/
|
|
sync
|
|
umount "$MNT"
|
|
rmdir "$MNT"
|
|
MNT=
|
|
else
|
|
echo "==> loop mount unavailable; populating $EXT4 via mke2fs -d"
|
|
if [ -n "$MNT" ]; then rmdir "$MNT" 2>/dev/null || true; MNT=; fi
|
|
rm -f "$EXT4"
|
|
truncate -s "$SIZE" "$EXT4"
|
|
mkfs.ext4 -F -q -d "$STAGING" "$EXT4"
|
|
fi
|
|
echo "==> wrote initfs $EXT4"
|
|
|
|
if [ -n "$TAR" ]; then
|
|
mkdir -p "$(dirname "$TAR")"
|
|
rm -f "$TAR"
|
|
tar -czf "$TAR" -C "$STAGING" .
|
|
echo "==> wrote rootfs tar $TAR"
|
|
fi
|