forked from mirrors/linux
		
	btrfs: canonicalize the device path before adding it
[PROBLEM]
Currently btrfs accepts any file path for its device, resulting some
weird situation:
 # ./mount_by_fd /dev/test/scratch1  /mnt/btrfs/
The program has the following source code:
 #include <fcntl.h>
 #include <stdio.h>
 #include <sys/mount.h>
 int main(int argc, char *argv[]) {
	int fd = open(argv[1], O_RDWR);
	char path[256];
	snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
	return mount(path, argv[2], "btrfs", 0, NULL);
 }
Then we can have the following weird device path:
 BTRFS: device fsid 2378be81-fe12-46d2-a9e8-68cf08dd98d5 devid 1 transid 7 /proc/self/fd/3 (253:2) scanned by mount_by_fd (18440)
Normally it's not a big deal, and later udev can trigger a device path
rename. But if udev didn't trigger, the device path "/proc/self/fd/3"
will show up in mtab.
[CAUSE]
For filename "/proc/self/fd/3", it means the opened file descriptor 3.
In above case, it's exactly the device we want to open, aka points to
"/dev/test/scratch1" which is another symlink pointing to "/dev/dm-2".
Inside kernel we solve the mount source using LOOKUP_FOLLOW, which
follows the symbolic link and grab the proper block device.
But inside btrfs we also save the filename into btrfs_device::name, and
utilize that member to report our mount source, which leads to the above
situation.
[FIX]
Instead of unconditionally trust the path, check if the original file
(not following the symbolic link) is inside "/dev/", if not, then
manually lookup the path to its final destination, and use that as our
device path.
This allows us to still use symbolic links, like
"/dev/mapper/test-scratch" from LVM2, which is required for fstests runs
with LVM2 setup.
And for really weird names, like the above case, we solve it to
"/dev/dm-2" instead.
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Link: https://bugzilla.suse.com/show_bug.cgi?id=1230641
Reported-by: Fabian Vogt <fvogt@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
			
			
This commit is contained in:
		
							parent
							
								
									2e8b6bc0ab
								
							
						
					
					
						commit
						7e06de7c83
					
				
					 1 changed files with 86 additions and 1 deletions
				
			
		| 
						 | 
				
			
			@ -732,6 +732,78 @@ const u8 *btrfs_sb_fsid_ptr(const struct btrfs_super_block *sb)
 | 
			
		|||
	return has_metadata_uuid ? sb->metadata_uuid : sb->fsid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * We can have very weird soft links passed in.
 | 
			
		||||
 * One example is "/proc/self/fd/<fd>", which can be a soft link to
 | 
			
		||||
 * a block device.
 | 
			
		||||
 *
 | 
			
		||||
 * But it's never a good idea to use those weird names.
 | 
			
		||||
 * Here we check if the path (not following symlinks) is a good one inside
 | 
			
		||||
 * "/dev/".
 | 
			
		||||
 */
 | 
			
		||||
static bool is_good_dev_path(const char *dev_path)
 | 
			
		||||
{
 | 
			
		||||
	struct path path = { .mnt = NULL, .dentry = NULL };
 | 
			
		||||
	char *path_buf = NULL;
 | 
			
		||||
	char *resolved_path;
 | 
			
		||||
	bool is_good = false;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (!dev_path)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	path_buf = kmalloc(PATH_MAX, GFP_KERNEL);
 | 
			
		||||
	if (!path_buf)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Do not follow soft link, just check if the original path is inside
 | 
			
		||||
	 * "/dev/".
 | 
			
		||||
	 */
 | 
			
		||||
	ret = kern_path(dev_path, 0, &path);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto out;
 | 
			
		||||
	resolved_path = d_path(&path, path_buf, PATH_MAX);
 | 
			
		||||
	if (IS_ERR(resolved_path))
 | 
			
		||||
		goto out;
 | 
			
		||||
	if (strncmp(resolved_path, "/dev/", strlen("/dev/")))
 | 
			
		||||
		goto out;
 | 
			
		||||
	is_good = true;
 | 
			
		||||
out:
 | 
			
		||||
	kfree(path_buf);
 | 
			
		||||
	path_put(&path);
 | 
			
		||||
	return is_good;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int get_canonical_dev_path(const char *dev_path, char *canonical)
 | 
			
		||||
{
 | 
			
		||||
	struct path path = { .mnt = NULL, .dentry = NULL };
 | 
			
		||||
	char *path_buf = NULL;
 | 
			
		||||
	char *resolved_path;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (!dev_path) {
 | 
			
		||||
		ret = -EINVAL;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	path_buf = kmalloc(PATH_MAX, GFP_KERNEL);
 | 
			
		||||
	if (!path_buf) {
 | 
			
		||||
		ret = -ENOMEM;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = kern_path(dev_path, LOOKUP_FOLLOW, &path);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto out;
 | 
			
		||||
	resolved_path = d_path(&path, path_buf, PATH_MAX);
 | 
			
		||||
	ret = strscpy(canonical, resolved_path, PATH_MAX);
 | 
			
		||||
out:
 | 
			
		||||
	kfree(path_buf);
 | 
			
		||||
	path_put(&path);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_same_device(struct btrfs_device *device, const char *new_path)
 | 
			
		||||
{
 | 
			
		||||
	struct path old = { .mnt = NULL, .dentry = NULL };
 | 
			
		||||
| 
						 | 
				
			
			@ -1419,12 +1491,23 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags,
 | 
			
		|||
	bool new_device_added = false;
 | 
			
		||||
	struct btrfs_device *device = NULL;
 | 
			
		||||
	struct file *bdev_file;
 | 
			
		||||
	char *canonical_path = NULL;
 | 
			
		||||
	u64 bytenr;
 | 
			
		||||
	dev_t devt;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	lockdep_assert_held(&uuid_mutex);
 | 
			
		||||
 | 
			
		||||
	if (!is_good_dev_path(path)) {
 | 
			
		||||
		canonical_path = kmalloc(PATH_MAX, GFP_KERNEL);
 | 
			
		||||
		if (canonical_path) {
 | 
			
		||||
			ret = get_canonical_dev_path(path, canonical_path);
 | 
			
		||||
			if (ret < 0) {
 | 
			
		||||
				kfree(canonical_path);
 | 
			
		||||
				canonical_path = NULL;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	/*
 | 
			
		||||
	 * Avoid an exclusive open here, as the systemd-udev may initiate the
 | 
			
		||||
	 * device scan which may race with the user's mount or mkfs command,
 | 
			
		||||
| 
						 | 
				
			
			@ -1469,7 +1552,8 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags,
 | 
			
		|||
		goto free_disk_super;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	device = device_list_add(path, disk_super, &new_device_added);
 | 
			
		||||
	device = device_list_add(canonical_path ? : path, disk_super,
 | 
			
		||||
				 &new_device_added);
 | 
			
		||||
	if (!IS_ERR(device) && new_device_added)
 | 
			
		||||
		btrfs_free_stale_devices(device->devt, device);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1478,6 +1562,7 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags,
 | 
			
		|||
 | 
			
		||||
error_bdev_put:
 | 
			
		||||
	fput(bdev_file);
 | 
			
		||||
	kfree(canonical_path);
 | 
			
		||||
 | 
			
		||||
	return device;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue