From f11a7ca90c37f089047057f033d7cacf593bab3f Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 18 Jan 2021 13:30:20 +0100 Subject: [PATCH] mountinfo: fix PrefixFilter() being too greedy The PrefixFilter matched on a literal "prefix", making it too greedy, and, for example, filtering on "/a" prefix would also return mounts for "/aaa" or "/abc". This patch changes the matching to match on a prefix / parent _path_ instead. Signed-off-by: Sebastiaan van Stijn --- mountinfo/mountinfo_filters.go | 11 +++++++--- mountinfo/mountinfo_filters_test.go | 31 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 mountinfo/mountinfo_filters_test.go diff --git a/mountinfo/mountinfo_filters.go b/mountinfo/mountinfo_filters.go index 5869b2ce..16079c3c 100644 --- a/mountinfo/mountinfo_filters.go +++ b/mountinfo/mountinfo_filters.go @@ -14,11 +14,16 @@ import "strings" // stop: true if parsing should be stopped after the entry. type FilterFunc func(*Info) (skip, stop bool) -// PrefixFilter discards all entries whose mount points -// do not start with a specific prefix. +// PrefixFilter discards all entries whose mount points do not start with, or +// are equal to the path specified in prefix. The prefix path must be absolute, +// have all symlinks resolved, and cleaned (i.e. no extra slashes or dots). +// +// PrefixFilter treats prefix as a path, not a partial prefix, which means that +// given "/foo", "/foo/bar" and "/foobar" entries, PrefixFilter("/foo") returns +// "/foo" and "/foo/bar", and discards "/foobar". func PrefixFilter(prefix string) FilterFunc { return func(m *Info) (bool, bool) { - skip := !strings.HasPrefix(m.Mountpoint, prefix) + skip := !strings.HasPrefix(m.Mountpoint+"/", prefix+"/") return skip, false } } diff --git a/mountinfo/mountinfo_filters_test.go b/mountinfo/mountinfo_filters_test.go new file mode 100644 index 00000000..cf5b35e0 --- /dev/null +++ b/mountinfo/mountinfo_filters_test.go @@ -0,0 +1,31 @@ +package mountinfo + +import "testing" + +func TestPrefixFilter(t *testing.T) { + tests := []struct { + prefix string + mountPoint string + shouldSkip bool + }{ + {prefix: "/a", mountPoint: "/a", shouldSkip: false}, + {prefix: "/a", mountPoint: "/a/b", shouldSkip: false}, + {prefix: "/a", mountPoint: "/aa", shouldSkip: true}, + {prefix: "/a", mountPoint: "/aa/b", shouldSkip: true}, + + // invalid prefix: prefix path must be cleaned and have no trailing slash + {prefix: "/a/", mountPoint: "/a", shouldSkip: true}, + {prefix: "/a/", mountPoint: "/a/b", shouldSkip: true}, + } + for _, tc := range tests { + filter := PrefixFilter(tc.prefix) + skip, _ := filter(&Info{Mountpoint: tc.mountPoint}) + if skip != tc.shouldSkip { + if tc.shouldSkip { + t.Errorf("prefix %q: expected %q to be skipped", tc.prefix, tc.mountPoint) + } else { + t.Errorf("prefix %q: expected %q not to be skipped", tc.prefix, tc.mountPoint) + } + } + } +}