From: Filipe Manana <fdman...@suse.com> 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 <d...@suse.com> Signed-off-by: Filipe Manana <fdman...@suse.com> --- 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