]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
btrfs: harden identification of a stale device
authorAnand Jain <anand.jain@oracle.com>
Wed, 12 Jan 2022 05:05:59 +0000 (13:05 +0800)
committerStefan Bader <stefan.bader@canonical.com>
Fri, 20 May 2022 12:39:47 +0000 (14:39 +0200)
BugLink: https://bugs.launchpad.net/bugs/1969110
[ Upstream commit 770c79fb65506fc7c16459855c3839429f46cb32 ]

Identifying and removing the stale device from the fs_uuids list is done
by btrfs_free_stale_devices().  btrfs_free_stale_devices() in turn
depends on device_path_matched() to check if the device appears in more
than one btrfs_device structure.

The matching of the device happens by its path, the device path. However,
when device mapper is in use, the dm device paths are nothing but a link
to the actual block device, which leads to the device_path_matched()
failing to match.

Fix this by matching the dev_t as provided by lookup_bdev() instead of
plain string compare of the device paths.

Reported-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Anand Jain <anand.jain@oracle.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
(cherry picked from commit 40d006dfedd60ed8415bf8edec539be2d6c61662)
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
fs/btrfs/volumes.c

index 57021cc83811233fcbcc8adceba634ccd7d0ba0e..f6d6825f218a17136fb3990628818fab8fc3e5bd 100644 (file)
@@ -530,15 +530,48 @@ error:
        return ret;
 }
 
-static bool device_path_matched(const char *path, struct btrfs_device *device)
+/*
+ * Check if the device in the path matches the device in the given struct device.
+ *
+ * Returns:
+ *   true  If it is the same device.
+ *   false If it is not the same device or on error.
+ */
+static bool device_matched(const struct btrfs_device *device, const char *path)
 {
-       int found;
+       char *device_name;
+       dev_t dev_old;
+       dev_t dev_new;
+       int ret;
+
+       /*
+        * If we are looking for a device with the matching dev_t, then skip
+        * device without a name (a missing device).
+        */
+       if (!device->name)
+               return false;
+
+       device_name = kzalloc(BTRFS_PATH_NAME_MAX, GFP_KERNEL);
+       if (!device_name)
+               return false;
 
        rcu_read_lock();
-       found = strcmp(rcu_str_deref(device->name), path);
+       scnprintf(device_name, BTRFS_PATH_NAME_MAX, "%s", rcu_str_deref(device->name));
        rcu_read_unlock();
 
-       return found == 0;
+       ret = lookup_bdev(device_name, &dev_old);
+       kfree(device_name);
+       if (ret)
+               return false;
+
+       ret = lookup_bdev(path, &dev_new);
+       if (ret)
+               return false;
+
+       if (dev_old == dev_new)
+               return true;
+
+       return false;
 }
 
 /*
@@ -571,9 +604,7 @@ static int btrfs_free_stale_devices(const char *path,
                                         &fs_devices->devices, dev_list) {
                        if (skip_device && skip_device == device)
                                continue;
-                       if (path && !device->name)
-                               continue;
-                       if (path && !device_path_matched(path, device))
+                       if (path && !device_matched(device, path))
                                continue;
                        if (fs_devices->opened) {
                                /* for an already deleted device return 0 */