From: Filipe Manana <fdmanana@xxxxxxxx>
When attempting to find the mount point of a path we can end up returning
an incorrect mount point. This happens because we consider a mount point
valid for the given path even if it matches only partially the patch.
Consider the following example, which makes btrfs receive fail:
$ truncate -s 1G disk1
$ truncate -s 1G disk2
$ losetup /dev/loop1 disk1
$ losetup /dev/loop2 disk2
$ mkfs.btrfs -f /dev/loop1
$ mkfs.btrfs -f /dev/loop2
$ mount /dev/loop1 /mnt
$ mkdir /mnt/ddis
$ mkdir /mnt/ddis-not-a-mount
$ mount /dev/loop2 /mnt/ddis
$ echo "some data" > /mnt/ddis/file
$ btrfs subvolume snapshot -r /mnt/ddis /mnt/ddis/snap
$ btrfs send -f /tmp/send.data /mnt/ddis/snap
$ btrfs receive -f /tmp/send.data /mnt/ddis-not-a-mount
At subvol snap
ERROR: chown failed: No such file or directory
In that example btrfs receive passes the path "/mnt/ddis-not-a-mount" to
find_mount_root() which picks "/mnt/ddis" as the mount point instead of
"/mnt". The wrong decision happens because "/mnt/ddis" is the longest
string found that is a prefix of "/mnt/ddis-not-a-mount", however it
shouldn't be considered valid because what follows the substring "ddis"
in the given path is not a path separator ("/") nor the null character
('\0'). So fix find_mount_root() to check for the presence of a path
separator or a null byte character after if finds a mount point string
that matches the given path.
A test case will follow soon in a separate patch.
Reported-by: David Disseldorp <ddis@xxxxxxxx>
Signed-off-by: Filipe Manana <fdmanana@xxxxxxxx>
---
utils.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/utils.c b/utils.c
index 3a4bc92a..6616630b 100644
--- a/utils.c
+++ b/utils.c
@@ -2064,7 +2064,8 @@ int find_mount_root(const char *path, char **mount_root)
while ((ent = getmntent(mnttab))) {
len = strlen(ent->mnt_dir);
- if (strncmp(ent->mnt_dir, path, len) == 0) {
+ if (strncmp(ent->mnt_dir, path, len) == 0 &&
+ (path[len] == '/' || path[len] == '\0')) {
/* match found and use the latest match */
if (longest_matchlen <= len) {
free(longest_match);
--
2.11.0