From d12baba1a9bfe2d82537b20bc768758d84b263b6 Mon Sep 17 00:00:00 2001
From: David Teigland <teigland@redhat.com>
Date: Mon, 29 Nov 2021 17:13:44 -0600
Subject: [PATCH] pvscan: match device arg to filter symlink

This fixes an issue related to the optimization in
  "pvscan: only add device args to dev cache"

If the devices file is not used, and the lvm.conf filter
accepts devices via symlink names, then those devices won't
be accepted by pvscan for autoactivation.  To resolve this,
recognize when the filter contains symlinks and disable the
optimization.  When the optimization is disabled, a full
dev_cache_scan is performed, and symlinks are associated
with the device names passed to pvscan.  filter-regex
will accept a device if symlinks to that device are accepted.

Conflict: context adaptation in test/shell/udev-pvscan-vgchange.sh
---
 test/shell/udev-pvscan-vgchange.sh | 52 ++++++++++++++++++++++++
 tools/pvscan.c                     | 63 ++++++++++++++++++++++++++++++
 2 files changed, 115 insertions(+)

diff --git a/test/shell/udev-pvscan-vgchange.sh b/test/shell/udev-pvscan-vgchange.sh
index a82bf8876..a209dc048 100644
--- a/test/shell/udev-pvscan-vgchange.sh
+++ b/test/shell/udev-pvscan-vgchange.sh
@@ -382,8 +382,13 @@ BDEVMD=$(basename "$mddev")
 lvcreate -l1 -an -n $lv1 $vg9
 lvcreate -l1 -an -n $lv2 $vg9
 
+mdadm --stop "$mddev"
+systemctl stop lvm-activate-$vg9 || true
 _clear_online_files
+mdadm --assemble "$mddev" "$dev1" "$dev2"
 
+# this trigger might be redundant because the mdadm --assemble
+# probably triggers an add uevent
 udevadm trigger --settle -c add /sys/block/$BDEVMD
 
 wait_lvm_activate $vg9
@@ -401,3 +406,50 @@ mdadm --stop "$mddev"
 aux udev_wait
 wipe_all
 
+# no devices file, filter with symlink of PV
+# the pvscan needs to look at all dev names to
+# match the symlink in the filter with the
+# dev name (or major minor) passed to pvscan.
+# This test doesn't really belong in this file
+# because it's not testing lvm-activate.
+
+aux lvmconf 'devices/use_devicesfile = 0'
+_clear_online_files
+rm "$DF"
+vgcreate $vg10 "$dev1"
+lvcreate -l1 -an -n $lv1 $vg10 "$dev1"
+
+PVID1=$(pvs "$dev1" --noheading -o uuid | tr -d - | awk '{print $1}')
+# PVID with dashes
+OPVID1=`pvs "$dev1" --noheading -o uuid | awk '{print $1}'`
+
+udevadm trigger --settle -c add /sys/block/$BDEV1
+
+# uevent from the trigger should create this symlink
+ls /dev/disk/by-id/lvm-pv-uuid-$OPVID1
+
+vgchange -an $vg10
+systemctl stop lvm-activate-$vg10
+_clear_online_files
+
+aux lvmconf "devices/filter = [ \"a|/dev/disk/by-id/lvm-pv-uuid-$OPVID1|\", \"r|.*|\" ]"
+aux lvmconf 'devices/global_filter = [ "a|.*|" ]'
+
+pvscan --cache -aay "$dev1"
+
+check lv_field $vg10/$lv1 lv_active "active"
+
+vgchange -an $vg10
+_clear_online_files
+
+aux lvmconf 'devices/filter = [ "a|lvm-pv-uuid|", "r|.*|" ]'
+aux lvmconf 'devices/global_filter = [ "a|.*|" ]'
+
+pvscan --cache -aay "$dev1"
+
+check lv_field $vg10/$lv1 lv_active "active"
+
+vgchange -an $vg10
+vgremove -y $vg10
+wipe_all
+
diff --git a/tools/pvscan.c b/tools/pvscan.c
index d360ec87f..0c25fb542 100644
--- a/tools/pvscan.c
+++ b/tools/pvscan.c
@@ -1127,6 +1127,45 @@ out:
 	return ret;
 }
 
+/*
+ * The optimization in which only the pvscan arg devname is added to dev-cache
+ * does not work if there's an lvm.conf filter containing symlinks to the dev
+ * like /dev/disk/by-id/lvm-pv-uuid-xyz entries.  A full dev_cache_scan will
+ * associate the symlinks with the system dev name passed to pvscan, which lets
+ * filter-regex match the devname with the symlink name in the filter.
+ */
+static int _filter_uses_symlinks(struct cmd_context *cmd, int filter_cfg)
+{
+	const struct dm_config_node *cn;
+	const struct dm_config_value *cv;
+
+	if ((cn = find_config_tree_array(cmd, filter_cfg, NULL))) {
+        	for (cv = cn->v; cv; cv = cv->next) {
+			if (cv->type != DM_CFG_STRING)
+				continue;
+			if (!cv->v.str)
+				continue;
+
+			if (!strncmp(cv->v.str, "/dev/disk/", 10))
+				return 1;
+			if (!strncmp(cv->v.str, "/dev/mapper/", 12))
+				return 1;
+			if (cv->v.str[0] == '/')
+				continue;
+
+			/* In case /dev/disk/by was omitted */
+			if (strstr(cv->v.str, "lvm-pv-uuid"))
+				return 1;
+			if (strstr(cv->v.str, "dm-uuid"))
+				return 1;
+			if (strstr(cv->v.str, "wwn-"))
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
 struct pvscan_arg {
 	struct dm_list list;
 	const char *devname;
@@ -1191,6 +1230,30 @@ static int _get_args_devs(struct cmd_context *cmd, struct dm_list *pvscan_args,
 	struct pvscan_arg *arg;
 	struct device_list *devl;
 
+	/*
+	 * If no devices file is used, and lvm.conf filter is set to
+	 * accept /dev/disk/by-id/lvm-pv-uuid-xyz or another symlink,
+	 * but pvscan --cache is passed devname or major:minor, so
+	 * pvscan needs to match its arg device to the filter symlink.
+	 * setup_dev_in_dev_cache() adds /dev/sda2 to dev-cache which
+	 * does not match a symlink to /dev/sda2, so we need a full
+	 * dev_cache_scan that will associate all symlinks to sda2,
+	 * which allows filter-regex to work.  This case could be
+	 * optimized if needed by adding dev-cache entries for each
+	 * filter "a" entry (filter symlink patterns would still need
+	 * a full dev_cache_scan.)
+	 * (When no devices file is used and 69-dm-lvm.rules is
+	 * used which calls pvscan directly, symlinks may not
+	 * have been created by other rules when pvscan runs, so
+	 * the full dev_cache_scan may still not find them.)
+	 */
+	if (!cmd->enable_devices_file && !cmd->enable_devices_list &&
+	    (_filter_uses_symlinks(cmd, devices_filter_CFG) ||
+	     _filter_uses_symlinks(cmd, devices_global_filter_CFG))) {
+		log_print_pvscan(cmd, "finding all devices for filter symlinks.");
+		dev_cache_scan(cmd);
+	}
+
 	/* pass NULL filter when getting devs from dev-cache, filtering is done separately */
 
 	/* in common usage, no dev will be found for a devno */
-- 
2.33.0

