NetSurf: what GTK's images and libcurl's resolver needed
NetSurf (GTK 3) now starts under X and loads pages over HTTPS. Every icon GTK draws goes gdk-pixbuf -> glycin -> a loader process -> a sealed memfd -> GMappedFile, and each hop found something missing: - mkaroot runs Alpine's package triggers in the root under bubblewrap. --no-scripts had skipped them, so there was no MIME cache, pixbuf loader list, compiled GSettings schemas, GIO module cache, font cache or busybox applet links. - /usr/bin/bwrap is a stand-in that parses bubblewrap's options and runs the program with no sandbox: glycin starts its loaders only through bwrap, and real bwrap needs namespaces. schos-base replaces bubblewrap for it. - ioctl: FIOCLEX, FIONCLEX, FIONBIO and FIOASYNC on any descriptor (Rust's std uses them; pipes and sockets answered ENOTTY). - capget/capset: root has every capability, others none. - socketpair accepts SOCK_DGRAM and SOCK_SEQPACKET, carried as a stream. - waitpid and setpgid match children against the thread group leader, so a thread can wait for the process it spawned (ECHILD before). - memfd: fstat reports the ftruncate length, not the page-rounded one; file seals (F_ADD_SEALS, F_GET_SEALS, MFD_ALLOW_SEALING) are recorded and enforced on later writes, truncation and writable mappings; MFD_CLOEXEC is honoured; read-only MAP_PRIVATE maps are allowed. - recvfrom honours O_NONBLOCK and MSG_DONTWAIT. musl's resolver reads its socket until EAGAIN and blocked for ever after the first DNS answer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
f4d7f9100d
commit
393bf674f5
11 changed files with 251 additions and 21 deletions
|
|
@ -168,6 +168,49 @@ Section "ServerLayout"
|
|||
EndSection
|
||||
XEOF
|
||||
|
||||
# bwrap, without the sandbox. glycin -- which loads every image gdk-pixbuf is
|
||||
# asked for, so every GTK icon -- runs its loaders under bubblewrap and has no
|
||||
# other way to start them. Real bwrap needs user and mount namespaces, which
|
||||
# SchOS does not have. This takes the same arguments, keeps what they say
|
||||
# about the environment and the directory, and runs the program as it is:
|
||||
# no isolation at all, and it says so in its name. It replaces bubblewrap's own
|
||||
# /usr/bin/bwrap, which glycin depends on and which cannot run here.
|
||||
mkdir -p "$P/usr/bin"
|
||||
cat > "$P/usr/bin/bwrap" <<'BEOF'
|
||||
#!/bin/sh
|
||||
# SchOS: bubblewrap's command line, with no sandbox behind it. SchOS has no
|
||||
# namespaces; what the options say about the environment and the working
|
||||
# directory is honoured, and everything about isolation is ignored.
|
||||
clear= envs=
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--clearenv) clear=1; shift ;;
|
||||
--setenv) envs="$envs $2=$3"; shift 3 ;;
|
||||
--unsetenv) unset "$2"; shift 2 ;;
|
||||
--chdir) cd "$2" || exit 1; shift 2 ;;
|
||||
--bind|--bind-try|--ro-bind|--ro-bind-try|--dev-bind|--dev-bind-try|\
|
||||
--symlink|--bind-data|--ro-bind-data|--file|--chmod) shift 3 ;;
|
||||
--dev|--proc|--tmpfs|--dir|--mqueue|--remount-ro|--seccomp|--add-seccomp-fd|\
|
||||
--uid|--gid|--hostname|--cap-add|--cap-drop|--lock-file|--sync-fd|\
|
||||
--info-fd|--json-status-fd|--block-fd|--userns-block-fd|--perms|--size|\
|
||||
--argv0|--exec-label|--file-label|--userns|--userns2|--pidns) shift 2 ;;
|
||||
--unshare-*|--share-net|--die-with-parent|--new-session|--as-pid-1|\
|
||||
--disable-userns|--assert-userns-disabled) shift ;;
|
||||
--) shift; break ;;
|
||||
-*) echo "bwrap: $1: not understood (SchOS runs bwrap without a sandbox)" >&2
|
||||
exit 1 ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
[ $# -gt 0 ] || { echo "bwrap: no command" >&2; exit 1; }
|
||||
# shellcheck disable=SC2086
|
||||
if [ -n "$clear" ]; then exec env -i PATH="$PATH" $envs "$@"; fi
|
||||
# shellcheck disable=SC2086
|
||||
[ -n "$envs" ] && exec env $envs "$@"
|
||||
exec "$@"
|
||||
BEOF
|
||||
chmod 755 "$P/usr/bin/bwrap"
|
||||
|
||||
# Mount points and runtime directories are alpine-baselayout's; an empty
|
||||
# directory here only duplicates it.
|
||||
for d in dev proc sys run tmp srv; do
|
||||
|
|
@ -197,7 +240,7 @@ cp "$PKG/post-install" "$PKG/post-upgrade"
|
|||
--info url:https://github.com/Nexoniarz/SchOS \
|
||||
--info origin:schos-base \
|
||||
--info "depends:alpine-baselayout busybox musl apk-tools zsh" \
|
||||
--info "replaces:alpine-release alpine-baselayout zsh" \
|
||||
--info "replaces:alpine-release alpine-baselayout zsh bubblewrap" \
|
||||
--files "$P" \
|
||||
--script "post-install:$PKG/post-install" \
|
||||
--script "post-upgrade:$PKG/post-upgrade" \
|
||||
|
|
@ -234,6 +277,41 @@ ROOT_PI=$ROOT sh -c '
|
|||
done
|
||||
grep -qw schos "$ROOT_PI/etc/hosts" || printf "127.0.1.1\tschos\n" >> "$ROOT_PI/etc/hosts"'
|
||||
|
||||
# The triggers --no-scripts skipped, and they are not optional: they build the
|
||||
# caches half the desktop reads. Without shared-mime-info's, GLib cannot tell a
|
||||
# PNG from anything else, so gdk-pixbuf -- which hands images to glycin by MIME
|
||||
# type -- refused every icon and NetSurf died on its first toolbar button.
|
||||
# Likewise gdk-pixbuf's loader list, GLib's compiled schemas and GIO modules,
|
||||
# GTK's input methods, the font caches and busybox's applet links.
|
||||
#
|
||||
# They run in the root as /, which is what bubblewrap is for: no chroot, so no
|
||||
# root needed. apk names each script by the hex form of the checksum the
|
||||
# triggers database keys in base64, and passes the paths it watches as
|
||||
# arguments, globs expanded.
|
||||
if command -v bwrap >/dev/null 2>&1; then
|
||||
TRIG=$(mktemp -d)
|
||||
tar -xzf "$ROOT/lib/apk/db/scripts.tar.gz" -C "$TRIG" --wildcards '*.trigger' 2>/dev/null || true
|
||||
mkdir -p "$ROOT/tmp"
|
||||
for t in "$TRIG"/*.trigger; do
|
||||
[ -e "$t" ] || continue
|
||||
hex=${t##*.X1}; hex=${hex%.trigger}
|
||||
q=Q1$(python3 -c 'import base64,sys; print(base64.b64encode(bytes.fromhex(sys.argv[1])).decode())' "$hex")
|
||||
paths=$(grep "^$q " "$ROOT/lib/apk/db/triggers" | cut -d' ' -f2-)
|
||||
cp "$t" "$ROOT/tmp/apk-trigger"
|
||||
# --clearenv: the host's environment is not the root's. A Nix shell
|
||||
# exports GDK_PIXBUF_MODULE_FILE, and the loader cache went there.
|
||||
bwrap --bind "$ROOT" / --dev /dev --proc /proc --clearenv \
|
||||
--setenv PATH /usr/sbin:/usr/bin:/sbin:/bin --setenv HOME /root \
|
||||
/bin/sh -c 'a=; for g in '"$paths"'; do for p in $g; do [ -e "$p" ] && a="$a $p"; done; done
|
||||
exec /bin/sh /tmp/apk-trigger $a' \
|
||||
|| echo "mkaroot: trigger $(basename "$t") failed" >&2
|
||||
rm -f "$ROOT/tmp/apk-trigger"
|
||||
done
|
||||
rm -rf "$TRIG"
|
||||
else
|
||||
echo "mkaroot: no bwrap -- triggers not run; image and MIME caches are missing" >&2
|
||||
fi
|
||||
|
||||
# The shared scratch directories, world-writable and sticky as baselayout
|
||||
# means them. apk running as an ordinary user on the build host lands them as
|
||||
# 0755, and with the kernel reporting real modes that is what tmux and every
|
||||
|
|
|
|||
Loading…
Reference in a new issue