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