From 542390047f9e38d139a68088ffdc9acad74ab8fa Mon Sep 17 00:00:00 2001
From: David Teigland <teigland@redhat.com>
Date: Thu, 19 Jan 2023 17:37:31 -0600
Subject: [PATCH] pvscan: use alternate device names from DEVLINKS to check
 filter

pvscan --cache <dev> is called by our udev rule at a time when all
the symlinks for <dev> may not be created yet (by other udev rules.)
The regex filter in lvm.conf may refer to <dev> using a symlink name
that hasn't yet been created, which would cause <dev> to not match
the filter regex.  The DEVLINKS env var, set by udev, contains all
the symlink names for <dev> that have been or will be created.
So, we add all these symlink names to dev->aliases, as if we had
found them in /dev.  This allows <dev> to be recognized by a regex
filter containing a symlink for <dev>.
---
 lib/commands/toolcontext.h |  1 +
 lib/filters/filter-regex.c |  2 +-
 tools/pvscan.c             | 60 +++++++++++++++++++++++---------------
 3 files changed, 38 insertions(+), 25 deletions(-)

diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index 957ab7f..2840fb5 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -202,6 +202,7 @@ struct cmd_context {
 	unsigned ignore_device_name_mismatch:1; /* skip updating devices file names */
 	unsigned backup_disabled:1;		/* skip repeated debug message */
 	unsigned event_activation:1;		/* whether event_activation is set */
+	unsigned filter_regex_set_preferred_name_disable:1; /* prevent dev_set_preferred_name */
 
 	/*
 	 * Devices and filtering.
diff --git a/lib/filters/filter-regex.c b/lib/filters/filter-regex.c
index 05c5b3f..d9ed010 100644
--- a/lib/filters/filter-regex.c
+++ b/lib/filters/filter-regex.c
@@ -179,7 +179,7 @@ static int _accept_p(struct cmd_context *cmd, struct dev_filter *f, struct devic
 
 		if (m >= 0) {
 			if (dm_bit(rf->accept, m)) {
-				if (!first)
+				if (!first && !cmd->filter_regex_set_preferred_name_disable)
 					dev_set_preferred_name(sl, dev);
 
 				return 1;
diff --git a/tools/pvscan.c b/tools/pvscan.c
index 35258c5..710ffcd 100644
--- a/tools/pvscan.c
+++ b/tools/pvscan.c
@@ -1234,30 +1234,6 @@ 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 */
@@ -1835,6 +1811,42 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
 
 	cmd->filter_nodata_only = 1;
 
+	/*
+	 * Hack to handle regex filter that contains a symlink name for dev arg.
+	 * pvscan --cache <dev> is called by our udev rule at a time when the
+	 * symlinks for <dev> may not all be created yet (by other udev rules.)
+	 * The regex filter in lvm.conf may refer to <dev> using a symlink name,
+	 * so we need to know all the symlinks for <dev> in order for the filter
+	 * to work correctly. Scanning /dev with dev_cache_scan() would usually
+	 * find all the symlink names for <dev>, adding them to dev->aliases,
+	 * which would let the filter work, but all symlinks aren't created yet.
+	 * But, the DEVLINKS env var, set by udev, contains all the symlink
+	 * names for <dev> that have been or *will be* created. So, we add all
+	 * these symlink names to dev->aliases, as if we had found them in /dev.
+	 * This allows <dev> to be recognized by a regex filter containing a
+	 * symlink for <dev>. We have to tell filter-regex to not set the
+	 * preferred name for <dev> to a symlink name since the <dev> may not
+	 * be usable by that symlink name yet.
+	 */
+	if ((dm_list_size(&pvscan_devs) == 1) &&
+	    !cmd->enable_devices_file && !cmd->enable_devices_list &&
+	    (_filter_uses_symlinks(cmd, devices_filter_CFG) ||
+	     _filter_uses_symlinks(cmd, devices_global_filter_CFG))) {
+		char *env_str;
+		struct dm_list *env_aliases;
+		devl = dm_list_item(dm_list_first(&pvscan_devs), struct device_list);
+		if ((env_str = getenv("DEVLINKS"))) {
+			log_debug("Finding symlink names from DEVLINKS for filter regex.");
+			log_debug("DEVLINKS %s", env_str);
+			env_aliases = str_to_str_list(cmd->mem, env_str, " ", 0);
+			dm_list_splice(&devl->dev->aliases, env_aliases);
+		} else {
+			log_debug("Finding symlink names from /dev for filter regex.");
+			dev_cache_scan(cmd);
+		}
+		cmd->filter_regex_set_preferred_name_disable = 1;
+	}
+
 	dm_list_iterate_items_safe(devl, devl2, &pvscan_devs) {
 		if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
 			log_print_pvscan(cmd, "%s excluded by filters: %s.",
-- 
2.33.0

