in antlir/nspawn_in_subvol/plugins/shadow_paths.py [0:0]
def _copy_to_shadowed_root(subvol: Subvol, container_paths: Iterable[Path]):
originals_and_backups = [
(
subvol.path(p),
subvol.path(SHADOWED_PATHS_ROOT / p.strip_leading_slashes()),
)
for p in container_paths
]
# This is redundant with our other "no ambiguity" and "no aliasing"
# checks, so it should never be hit.
assert 1 == len(
{
len(originals_and_backups),
*(len(set(x)) for x in zip(*originals_and_backups)),
}
), originals_and_backups
try:
# We don't use `--reflink=always` because in some debug-only
# scenarios (see the description of the diff introducing this), it
# makes sense to allow shadowing paths that come from mounts -- and
# are thus both read-only, and possibly on a different FS.
#
# Falling back to `cp` incurs some I/O, but it should make no
# difference in practice -- it's a debug-only fall-back. In
# principle, we could fall back to a bind mount instead, but the
# implementation would be noticeably harder (especially the
# `librename_shadowed.so` bits).
#
# Future: The directories we make under don't have the original
# permissions. I'm punting on fixing this, since our general thesis
# is that build-time code is trusted.
subvol.run_as_root(
[
"sh",
"-uec",
"\n".join(
f"""\
b={backup.shell_quote()}
b_dir=$(dirname "$b")
mkdir -p "$b_dir"
cp --reflink=auto --preserve=all {orig.shell_quote()} "$b"
"""
for orig, backup in originals_and_backups
),
]
)
yield
finally:
# As per the note above, the `cp` below will fail if we were
# shadowing a mount -- all our mounts are currently read-only. This
# means that we can also get away with `--reflink=always`, we only
# need `auto` above to support debug-only experiments.
#
# If you found a really good reason to improve support for this
# situation, which only makes sense when you definitely never need
# to update the shadowed file, there's low hanging-fruit.
# Simply add `|| diff -q "$orig" "$backup"` -- there's no sense in
# failing if the file hasn't changed.
#
# It IS possible to deal with failures to capture changes, too, but
# I find this far-fetched, and so won't write out the solution here.
#
# Future: we could skip the "move back" part if we knew that the
# intended use of the snapshot is ephemeral -- either because the
# user explicitly told us via a flag, or because:
# opts.snapshot and not opts.debug_only_opts.snapshot_into
# However, I think the savings in practice are too minimal to
# bother with the extra complexity & requisite testing.
subvol.run_as_root(
[
"sh",
"-uec",
"\n".join(
f"""\
o={orig.shell_quote()}
cp --reflink=always --preserve=all {backup.shell_quote()} "$o"
rm {backup.shell_quote()}
"""
for orig, backup in originals_and_backups
)
+ "\n"
# Try to remove /__antlir__ because it is possible that this
# plugin was the only thing that made it exist.
+ f"""