Skip to content

Commit 31c7d5b

Browse files
osandovmaharmstone
authored andcommitted
btrfs-progs: subvol list: document and test actual behavior of paths
The way btrfs subvol list prints paths and what the -o and -a flags do are all nonsense. Apparently, very early versions of Btrfs had a concept of a "top level" of subvolumes rather than the root filesystem tree that we have today; see commit 4ff9e2a ("Add btrfs-list for listing subvolumes"). The original subvol list code tracked the ID of that top level subvolume. Eventually, 5 became the only possibility for the top level, and -o, -a, and path printing were based on that. Commit 4f5ebb3 ("Btrfs-progs: fix to make list specified directory's subvolumes work") broke this and changed the top level to be the same as the parent subvolume ID, which gave us the illogical behavior we have today. It has been this way for a decade, so we're probably stuck with it. But let's at least document precisely what these all do in preparation for adding sensible options. Let's also add tests in preparation for the upcoming changes. Signed-off-by: Omar Sandoval <[email protected]>
1 parent a079982 commit 31c7d5b

File tree

3 files changed

+143
-8
lines changed

3 files changed

+143
-8
lines changed

Documentation/btrfs-subvolume.rst

+15-5
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ list [options] [-G [\+|-]<value>] [-C [+|-]<value>] [--sort=rootid,gen,ogen,path
143143

144144
where *ID* is subvolume's (root)id, *generation* is an internal counter which is
145145
updated every transaction, *parent_ID* is the same as the parent subvolume's id,
146-
and *path* is the relative path of the subvolume to the top level subvolume.
146+
and *path* is the path of the subvolume. The exact meaning of *path*
147+
depends on the **Path filtering** option used.
147148
The subvolume's ID may be used by the subvolume set-default command,
148149
or at mount time via the *subvolid=* option.
149150

@@ -152,11 +153,20 @@ list [options] [-G [\+|-]<value>] [-C [+|-]<value>] [--sort=rootid,gen,ogen,path
152153
Path filtering:
153154

154155
-o
155-
Print only subvolumes below specified <path>. Note that this is not a
156-
recursive command, and won't show nested subvolumes under <path>.
156+
Print only the immediate children subvolumes of the subvolume
157+
containing <path>. Paths are printed relative to the root of
158+
the filesystem.
157159
-a
158-
print all the subvolumes in the filesystem and distinguish between
159-
absolute and relative path with respect to the given *path*.
160+
Print all subvolumes in the filesystem other than the root
161+
subvolume. Paths are printed relative to the root of the
162+
filesystem, except that subvolumes that are not an immediate
163+
child of the subvolume containing <path> are prefixed with
164+
"<FS_TREE>/".
165+
166+
If none of these are given, print all subvolumes in the filesystem
167+
other than the root subvolume. Paths below the subvolume containing
168+
<path> are printed relative to that subvolume, and other paths are
169+
printed relative to the root of the filesystem.
160170

161171
Field selection:
162172

cmds/subvolume-list.c

+10-3
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,16 @@ static const char * const cmd_subvolume_list_usage[] = {
5757
"List subvolumes and snapshots in the filesystem.",
5858
"",
5959
"Path filtering:",
60-
OPTLINE("-o", "print only subvolumes below specified path"),
61-
OPTLINE("-a", "print all the subvolumes in the filesystem and "
62-
"distinguish absolute and relative path with respect to the given <path>"),
60+
OPTLINE("-o", "print only the immediate children subvolumes of the "
61+
"subvolume containing <path>"),
62+
OPTLINE("-a", "print all subvolumes in the filesystem other than the "
63+
"root subvolume, and prefix subvolumes that are not an "
64+
"immediate child of the subvolume containing <path> with "
65+
"\"<FS_TREE>/\""),
66+
"",
67+
"If none of these are given, print all subvolumes other than the root",
68+
"subvolume relative to the subvolume containing <path> if below it,",
69+
"otherwise relative to the root of the filesystem.",
6370
"",
6471
"Field selection:",
6572
OPTLINE("-p", "print parent ID"),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#!/bin/bash
2+
# Test how btrfs subvolume list prints paths, including all of the weird
3+
# accidental behavior.
4+
5+
source "$TEST_TOP/common" || exit
6+
7+
check_prereq btrfs
8+
check_prereq mkfs.btrfs
9+
10+
setup_root_helper
11+
prepare_test_dev
12+
13+
run_check_mkfs_test_dev
14+
run_check_mount_test_dev
15+
16+
cd "$TEST_MNT"
17+
18+
run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "a"
19+
run_check $SUDO_HELPER mkdir "a/b"
20+
run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "a/b/c"
21+
run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "a/b/c/d"
22+
run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "a/e"
23+
24+
subvol_list_paths() {
25+
run_check_stdout $SUDO_HELPER "$TOP/btrfs" subvolume list "$@" | sed 's/.*path //'
26+
}
27+
28+
expect_subvol_list_paths() {
29+
diff -u - <(subvol_list_paths "$@") || _fail "wrong output from btrfs subvolume list $*"
30+
}
31+
32+
### No options ###
33+
34+
# Paths are relative to the given subvolume if they are beneath it and relative
35+
# to the root of the filesystem otherwise.
36+
expect_subvol_list_paths . << EOF
37+
a
38+
a/b/c
39+
a/b/c/d
40+
a/e
41+
EOF
42+
43+
expect_subvol_list_paths a << EOF
44+
a
45+
b/c
46+
b/c/d
47+
e
48+
EOF
49+
50+
expect_subvol_list_paths a/b/c << EOF
51+
a
52+
a/b/c
53+
d
54+
a/e
55+
EOF
56+
57+
# If passed a directory, it's treated as the containing subvolume.
58+
expect_subvol_list_paths a/b << EOF
59+
a
60+
b/c
61+
b/c/d
62+
e
63+
EOF
64+
65+
### -a ###
66+
67+
# Paths are relative to the root of the filesystem. Subvolumes that are not an
68+
# immediate child of the passed subvolume are prefixed with <FS_TREE>/.
69+
expect_subvol_list_paths -a . << EOF
70+
a
71+
<FS_TREE>/a/b/c
72+
<FS_TREE>/a/b/c/d
73+
<FS_TREE>/a/e
74+
EOF
75+
76+
expect_subvol_list_paths -a a << EOF
77+
<FS_TREE>/a
78+
a/b/c
79+
<FS_TREE>/a/b/c/d
80+
a/e
81+
EOF
82+
83+
# If passed a directory, it's treated as the containing subvolume.
84+
expect_subvol_list_paths -a a/b << EOF
85+
<FS_TREE>/a
86+
a/b/c
87+
<FS_TREE>/a/b/c/d
88+
a/e
89+
EOF
90+
91+
### -o ###
92+
93+
# Only immediate children of the passed subvolume are printed, and they are
94+
# printed relative to the root of the filesystem.
95+
expect_subvol_list_paths -o . << EOF
96+
a
97+
EOF
98+
99+
expect_subvol_list_paths -o a << EOF
100+
a/b/c
101+
a/e
102+
EOF
103+
104+
# If passed a directory, it's treated as the containing subvolume.
105+
expect_subvol_list_paths -o a/b << EOF
106+
a/b/c
107+
a/e
108+
EOF
109+
110+
expect_subvol_list_paths -o a/b/c << EOF
111+
a/b/c/d
112+
EOF
113+
114+
expect_subvol_list_paths -o a/b/c/d << EOF
115+
EOF
116+
117+
cd ..
118+
run_check_umount_test_dev

0 commit comments

Comments
 (0)