in antlir/subvol_utils.py [0:0]
def _gen_inner_subvol_paths(self) -> Iterable[Path]:
"""
Implementation detail for `delete`.
The intent of the code below is to make as many assertions as
possible to avoid accidentally deleting a subvolume that's not a
descendant of `self.` So, we write many assertions. Additionally,
this gets some implicit safeguards from other `Subvol` methods.
- `.path` checks the inner subvol paths to ensure they're not
traversing symlinks to go outside of the subvol.
- The fact that `Subvol` exists means that we checked that it's a
subvolume at construction time -- this is important since `btrfs
subvol list -o` returns bad results for non-subvolume paths.
Moreover, our `btrfs subvol show` reconfirms it.
"""
# `btrfs subvol {show,list}` both use the subvolume's path relative
# to the volume root.
my_rel_to_vol_root, _ = self.run_as_root(
["btrfs", "subvolume", "show", self.path()], stdout=subprocess.PIPE
).stdout.split(b"\n", 1)
my_path = self.path()
# NB: The below will fire if `Subvol` is the root subvol, since its
# relative path is `/`. However, that's not a case we need to
# support in any foreseeable future, and it would also require
# special-casing in the prefix search logic.
assert not my_rel_to_vol_root.startswith(b"/"), my_rel_to_vol_root
# Depending on how this subvolume has been mounted and is being used
# the interaction between the `btrfs subvolume show` path (the first
# line of `btrfs subvolume show` is what we care about) and this
# subvolume path (`self.path()`) is different. The cases we have to
# solve for are as it relates to inner subvolumes are:
# - This subvolume is used as the "root" subvol for a container
# and inner subvols are created within that container.
# This is what happens with `nspawn_in_subvol`, ie: as part of an
# `image_*_unittest`, `image.genrule`, or via a `=container`
# `buck run` target. In this case the btrfs volume is mounted
# using a `subvol=` mount option, resulting in the mount "seeing"
# only the contents of the selected subvol.
# - This subvol is used on the *host* machine (where `buck` runs)
# and inner subvols are created. This is the standard case for
# `*_unittest` targets since those are executed in the host context.
# In this case the btrfs volume is mounted such that the `FS_TREE`
# subvol (id=5) is used resulting in the mount "seeing" *all*
# of the subvols contained within the volume.
# In this case the output of `btrfs subvolume show` looks something
# like this (taken from running the `:test-subvol-utils` test):
#
# tmp/delete_recursiveo7x56sn2/outer
# Name: outer
# UUID: aa2d8590-ba00-8a45-aee2-c1553f3dd292
# Parent UUID: -
# Received UUID: -
# Creation time: 2021-05-18 08:07:17 -0700
# Subvolume ID: 323
# Generation: 92
# Gen at creation: 89
# Parent ID: 5
# Top level ID: 5
# Flags: -
# Snapshot(s):
# and `my_path` looks something like this:
# /data/users/lsalis/fbsource/fbcode/buck-image-out/volume/tmp/delete_recursiveo7x56sn2/outer # noqa: E501
vol_mounted_at_fstree = my_path.endswith(b"/" + my_rel_to_vol_root)
# In this case the output of `btrfs subvolume show` looks something
# like this (taken from running the `:test-subvol-utils-inner` test):
#
#
# tmp/TempSubvolumes_wk81xmx0/test-subvol-utils-inner__test_layer:Jb__IyU.HzvZ.p73f/delete_recursiveotwxda64/outer # noqa: E501
# Name: outer
# UUID: 76866b7c-c4cc-1d4b-bafa-6aa6f898de16
# Parent UUID: -
# Received UUID: -
# Creation time: 2021-05-18 08:04:01 -0700
# Subvolume ID: 319
# Generation: 87
# Gen at creation: 84
# Parent ID: 318
# Top level ID: 318
# Flags: -
# Snapshot(s):
#
# and `my_path` looks something like this:
# /delete_recursiveotwxda64/outer
vol_mounted_at_subvol = my_rel_to_vol_root.endswith(my_path)
assert vol_mounted_at_fstree ^ vol_mounted_at_subvol, (
"Unexpected paths calculated from btrfs subvolume show: "
f"{my_rel_to_vol_root}, {my_path}"
)
# In either case we need to calculate what the proper vol_dir is, this
# is used below to list all the subvolumes that the volume contains
# and filter out subvolumes that are "inside" this subvol.
# If the volume has been mounted as an fstree (see the comments above)
# then we want to list subvols below the "root" of the volume, which is
# right above the path returned by `btrfs subvolume show`.
# Example `btrfs subvolume list` (taken from `:test-subvol-utils`):
#
# ]# btrfs subvolume list /data/users/lsalis/fbsource/fbcode/buck-image-out/volume/ # noqa: E501
# ID 260 gen 20 top level 5 path targets/test-layer:Jb__FIQ.HyZR.fkyU/volume # noqa: E501
# ID 266 gen 83 top level 5 path targets/test-subvol-utils-inner__test_layer:Jb__IyU.HzvZ.p73f/volume # noqa: E501
# ID 272 gen 64 top level 5 path targets/build-appliance.c7:Jb__hV4.H42o.pR_O/volume # noqa: E501
# ID 300 gen 66 top level 5 path targets/build_appliance_testingprecursor-without-caches-to-build_appliance_testing:Jb__o1c.H8Bc.ASOl/volume # noqa: E501
# ID 307 gen 70 top level 5 path targets/build_appliance_testing:Jb__rtA.H89Z.j0z3/volume # noqa: E501
# ID 308 gen 72 top level 5 path targets/hello_world_base:Jb__u0g.H9yB.t9oN/volume # noqa: E501
# ID 323 gen 92 top level 5 path tmp/delete_recursiveo7x56sn2/outer
# ID 324 gen 91 top level 323 path tmp/delete_recursiveo7x56sn2/outer/inner1 # noqa: E501
# ID 325 gen 91 top level 324 path tmp/delete_recursiveo7x56sn2/outer/inner1/inner2 # noqa: E501
# ID 326 gen 92 top level 323 path tmp/delete_recursiveo7x56sn2/outer/inner3 # noqa: E501
# ]#
if vol_mounted_at_fstree:
vol_dir = my_path[: -len(my_rel_to_vol_root)]
my_prefix = my_rel_to_vol_root
# If the volume has been mounted at a specific subvol (see the comments
# above). Then we want to list subvols below `/` since that is seen
# to be the "root" of the volume.
# Example `btrfs subvolume list` taken from `:test-subvol-utils-inner`:
#
# ]# btrfs subvolume list /
# ID 260 gen 20 top level 5 path targets/test-layer:Jb__FIQ.HyZR.fkyU/volume # noqa: E501
# ID 266 gen 83 top level 5 path targets/test-subvol-utils-inner__test_layer:Jb__IyU.HzvZ.p73f/volume # noqa: E501
# ID 272 gen 64 top level 5 path targets/build-appliance.c7:Jb__hV4.H42o.pR_O/volume # noqa: E501
# ID 300 gen 66 top level 5 path targets/build_appliance_testingprecursor-without-caches-to-build_appliance_testing:Jb__o1c.H8Bc.ASOl/volume # noqa: E501
# ID 307 gen 70 top level 5 path targets/build_appliance_testing:Jb__rtA.H89Z.j0z3/volume # noqa: E501
# ID 308 gen 72 top level 5 path targets/hello_world_base:Jb__u0g.H9yB.t9oN/volume # noqa: E501
# ID 318 gen 84 top level 5 path tmp/TempSubvolumes_wk81xmx0/test-subvol-utils-inner__test_layer:Jb__IyU.HzvZ.p73f # noqa: E501
# ID 319 gen 87 top level 318 path delete_recursiveotwxda64/outer
# ID 320 gen 86 top level 319 path delete_recursiveotwxda64/outer/inner1 # noqa: E501
# ID 321 gen 86 top level 320 path delete_recursiveotwxda64/outer/inner1/inner2 # noqa: E501
# ID 322 gen 87 top level 319 path delete_recursiveotwxda64/outer/inner3 # noqa: E501
# ]#
# Note: code coverage for this branch is in the
# :test-subvol-utils-inner test, but because of the way
# coverage works I can't properly cover this in the larger
# :test-subvol-utils test.
elif vol_mounted_at_subvol: # pragma: nocover
vol_dir = b"/"
my_prefix = my_path[1:]
# We need a trailing slash to chop off this path prefix below.
my_prefix = my_prefix + (b"" if my_prefix.endswith(b"/") else b"/")
# NB: The `-o` option does not work correctly, don't even bother.
for inner_line in self.run_as_root(
["btrfs", "subvolume", "list", vol_dir], stdout=subprocess.PIPE
).stdout.split(b"\n"):
if not inner_line: # Handle the trailing newline
continue
l = {} # Used to check that the labels are as expected
(
l["ID"],
_,
l["gen"],
_,
l["top"],
l["level"],
_,
l["path"],
p,
) = inner_line.split(b" ", 8)
for k, v in l.items():
assert k.encode() == v, (k, v)
if not p.startswith(my_prefix): # Skip non-inner subvolumes
continue
inner_subvol = p[len(my_prefix) :]
assert inner_subvol == os.path.normpath(inner_subvol), inner_subvol
yield self.path(inner_subvol)