To reproduce
Have fakemachine, bubblewrap and hostname installed. Have this script in repro.sh:
#!/bin/sh
set -eu
bwrap --dev-bind / / hostname
Make it executable, and run it under fakemachine:
$ chmod +x repro.sh
$ fakemachine -v "$(pwd)" "$(pwd)/repro.sh"
Expected result
hostname(1) is run inside the fake machine, and outputs fakemachine.
Actual result
$ fakemachine -v "$(pwd)" "$(pwd)/repro.sh"
Running /home/smcv/tmp/fm/repro.sh using kvm backend
bwrap: pivot_root: Invalid argument
pivot_root(2) documents no fewer than six reasons why it can fail with EINVAL. After some head scratching, I isolated this to: "The current root is on the rootfs (initial ramfs) mount".
(This simplified reproducer runs https://github.com/containers/bubblewrap, but what I actually wanted to do was to run podman, and that fails similarly.)
Workaround
Have this in make-root-pivotable.sh:
#!/bin/sh
# Copyright 2026 Collabora Ltd
# SPDX-License-Identifier: MIT
set -eu
#set -x
# fakemachine runs our payload code with the initramfs as the root,
# and "The rootfs (initial ramfs) cannot be pivot_root()ed" — pivot_root(2).
# To work around this, we duplicate the entire fakemachine filesystem,
# except for the rootfs itself and /mnt, into /mnt.
install -d /mnt
mount -t tmpfs -o mode=0755 workaround /mnt
for member in /*; do
if [ "$member" = /mnt ]; then
install -d "/mnt/$member"
elif [ -L "$member" ]; then
ln -fns -- "$(readlink -- "$member")" "/mnt/$member"
elif [ -d "$member" ]; then
install -d "/mnt/$member"
mount --rbind -- "$member" "/mnt/$member"
else
: > "/mnt/$member"
mount --rbind -- "$member" "/mnt/$member"
fi
done
exec unshare -m -R /mnt -w "$(pwd)" -- "$@"
Then run
$ chmod +x repro.sh make-root-pivotable.sh
fakemachine -v "$(pwd)" "$(pwd)/make-root-pivotable.sh" "$(pwd)/repro.sh"
This creates a new filesystem hierarchy in /mnt, creates a new mount namespace, pivots the root into it, and then runs the payload command that I originally wanted to run inside that namespace. Quite a tower of abstractions, but it works.
Possible solution (untested)
It would be great if fakemachine could do this internally, or do something equivalent but more efficient internally. Perhaps instead of running directly from the initramfs, it could behave a little bit more like a distro initramfs:
- create a directory like
/root and mount a tmpfs on it
- copy or bind-mount the various
/etc files into /root
- mount static volumes into
/root instead of the rootfs
- mount hard-coded filesystems like
/proc and /sys into /root, too
- in
/init, instead of exec /lib/systemd/systemd, behave more like the examples in pivot_root(8), pivoting into the new root before running systemd
To reproduce
Have
fakemachine,bubblewrapandhostnameinstalled. Have this script inrepro.sh:Make it executable, and run it under fakemachine:
Expected result
hostname(1)is run inside the fake machine, and outputsfakemachine.Actual result
pivot_root(2)documents no fewer than six reasons why it can fail withEINVAL. After some head scratching, I isolated this to: "The current root is on the rootfs (initial ramfs) mount".(This simplified reproducer runs https://github.com/containers/bubblewrap, but what I actually wanted to do was to run
podman, and that fails similarly.)Workaround
Have this in
make-root-pivotable.sh:Then run
This creates a new filesystem hierarchy in
/mnt, creates a new mount namespace, pivots the root into it, and then runs the payload command that I originally wanted to run inside that namespace. Quite a tower of abstractions, but it works.Possible solution (untested)
It would be great if fakemachine could do this internally, or do something equivalent but more efficient internally. Perhaps instead of running directly from the initramfs, it could behave a little bit more like a distro initramfs:
/rootand mount a tmpfs on it/etcfiles into/root/rootinstead of the rootfs/procand/sysinto/root, too/init, instead ofexec /lib/systemd/systemd, behave more like the examples inpivot_root(8), pivoting into the new root before running systemd