sane-sandboxed: tweak symlink caching to allow /run/current-system to be bind-mounted instead of symlinked

This commit is contained in:
Colin 2024-05-13 02:11:47 +00:00
parent 660ba94c7c
commit bd3e06982b
2 changed files with 36 additions and 14 deletions

View File

@ -117,7 +117,11 @@ let
"${builtins.unsafeDiscardStringContext config.environment.binsh}" = "bash";
"/usr/bin/env" = config.environment.usrbinenv;
"${builtins.unsafeDiscardStringContext config.environment.usrbinenv}" = "coreutils";
# "/run/current-system" = "${config.system.build.toplevel}";
# XXX: /run/current-system symlink can't be cached without forcing regular mass rebuilds:
# mount it as if it were a directory instead.
"/run/current-system" = "";
} // lib.optionalAttrs config.hardware.opengl.enable {
"/run/opengl-driver" = let
gl = config.hardware.opengl;

View File

@ -274,6 +274,30 @@ contains() {
return 1
}
# `readlinkOnce outVar path`: writes the link target to outVar
# or sets outVar="" if path isn't a link.
# unlink `derefOnce`, this only acts if the leaf of `path` is a symlink,
# not if it's an ordinary entry within a symlinked directory.
readlinkOnce() {
local outVar="$1"
local path="$2"
local linkTarget=
if [ -v "linkCache["$path"]" ]; then
linkTarget="${linkCache["$path"]}"
elif [ -L "$path" ]; then
# path is a link, but not in the cache
linkTarget="$(readlink "$path")"
# insert it into the cache, in case we traverse it again
linkCache["$path"]="$linkTarget"
else
# remember for later that this path doesn't represent a link.
# empty target is used to indicate a non-symlink (i.e. ordinary file/directory).
# i think a symlink can *technically* point to "" (via `symlink` syscall), but `ln -s` doesn't allow it
linkCache["$path"]=
fi
declare -g "$outVar"="$linkTarget"
}
# `derefOnce outVar path`: walks from `/` to `path` and derefs the first symlink it encounters.
# the dereferenced equivalent of `path` is written to `outVar`.
# the dereferenced path may yet contain more unresolved symlinks.
@ -281,7 +305,6 @@ contains() {
derefOnce() {
local outVar="$1"
local source="$2"
local flag="$3"
local target=
local walked=
@ -289,13 +312,7 @@ derefOnce() {
while [ -n "$_unwalked" ]; do
splitHead _head _unwalked "$_unwalked"
walked="$walked$_head"
local linkTarget="${linkCache["$walked"]}"
if [ -z "$linkTarget" ] && [ -L "$walked" ]; then
# path is a link, but not in the cache
linkTarget="$(readlink "$walked")"
# insert it into the cache, in case we traverse it again
linkCache["$walked"]="$linkTarget"
fi
readlinkOnce linkTarget "$walked"
if [ -n "$linkTarget" ]; then
target="$linkTarget$_unwalked"
break
@ -303,7 +320,7 @@ derefOnce() {
done
# make absolute
if [ "$flag" != '--no-canon' ] && [ -n "$target" ]; then
if [ -n "$target" ]; then
if [ "${target:0:1}" != "/" ]; then
# `walked` is a relative link.
# then, the link is relative to the parent directory of `walked`
@ -637,11 +654,12 @@ bwrapIngestPath() {
# may be possible to place ever mount in a subdir, and mount the super dir?
# or maybe configure remote mounts to somehow never hang.
# test -r "$1" && bwrapFlags+=("--dev-bind-try" "$1" "$1")
if [ -L "$1" ]; then
# N.B.: test specifically whether this path is a link, not whether it's a non-symlink under a symlink'd dir.
# this way, the filetype of this path is *always* the same both inside and outside the sandbox.
derefOnce _target "$1" '--no-canon'
bwrapFlags+=("--symlink" "$_target" "$1")
# N.B.: test specifically whether this path is a link, not whether it's a non-symlink under a symlink'd dir.
# this way, the filetype of this path is *always* the same both inside and outside the sandbox.
readlinkOnce linkTarget "$1"
if [ -n "$linkTarget" ]; then
bwrapFlags+=("--symlink" "$linkTarget" "$1")
else
bwrapFlags+=("--dev-bind-try" "$1" "$1")
fi