mirror of
https://gitlab.redox-os.org/redox-os/redox.git
synced 2026-11-05 04:06:38 +08:00
share host directories with Redox from 'make qemu'
'make qemu share=<dir>' (or share="tag=dir ...") starts one virtiofsd per share and adds the matching vhost-user-fs devices, so the guest sees /scheme/virtio-fsd.<tag>. podman_bootstrap.sh installs virtiofsd, and the selftest recipe builds from the base source. Signed-off-by: MJ Pooladkhay <mj@pooladkhay.com>
This commit is contained in:
parent
7aaea1939c
commit
45bb425a4b
54
mk/qemu.mk
54
mk/qemu.mk
@ -189,6 +189,56 @@ else
|
|||||||
QEMUFLAGS+=-machine $(QEMU_MACHINE)
|
QEMUFLAGS+=-machine $(QEMU_MACHINE)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# Share host directories with the guest through virtio-fs.
|
||||||
|
# make qemu share=/path/to/dir # one share, tag is "host" by default
|
||||||
|
# make qemu share="tag=/path/to/dir" # one share, tag is specified as "tag"
|
||||||
|
# make qemu share=/path/a share_tag=tag # one share, tag is specified as "tag"
|
||||||
|
# make qemu share="tag_1=/path/a tag_2=/path/b" # several shares: tag=dir ...
|
||||||
|
# make qemu share=redox # this build tree, tag "redox"
|
||||||
|
# make qemu share="redox=/other/path" # ordinary share, tag "redox"
|
||||||
|
#
|
||||||
|
# Each share gets its own host-side virtiofsd daemon (started and stopped by
|
||||||
|
# scripts/virtiofs-run.sh), a vhost-user-fs-pci device, and a virtio-fsd scheme
|
||||||
|
# process on Redox. In the guest, a share appears at /scheme/virtio-fsd.<tag>.
|
||||||
|
#
|
||||||
|
# WARNING: share=redox exposes the whole build tree to the guest, including
|
||||||
|
# the harddrive.img Redox is booted from, .git and .config. Writing to them
|
||||||
|
# from Redox can corrupt the build system.
|
||||||
|
share?=
|
||||||
|
share_tag?=host
|
||||||
|
share_sockdir?=$(BUILD)
|
||||||
|
QEMU_RUN=
|
||||||
|
ifneq ($(strip $(share)),)
|
||||||
|
share_norm:=$(foreach s,$(share),$(if $(filter redox,$(s)),redox=$(ROOT),$(s)))
|
||||||
|
# Normalise to a list of tag=dir entries.
|
||||||
|
ifeq ($(words $(share_norm)),1)
|
||||||
|
share_list:=$(if $(findstring =,$(share_norm)),$(share_norm),$(share_tag)=$(share_norm))
|
||||||
|
else
|
||||||
|
share_list:=$(share_norm)
|
||||||
|
share_bare:=$(foreach s,$(share_list),$(if $(findstring =,$(s)),,$(s)))
|
||||||
|
ifneq ($(strip $(share_bare)),)
|
||||||
|
# Keep the next line free of leading tabs: make would read it as a recipe.
|
||||||
|
$(error share: every entry must be tag=dir when sharing several directories (bad: $(share_bare)); directories containing spaces are not supported)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
share_idx:=$(shell seq 1 $(words $(share_list)))
|
||||||
|
vfs_entry=$(word $(1),$(share_list))
|
||||||
|
vfs_tag=$(firstword $(subst =, ,$(call vfs_entry,$(1))))
|
||||||
|
vfs_sock=$(share_sockdir)/virtiofsd-$(1).sock
|
||||||
|
|
||||||
|
# vhost-user needs guest RAM to be shared with the virtiofsd processes, and
|
||||||
|
# the NUMA memdev must be exactly as large as -m. One backend serves all
|
||||||
|
# shares.
|
||||||
|
ifeq ($(UNAME),Linux)
|
||||||
|
QEMUFLAGS+=-object memory-backend-memfd,id=mem,size=$(QEMU_MEM)M,share=on
|
||||||
|
else
|
||||||
|
QEMUFLAGS+=-object memory-backend-file,id=mem,size=$(QEMU_MEM)M,mem-path=/dev/shm,share=on
|
||||||
|
endif
|
||||||
|
QEMUFLAGS+=-numa node,memdev=mem
|
||||||
|
QEMUFLAGS+=$(foreach i,$(share_idx),-chardev socket,id=vfs$(i),path=$(call vfs_sock,$(i)) -device vhost-user-fs-pci,queue-size=1024,chardev=vfs$(i),tag=$(call vfs_tag,$(i)))
|
||||||
|
QEMU_RUN=scripts/virtiofs-run.sh $(foreach i,$(share_idx),--share "$(call vfs_sock,$(i))" "$(call vfs_entry,$(i))") --
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(audio),no)
|
ifeq ($(audio),no)
|
||||||
# No audio
|
# No audio
|
||||||
else ifeq ($(audio),ac97)
|
else ifeq ($(audio),ac97)
|
||||||
@ -383,9 +433,9 @@ $(BUILD)/qemu_uboot.rom:
|
|||||||
|
|
||||||
qemu: qemu-deps
|
qemu: qemu-deps
|
||||||
ifeq ($(wildcard $(MOUNT_DIR)),)
|
ifeq ($(wildcard $(MOUNT_DIR)),)
|
||||||
$(QEMU) $(QEMUFLAGS)
|
$(QEMU_RUN) $(QEMU) $(QEMUFLAGS)
|
||||||
else
|
else
|
||||||
@echo "\033[1;38;5;196mDisk is mounted. Unmounting the disk...\033[0m";
|
@echo "\033[1;38;5;196mDisk is mounted. Unmounting the disk...\033[0m";
|
||||||
$(MAKE) unmount
|
$(MAKE) unmount
|
||||||
$(QEMU) $(QEMUFLAGS)
|
$(QEMU_RUN) $(QEMU) $(QEMUFLAGS)
|
||||||
endif
|
endif
|
||||||
|
|||||||
@ -472,6 +472,110 @@ solus()
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# virtiofsd is needed on the host by `make qemu share=<dir>` to share
|
||||||
|
# directories with Redox (scripts/virtiofs-run.sh only looks for it).
|
||||||
|
###############################################################################
|
||||||
|
virtiofsd_find()
|
||||||
|
{
|
||||||
|
local c
|
||||||
|
for c in "${VIRTIOFSD:-}" virtiofsd /usr/libexec/virtiofsd /usr/lib/qemu/virtiofsd \
|
||||||
|
"${XDG_CACHE_HOME:-$HOME/.cache}/virtiofsd-redox/virtiofsd"; do
|
||||||
|
[ -n "$c" ] || continue
|
||||||
|
if [ -f "$c" ] && [ -x "$c" ]; then
|
||||||
|
echo "$c"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if command -v "$c" >/dev/null 2>&1; then
|
||||||
|
command -v "$c"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Download a static x86_64 virtiofsd build from the upstream CI into
|
||||||
|
# ~/.cache/virtiofsd-redox, after asking for confirmation.
|
||||||
|
# Override the source with VIRTIOFSD_URL.
|
||||||
|
###############################################################################
|
||||||
|
virtiofsd_download()
|
||||||
|
{
|
||||||
|
local url cache dest tmp answer
|
||||||
|
url="${VIRTIOFSD_URL:-https://gitlab.com/virtio-fs/virtiofsd/-/jobs/artifacts/main/raw/target/x86_64-unknown-linux-musl/release/virtiofsd?job=publish}"
|
||||||
|
cache="${XDG_CACHE_HOME:-$HOME/.cache}/virtiofsd-redox"
|
||||||
|
dest="$cache/virtiofsd"
|
||||||
|
|
||||||
|
if [ "$(uname -m)" != "x86_64" ]; then
|
||||||
|
echo "No prebuilt virtiofsd is available for $(uname -m)."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "A static virtiofsd build can be downloaded from"
|
||||||
|
echo " gitlab.com/virtio-fs/virtiofsd (CI artifact of the main branch)"
|
||||||
|
echo "to $dest"
|
||||||
|
printf "Download it now? (y/N): "
|
||||||
|
read answer || answer=""
|
||||||
|
if ! echo "$answer" | grep -iq "^y"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$cache"
|
||||||
|
tmp="$(mktemp "$cache/virtiofsd.XXXXXX")"
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
curl -fL --retry 3 --retry-delay 1 -o "$tmp" "$url" || { rm -f "$tmp"; return 1; }
|
||||||
|
elif command -v wget >/dev/null 2>&1; then
|
||||||
|
wget -O "$tmp" "$url" || { rm -f "$tmp"; return 1; }
|
||||||
|
else
|
||||||
|
echo "Need curl or wget to download virtiofsd."
|
||||||
|
rm -f "$tmp"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ "$(head -c 4 "$tmp")" != "$(printf '\177ELF')" ]; then
|
||||||
|
echo "The download is not an ELF binary (see $url)."
|
||||||
|
rm -f "$tmp"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
chmod +x "$tmp"
|
||||||
|
mv -f "$tmp" "$dest"
|
||||||
|
echo "virtiofsd installed at $dest"
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# This function makes sure virtiofsd is available: it looks for an existing
|
||||||
|
# one, then tries the distribution package, then offers a download.
|
||||||
|
# It never fails the bootstrap, virtiofsd is only needed for `share=`.
|
||||||
|
# @params: $1 the package manager to use on Debian-based Linux
|
||||||
|
###############################################################################
|
||||||
|
virtiofsd_setup()
|
||||||
|
{
|
||||||
|
local found
|
||||||
|
echo "Checking for virtiofsd (used by 'make qemu share=<dir>')..."
|
||||||
|
if found="$(virtiofsd_find)"; then
|
||||||
|
echo "virtiofsd found at $found"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "virtiofsd not found."
|
||||||
|
|
||||||
|
if command -v pacman >/dev/null 2>&1; then
|
||||||
|
sudo pacman -S --needed virtiofsd || true
|
||||||
|
elif command -v dnf >/dev/null 2>&1; then
|
||||||
|
sudo dnf install virtiofsd || true
|
||||||
|
elif command -v apt-get >/dev/null 2>&1 && apt-cache show virtiofsd >/dev/null 2>&1; then
|
||||||
|
sudo "$1" install virtiofsd || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if found="$(virtiofsd_find)"; then
|
||||||
|
echo "virtiofsd found at $found"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if virtiofsd_download; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "Continuing without virtiofsd. 'make qemu share=<dir>' will not work until"
|
||||||
|
echo "it is installed (or set VIRTIOFSD=/path/to/virtiofsd), re-run this script"
|
||||||
|
echo "with -d to retry."
|
||||||
|
}
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# This function outlines the different options available for bootstrap
|
# This function outlines the different options available for bootstrap
|
||||||
######################################################################
|
######################################################################
|
||||||
@ -659,6 +763,11 @@ else
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Directory sharing with `make qemu share=<dir>` needs virtiofsd and a Linux host
|
||||||
|
if [ "$emulator" == "qemu" ] && [ "$(uname -s)" == "Linux" ]; then
|
||||||
|
virtiofsd_setup "$defpackman"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$dependenciesonly" = false ]; then
|
if [ "$dependenciesonly" = false ]; then
|
||||||
boot
|
boot
|
||||||
fi
|
fi
|
||||||
|
|||||||
7
recipes/tests/virtio-fsd-selftest/recipe.toml
Normal file
7
recipes/tests/virtio-fsd-selftest/recipe.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[source]
|
||||||
|
# The selftest is a member of the base workspace (base/selftests/virtio-fsd)
|
||||||
|
same_as = "../../core/base"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
template = "cargo"
|
||||||
|
cargopackages = ["virtio-fsd-selftest"]
|
||||||
124
scripts/virtiofs-run.sh
Executable file
124
scripts/virtiofs-run.sh
Executable file
@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Run a command (normally QEMU) next to one virtiofsd per shared host directory.
|
||||||
|
#
|
||||||
|
# virtiofs-run.sh --share <socket> <tag>=<dir> [--share ...] -- <command...>
|
||||||
|
#
|
||||||
|
# Used by `make qemu share=...` (see mk/qemu.mk). Each virtiofsd is started in
|
||||||
|
# the background and logs to <socket minus .sock>.log. All of them are stopped
|
||||||
|
# again when the command exits, fails to start, or is interrupted.
|
||||||
|
#
|
||||||
|
# virtiofsd is taken from $VIRTIOFSD, PATH, /usr/libexec, /usr/lib/qemu or
|
||||||
|
# ~/.cache/virtiofsd-redox. podman_bootstrap.sh installs it.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
die() {
|
||||||
|
echo "virtiofs: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "usage: $0 --share <socket> <tag>=<dir> [--share ...] -- <command...>" >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
SOCKS=()
|
||||||
|
SPECS=()
|
||||||
|
while [[ "${1:-}" == --share ]]; do
|
||||||
|
[[ $# -ge 3 ]] || usage
|
||||||
|
SOCKS+=("$2")
|
||||||
|
SPECS+=("$3")
|
||||||
|
shift 3
|
||||||
|
done
|
||||||
|
[[ ${#SOCKS[@]} -gt 0 && "${1:-}" == -- && $# -gt 1 ]] || usage
|
||||||
|
shift
|
||||||
|
|
||||||
|
# Validate everything before starting anything, so a mistake leaves nothing running
|
||||||
|
seen_tags=" "
|
||||||
|
for i in "${!SOCKS[@]}"; do
|
||||||
|
sock="${SOCKS[i]}" spec="${SPECS[i]}"
|
||||||
|
[[ "${spec}" == *=* ]] || die "share '${spec}': expected <tag>=<dir>"
|
||||||
|
tag="${spec%%=*}" dir="${spec#*=}"
|
||||||
|
[[ "${tag}" =~ ^[A-Za-z0-9._-]+$ ]] \
|
||||||
|
|| die "tag '${tag}': only letters, digits, '.', '_' and '-' are allowed (is '${spec}' a directory with an '=' in it? use tag=dir)"
|
||||||
|
[[ ${#tag} -le 36 ]] || die "tag '${tag}': longer than 36 bytes"
|
||||||
|
[[ "${seen_tags}" != *" ${tag} "* ]] || die "tag '${tag}' is used twice"
|
||||||
|
seen_tags+="${tag} "
|
||||||
|
[[ -n "${dir}" ]] || die "share '${tag}': empty directory"
|
||||||
|
[[ "${dir}" != *[[:space:]]* ]] \
|
||||||
|
|| die "share '${tag}': directories containing whitespace are not supported ('${dir}')"
|
||||||
|
[[ "${sock}" == *.sock ]] || die "socket '${sock}' must end in .sock"
|
||||||
|
# AF_UNIX sun_path is 108 bytes including the NUL
|
||||||
|
[[ ${#sock} -lt 108 ]] \
|
||||||
|
|| die "socket path too long (${#sock} bytes): ${sock}; use a shorter share_sockdir"
|
||||||
|
done
|
||||||
|
|
||||||
|
find_virtiofsd() {
|
||||||
|
local c
|
||||||
|
for c in "${VIRTIOFSD:-}" virtiofsd /usr/libexec/virtiofsd /usr/lib/qemu/virtiofsd \
|
||||||
|
"${XDG_CACHE_HOME:-${HOME:-/tmp}/.cache}/virtiofsd-redox/virtiofsd"; do
|
||||||
|
[[ -n "${c}" ]] && command -v "${c}" && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
VIRTIOFSD_BIN="$(find_virtiofsd)" \
|
||||||
|
|| die "virtiofsd not found. Install it (e.g. 'dnf install virtiofsd' or 'pacman -S virtiofsd'), run ./podman_bootstrap.sh -d, or set VIRTIOFSD=/path/to/virtiofsd"
|
||||||
|
|
||||||
|
PIDS=()
|
||||||
|
cleanup() {
|
||||||
|
trap - EXIT INT TERM
|
||||||
|
if [[ ${#PIDS[@]} -gt 0 ]]; then
|
||||||
|
kill "${PIDS[@]}" 2>/dev/null || true
|
||||||
|
wait "${PIDS[@]}" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
rm -f "${SOCKS[@]}" "${SOCKS[@]/%/.pid}"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
trap 'exit 130' INT
|
||||||
|
trap 'exit 143' TERM
|
||||||
|
|
||||||
|
resolved_dirs=" "
|
||||||
|
for i in "${!SOCKS[@]}"; do
|
||||||
|
sock="${SOCKS[i]}" spec="${SPECS[i]}"
|
||||||
|
mkdir -p "${spec#*=}" "$(dirname "${sock}")"
|
||||||
|
dir="$(cd "${spec#*=}" && pwd)"
|
||||||
|
if [[ "${resolved_dirs}" == *" ${dir} "* ]]; then
|
||||||
|
echo "virtiofs: warning: ${dir} is shared more than once; the instances do not share caches" >&2
|
||||||
|
fi
|
||||||
|
resolved_dirs+="${dir} "
|
||||||
|
rm -f "${sock}" "${sock}.pid"
|
||||||
|
|
||||||
|
# The guest root maps to the invoking user, so files created in the guest
|
||||||
|
# are owned by you on the host.
|
||||||
|
# --sandbox none avoids needing root or user namespaces.
|
||||||
|
"${VIRTIOFSD_BIN}" \
|
||||||
|
--socket-path="${sock}" \
|
||||||
|
--shared-dir "${dir}" \
|
||||||
|
--sandbox none \
|
||||||
|
--log-level warn \
|
||||||
|
--translate-uid="map:0:$(id -u):1" \
|
||||||
|
--translate-gid="map:0:$(id -g):1" \
|
||||||
|
</dev/null >"${sock%.sock}.log" 2>&1 &
|
||||||
|
PIDS+=("$!")
|
||||||
|
done
|
||||||
|
|
||||||
|
# Wait (up to ~5 seconds per share) until every socket exists
|
||||||
|
for i in "${!SOCKS[@]}"; do
|
||||||
|
sock="${SOCKS[i]}" tag="${SPECS[i]%%=*}" log="${SOCKS[i]%.sock}.log"
|
||||||
|
for _ in $(seq 50); do
|
||||||
|
[[ -S "${sock}" ]] && break
|
||||||
|
kill -0 "${PIDS[i]}" 2>/dev/null || break
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
if [[ ! -S "${sock}" ]]; then
|
||||||
|
echo "virtiofs: virtiofsd for '${tag}' did not create ${sock}; see ${log}" >&2
|
||||||
|
tail -n 20 "${log}" >&2 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "virtiofs: sharing '${tag}' = ${SPECS[i]#*=} (log: ${log})" >&2
|
||||||
|
done
|
||||||
|
|
||||||
|
# Run the command as a child, not with exec, so that the EXIT trap still runs
|
||||||
|
# and stops the virtiofsd processes once the command is done.
|
||||||
|
"$@"
|
||||||
Loading…
Reference in New Issue
Block a user