From cb8abddb20a0823c9dae5ab464a4f767d2268aca Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 14 Jan 2019 13:30:24 +0000 Subject: [PATCH] Btrfs-progs: fix mount point detection due to partial prefix match 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 only partially matches the path. 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 Reviewed-by: David Disseldorp Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- utils.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils.c b/utils.c index 5b983375..c360445a 100644 --- a/utils.c +++ b/utils.c @@ -2078,7 +2078,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);