Since 0.9.8, "multipath -f" fails with "device not found" when it is
given a path to a map, such as /dev/mapper/<alias> or
/dev/disk/by-id/dm-uuid-mpath-<WWID>, and multipathd is running.

get_dev_type() accepts such a path as DEV_DEVMAP, because it stat()s
to a device-mapper block device, and delegate_to_multipathd() sends
it unchanged as "del map <path>". cli_del_map() only strips a leading
"/dev/", and find_mp_by_str() only matches an alias, a WWID or a dm-N
name, so the map is not found. Before commit a4dbdcec, cli_del_map()
looked the argument up with dm_get_major_minor(), which accepted any
path to the device. And before commit 8c0c12ef, a failed delegation
fell back to a local flush, which also resolves paths.

This breaks callers that remove maps by path. OpenStack os-brick's
Fibre Channel connector flushes /dev/disk/by-id/dm-uuid-mpath-<WWID>
on every volume detach, so detach works on RHEL 9 (0.8.7 based) and
fails on RHEL 10 (0.9.9 based).

If the -f argument is a path, resolve it to the map name with
dm_mapname() before delegating. The local flush, used when multipathd
is not running, gets the same name. Arguments that are valid map
names are passed through unchanged.

Fixes: a4dbdcec ("multipathd: simplify cli_del_map()")
Reported-by: Simon Dodsley <[email protected]>
Signed-off-by: Brian Bunker <[email protected]>
---
 multipath/main.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/multipath/main.c b/multipath/main.c
index f20d701c..5068a659 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -729,6 +729,30 @@ get_dev_type(char *dev) {
        return DEV_NONE;
 }
 
+/*
+ * multipathd resolves "del map $map" arguments by alias, WWID or dm-N
+ * name, not by device path. If a map was passed as a path, e.g.
+ * /dev/mapper/$alias or /dev/disk/by-id/dm-uuid-mpath-$WWID, replace it
+ * with the map name.
+ */
+static void
+resolve_devmap_path(char *dev)
+{
+       struct stat buf;
+       char __attribute__((cleanup(cleanup_charp))) *name = NULL;
+
+       if (valid_alias(dev) || stat(dev, &buf) != 0 ||
+           !S_ISBLK(buf.st_mode))
+               return;
+
+       name = dm_mapname(major(buf.st_rdev), minor(buf.st_rdev));
+       if (!name)
+               return;
+
+       condlog(3, "%s: using map name %s", dev, name);
+       strlcpy(dev, name, FILE_NAME_SIZE);
+}
+
 /*
  * Some multipath commands are dangerous to run while multipathd is running.
  * For example, "multipath -r" may apply a modified configuration to the 
kernel,
@@ -1027,6 +1051,8 @@ main (int argc, char *argv[])
                condlog(0, "the -f option requires a map name to remove");
                goto out;
        }
+       if (cmd == CMD_FLUSH_ONE)
+               resolve_devmap_path(dev);
 
        while (1) {
                int ret = delegate_to_multipathd(cmd, dev, dev_type, conf);
-- 
2.55.0


Reply via email to