Blame SOURCES/0023-libmultipath-invert-regexes-that-start-with-exclamat.patch

49190a
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
49190a
From: Benjamin Marzinski <bmarzins@redhat.com>
49190a
Date: Mon, 8 Jun 2020 13:40:16 -0500
49190a
Subject: [PATCH] libmultipath: invert regexes that start with exclamation
49190a
 point
49190a
49190a
The number of devices that multipath needs to blacklist keeps growing,
49190a
and the udev rules already have
49190a
49190a
KERNEL!="sd*|dasd*|nvme*", GOTO="end_mpath"
49190a
49190a
so they only work correctly with these device types.  Instead of
49190a
individually blacklisting every type of device that can't be
49190a
multipathed, multipath's default blacklist should work like the udev
49190a
rule, and blacklist all devices that aren't scsi, dasd, or nvme.
49190a
Unfortunately, the c regex library doesn't support negative lookahead.
49190a
Instead, multipath should treat "!" at the beginning of
49190a
blacklist/exceptions regexes as inverse matching the rest of the regex.
49190a
If users need to match a literal '!' as the first character of their
49190a
regex, they can use "\!" instead. This allows multipath to change the
49190a
default devnode blacklist regex to "!^(sd[a-z]|dasd[a-z]|nvme[0-9])".
49190a
49190a
Extra tests have been added to the blacklist unit tests to verify the
49190a
inverse matching code and the new default blacklist.
49190a
49190a
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
49190a
---
49190a
 libmultipath/blacklist.c   |  41 +++++++++-----
49190a
 libmultipath/blacklist.h   |   3 +
49190a
 multipath/multipath.conf.5 |  17 ++++--
49190a
 tests/blacklist.c          | 110 +++++++++++++++++++++++++++++++++++++
49190a
 tests/test-lib.c           |   2 +-
49190a
 5 files changed, 155 insertions(+), 18 deletions(-)
49190a
49190a
diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c
49190a
index 04d3adb9..0c58aa32 100644
49190a
--- a/libmultipath/blacklist.c
49190a
+++ b/libmultipath/blacklist.c
49190a
@@ -15,9 +15,24 @@
49190a
 #include "structs_vec.h"
49190a
 #include "print.h"
49190a
 
49190a
+char *check_invert(char *str, bool *invert)
49190a
+{
49190a
+	if (str[0] == '!') {
49190a
+		*invert = true;
49190a
+		return str + 1;
49190a
+	}
49190a
+	if (str[0] == '\\' && str[1] == '!') {
49190a
+		*invert = false;
49190a
+		return str + 1;
49190a
+	}
49190a
+	*invert = false;
49190a
+	return str;
49190a
+}
49190a
+
49190a
 int store_ble(vector blist, char * str, int origin)
49190a
 {
49190a
 	struct blentry * ble;
49190a
+	char *regex_str;
49190a
 
49190a
 	if (!str)
49190a
 		return 0;
49190a
@@ -30,7 +45,8 @@ int store_ble(vector blist, char * str, int origin)
49190a
 	if (!ble)
49190a
 		goto out;
49190a
 
49190a
-	if (regcomp(&ble->regex, str, REG_EXTENDED|REG_NOSUB))
49190a
+	regex_str = check_invert(str, &ble->invert);
49190a
+	if (regcomp(&ble->regex, regex_str, REG_EXTENDED|REG_NOSUB))
49190a
 		goto out1;
49190a
 
49190a
 	if (!vector_alloc_slot(blist))
49190a
@@ -66,6 +82,7 @@ int alloc_ble_device(vector blist)
49190a
 int set_ble_device(vector blist, char * vendor, char * product, int origin)
49190a
 {
49190a
 	struct blentry_device * ble;
49190a
+	char *regex_str;
49190a
 
49190a
 	if (!blist)
49190a
 		return 1;
49190a
@@ -76,7 +93,8 @@ int set_ble_device(vector blist, char * vendor, char * product, int origin)
49190a
 		return 1;
49190a
 
49190a
 	if (vendor) {
49190a
-		if (regcomp(&ble->vendor_reg, vendor,
49190a
+		regex_str = check_invert(vendor, &ble->vendor_invert);
49190a
+		if (regcomp(&ble->vendor_reg, regex_str,
49190a
 			    REG_EXTENDED|REG_NOSUB)) {
49190a
 			FREE(vendor);
49190a
 			if (product)
49190a
@@ -86,7 +104,8 @@ int set_ble_device(vector blist, char * vendor, char * product, int origin)
49190a
 		ble->vendor = vendor;
49190a
 	}
49190a
 	if (product) {
49190a
-		if (regcomp(&ble->product_reg, product,
49190a
+		regex_str = check_invert(product, &ble->product_invert);
49190a
+		if (regcomp(&ble->product_reg, regex_str,
49190a
 			    REG_EXTENDED|REG_NOSUB)) {
49190a
 			FREE(product);
49190a
 			if (vendor) {
49190a
@@ -108,7 +127,7 @@ match_reglist (vector blist, const char * str)
49190a
 	struct blentry * ble;
49190a
 
49190a
 	vector_foreach_slot (blist, ble, i) {
49190a
-		if (!regexec(&ble->regex, str, 0, NULL, 0))
49190a
+		if (!!regexec(&ble->regex, str, 0, NULL, 0) == ble->invert)
49190a
 			return 1;
49190a
 	}
49190a
 	return 0;
49190a
@@ -125,9 +144,11 @@ match_reglist_device (const struct _vector *blist, const char * vendor,
49190a
 		if (!ble->vendor && !ble->product)
49190a
 			continue;
49190a
 		if ((!ble->vendor ||
49190a
-		     !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) &&
49190a
+		     !!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) ==
49190a
+		     ble->vendor_invert) &&
49190a
 		    (!ble->product ||
49190a
-		     !regexec(&ble->product_reg, product, 0, NULL, 0)))
49190a
+		     !!regexec(&ble->product_reg, product, 0, NULL, 0) ==
49190a
+		     ble->product_invert))
49190a
 			return 1;
49190a
 	}
49190a
 	return 0;
49190a
@@ -160,13 +181,7 @@ setup_default_blist (struct config * conf)
49190a
 	char * str;
49190a
 	int i;
49190a
 
49190a
-	str = STRDUP("^(ram|zram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]");
49190a
-	if (!str)
49190a
-		return 1;
49190a
-	if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
49190a
-		return 1;
49190a
-
49190a
-	str = STRDUP("^(td|hd|vd)[a-z]");
49190a
+	str = STRDUP("!^(sd[a-z]|dasd[a-z]|nvme[0-9])");
49190a
 	if (!str)
49190a
 		return 1;
49190a
 	if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
49190a
diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h
49190a
index 2d721f60..4305857d 100644
49190a
--- a/libmultipath/blacklist.h
49190a
+++ b/libmultipath/blacklist.h
49190a
@@ -20,6 +20,7 @@
49190a
 struct blentry {
49190a
 	char * str;
49190a
 	regex_t regex;
49190a
+	bool invert;
49190a
 	int origin;
49190a
 };
49190a
 
49190a
@@ -28,6 +29,8 @@ struct blentry_device {
49190a
 	char * product;
49190a
 	regex_t vendor_reg;
49190a
 	regex_t product_reg;
49190a
+	bool vendor_invert;
49190a
+	bool product_invert;
49190a
 	int origin;
49190a
 };
49190a
 
49190a
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
49190a
index 3455b1cc..6dc26f10 100644
49190a
--- a/multipath/multipath.conf.5
49190a
+++ b/multipath/multipath.conf.5
49190a
@@ -1248,6 +1248,16 @@ being handled by multipath-tools.
49190a
 .LP
49190a
 .
49190a
 .
49190a
+In the \fIblacklist\fR and \fIblacklist_exceptions\fR sections, starting a
49190a
+quoted value with an exclamation mark \fB"!"\fR will invert the matching
49190a
+of the rest of the regular expression. For instance, \fB"!^sd[a-z]"\fR will
49190a
+match all values that do not start with \fB"sd[a-z]"\fR. The exclamation mark
49190a
+can be escaped \fB"\\!"\fR to match a literal \fB!\fR at the start of a
49190a
+regular expression. \fBNote:\fR The exclamation mark must be inside quotes,
49190a
+otherwise it will be treated as starting a comment.
49190a
+.LP
49190a
+.
49190a
+.
49190a
 The \fIblacklist_exceptions\fR section is used to revert the actions of the
49190a
 \fIblacklist\fR section. This allows one to selectively include ("whitelist") devices which
49190a
 would normally be excluded via the \fIblacklist\fR section. A common usage is
49190a
@@ -1264,10 +1274,9 @@ unless explicitly stated.
49190a
 Regular expression matching the device nodes to be excluded/included.
49190a
 .RS
49190a
 .PP
49190a
-The default \fIblacklist\fR consists of the regular expressions
49190a
-"^(ram|zram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]" and
49190a
-"^(td|hd|vd)[a-z]". This causes virtual devices, non-disk devices, and some other
49190a
-device types to be excluded from multipath handling by default.
49190a
+The default \fIblacklist\fR consists of the regular expression
49190a
+\fB"!^(sd[a-z]|dasd[a-z]|nvme[0-9])"\fR. This causes all device types other
49190a
+than scsi, dasd, and nvme to be excluded from multipath handling by default.
49190a
 .RE
49190a
 .TP
49190a
 .B wwid
49190a
diff --git a/tests/blacklist.c b/tests/blacklist.c
49190a
index cc8a9a4a..d20e97af 100644
49190a
--- a/tests/blacklist.c
49190a
+++ b/tests/blacklist.c
49190a
@@ -60,20 +60,46 @@ __wrap_udev_list_entry_get_name(struct udev_list_entry *list_entry)
49190a
 	return *(const char **)list_entry;
49190a
 }
49190a
 
49190a
+vector elist_property_default;
49190a
+vector blist_devnode_default;
49190a
 vector blist_devnode_sdb;
49190a
+vector blist_devnode_sdb_inv;
49190a
 vector blist_all;
49190a
 vector blist_device_foo_bar;
49190a
+vector blist_device_foo_inv_bar;
49190a
+vector blist_device_foo_bar_inv;
49190a
 vector blist_device_all;
49190a
 vector blist_wwid_xyzzy;
49190a
+vector blist_wwid_xyzzy_inv;
49190a
 vector blist_protocol_fcp;
49190a
+vector blist_protocol_fcp_inv;
49190a
 vector blist_property_wwn;
49190a
+vector blist_property_wwn_inv;
49190a
 
49190a
 static int setup(void **state)
49190a
 {
49190a
+	struct config conf;
49190a
+
49190a
+	memset(&conf, 0, sizeof(conf));
49190a
+	conf.blist_devnode = vector_alloc();
49190a
+	if (!conf.blist_devnode)
49190a
+		return -1;
49190a
+	conf.elist_property = vector_alloc();
49190a
+	if (!conf.elist_property)
49190a
+		return -1;
49190a
+	if (setup_default_blist(&conf) != 0)
49190a
+		return -1;
49190a
+	elist_property_default = conf.elist_property;
49190a
+	blist_devnode_default = conf.blist_devnode;
49190a
+
49190a
 	blist_devnode_sdb = vector_alloc();
49190a
 	if (!blist_devnode_sdb ||
49190a
 	    store_ble(blist_devnode_sdb, strdup("sdb"), ORIGIN_CONFIG))
49190a
 		return -1;
49190a
+	blist_devnode_sdb_inv = vector_alloc();
49190a
+	if (!blist_devnode_sdb_inv ||
49190a
+	    store_ble(blist_devnode_sdb_inv, strdup("!sdb"), ORIGIN_CONFIG))
49190a
+		return -1;
49190a
 
49190a
 	blist_all = vector_alloc();
49190a
 	if (!blist_all || store_ble(blist_all, strdup(".*"), ORIGIN_CONFIG))
49190a
@@ -84,6 +110,18 @@ static int setup(void **state)
49190a
 	    set_ble_device(blist_device_foo_bar, strdup("foo"), strdup("bar"),
49190a
 			   ORIGIN_CONFIG))
49190a
 		return -1;
49190a
+	blist_device_foo_inv_bar = vector_alloc();
49190a
+	if (!blist_device_foo_inv_bar ||
49190a
+	    alloc_ble_device(blist_device_foo_inv_bar) ||
49190a
+	    set_ble_device(blist_device_foo_inv_bar, strdup("!foo"),
49190a
+			   strdup("bar"), ORIGIN_CONFIG))
49190a
+		return -1;
49190a
+	blist_device_foo_bar_inv = vector_alloc();
49190a
+	if (!blist_device_foo_bar_inv ||
49190a
+	    alloc_ble_device(blist_device_foo_bar_inv) ||
49190a
+	    set_ble_device(blist_device_foo_bar_inv, strdup("foo"),
49190a
+			   strdup("!bar"), ORIGIN_CONFIG))
49190a
+		return -1;
49190a
 
49190a
 	blist_device_all = vector_alloc();
49190a
 	if (!blist_device_all || alloc_ble_device(blist_device_all) ||
49190a
@@ -95,29 +133,50 @@ static int setup(void **state)
49190a
 	if (!blist_wwid_xyzzy ||
49190a
 	    store_ble(blist_wwid_xyzzy, strdup("xyzzy"), ORIGIN_CONFIG))
49190a
 		return -1;
49190a
+	blist_wwid_xyzzy_inv = vector_alloc();
49190a
+	if (!blist_wwid_xyzzy_inv ||
49190a
+	    store_ble(blist_wwid_xyzzy_inv, strdup("!xyzzy"), ORIGIN_CONFIG))
49190a
+		return -1;
49190a
 
49190a
 	blist_protocol_fcp = vector_alloc();
49190a
 	if (!blist_protocol_fcp ||
49190a
 	    store_ble(blist_protocol_fcp, strdup("scsi:fcp"), ORIGIN_CONFIG))
49190a
 		return -1;
49190a
+	blist_protocol_fcp_inv = vector_alloc();
49190a
+	if (!blist_protocol_fcp_inv ||
49190a
+	    store_ble(blist_protocol_fcp_inv, strdup("!scsi:fcp"),
49190a
+		      ORIGIN_CONFIG))
49190a
+		return -1;
49190a
 
49190a
 	blist_property_wwn = vector_alloc();
49190a
 	if (!blist_property_wwn ||
49190a
 	    store_ble(blist_property_wwn, strdup("ID_WWN"), ORIGIN_CONFIG))
49190a
 		return -1;
49190a
+	blist_property_wwn_inv = vector_alloc();
49190a
+	if (!blist_property_wwn_inv ||
49190a
+	    store_ble(blist_property_wwn_inv, strdup("!ID_WWN"), ORIGIN_CONFIG))
49190a
+		return -1;
49190a
 
49190a
 	return 0;
49190a
 }
49190a
 
49190a
 static int teardown(void **state)
49190a
 {
49190a
+	free_blacklist(elist_property_default);
49190a
+	free_blacklist(blist_devnode_default);
49190a
 	free_blacklist(blist_devnode_sdb);
49190a
+	free_blacklist(blist_devnode_sdb_inv);
49190a
 	free_blacklist(blist_all);
49190a
 	free_blacklist_device(blist_device_foo_bar);
49190a
+	free_blacklist_device(blist_device_foo_inv_bar);
49190a
+	free_blacklist_device(blist_device_foo_bar_inv);
49190a
 	free_blacklist_device(blist_device_all);
49190a
 	free_blacklist(blist_wwid_xyzzy);
49190a
+	free_blacklist(blist_wwid_xyzzy_inv);
49190a
 	free_blacklist(blist_protocol_fcp);
49190a
+	free_blacklist(blist_protocol_fcp_inv);
49190a
 	free_blacklist(blist_property_wwn);
49190a
+	free_blacklist(blist_property_wwn_inv);
49190a
 	return 0;
49190a
 }
49190a
 
49190a
@@ -141,6 +200,11 @@ static void test_devnode_blacklist(void **state)
49190a
 	expect_condlog(3, "sdb: device node name blacklisted\n");
49190a
 	assert_int_equal(filter_devnode(blist_devnode_sdb, NULL, "sdb"),
49190a
 			 MATCH_DEVNODE_BLIST);
49190a
+	assert_int_equal(filter_devnode(blist_devnode_sdb_inv, NULL, "sdb"),
49190a
+			 MATCH_NOTHING);
49190a
+	expect_condlog(3, "sdc: device node name blacklisted\n");
49190a
+	assert_int_equal(filter_devnode(blist_devnode_sdb_inv, NULL, "sdc"),
49190a
+			 MATCH_DEVNODE_BLIST);
49190a
 }
49190a
 
49190a
 static void test_devnode_whitelist(void **state)
49190a
@@ -159,12 +223,39 @@ static void test_devnode_missing(void **state)
49190a
 			 MATCH_NOTHING);
49190a
 }
49190a
 
49190a
+static void test_devnode_default(void **state)
49190a
+{
49190a
+	assert_int_equal(filter_devnode(blist_devnode_default, NULL, "sdaa"),
49190a
+			 MATCH_NOTHING);
49190a
+	assert_int_equal(filter_devnode(blist_devnode_default, NULL, "nvme0n1"),
49190a
+			 MATCH_NOTHING);
49190a
+	assert_int_equal(filter_devnode(blist_devnode_default, NULL, "dasda"),
49190a
+			 MATCH_NOTHING);
49190a
+	expect_condlog(3, "hda: device node name blacklisted\n");
49190a
+	assert_int_equal(filter_devnode(blist_devnode_default, NULL, "hda"),
49190a
+			 MATCH_DEVNODE_BLIST);
49190a
+}
49190a
+
49190a
 static void test_device_blacklist(void **state)
49190a
 {
49190a
 	expect_condlog(3, "sdb: (foo:bar) vendor/product blacklisted\n");
49190a
 	assert_int_equal(filter_device(blist_device_foo_bar, NULL, "foo",
49190a
 				       "bar", "sdb"),
49190a
 			 MATCH_DEVICE_BLIST);
49190a
+	assert_int_equal(filter_device(blist_device_foo_inv_bar, NULL, "foo",
49190a
+				        "bar", "sdb"),
49190a
+			 MATCH_NOTHING);
49190a
+	assert_int_equal(filter_device(blist_device_foo_bar_inv, NULL, "foo",
49190a
+				        "bar", "sdb"),
49190a
+			 MATCH_NOTHING);
49190a
+	expect_condlog(3, "sdb: (baz:bar) vendor/product blacklisted\n");
49190a
+	assert_int_equal(filter_device(blist_device_foo_inv_bar, NULL, "baz",
49190a
+				        "bar", "sdb"),
49190a
+			 MATCH_DEVICE_BLIST);
49190a
+	expect_condlog(3, "sdb: (foo:baz) vendor/product blacklisted\n");
49190a
+	assert_int_equal(filter_device(blist_device_foo_bar_inv, NULL, "foo",
49190a
+				        "baz", "sdb"),
49190a
+			 MATCH_DEVICE_BLIST);
49190a
 }
49190a
 
49190a
 static void test_device_whitelist(void **state)
49190a
@@ -191,6 +282,11 @@ static void test_wwid_blacklist(void **state)
49190a
 	expect_condlog(3, "sdb: wwid xyzzy blacklisted\n");
49190a
 	assert_int_equal(filter_wwid(blist_wwid_xyzzy, NULL, "xyzzy", "sdb"),
49190a
 			 MATCH_WWID_BLIST);
49190a
+	assert_int_equal(filter_wwid(blist_wwid_xyzzy_inv, NULL, "xyzzy",
49190a
+				     "sdb"), MATCH_NOTHING);
49190a
+	expect_condlog(3, "sdb: wwid plugh blacklisted\n");
49190a
+	assert_int_equal(filter_wwid(blist_wwid_xyzzy_inv, NULL, "plugh",
49190a
+				     "sdb"), MATCH_WWID_BLIST);
49190a
 }
49190a
 
49190a
 static void test_wwid_whitelist(void **state)
49190a
@@ -218,6 +314,12 @@ static void test_protocol_blacklist(void **state)
49190a
 	expect_condlog(3, "sdb: protocol scsi:fcp blacklisted\n");
49190a
 	assert_int_equal(filter_protocol(blist_protocol_fcp, NULL, &pp),
49190a
 			 MATCH_PROTOCOL_BLIST);
49190a
+	assert_int_equal(filter_protocol(blist_protocol_fcp_inv, NULL, &pp),
49190a
+			 MATCH_NOTHING);
49190a
+	pp.sg_id.proto_id = SCSI_PROTOCOL_ATA;
49190a
+	expect_condlog(3, "sdb: protocol scsi:ata blacklisted\n");
49190a
+	assert_int_equal(filter_protocol(blist_protocol_fcp_inv, NULL, &pp),
49190a
+			 MATCH_PROTOCOL_BLIST);
49190a
 }
49190a
 
49190a
 static void test_protocol_whitelist(void **state)
49190a
@@ -245,10 +347,17 @@ static void test_protocol_missing(void **state)
49190a
 static void test_property_blacklist(void **state)
49190a
 {
49190a
 	static struct udev_device udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } };
49190a
+	static struct udev_device udev_inv = { "sdb", { "ID_WWN", NULL } };
49190a
 	conf.blist_property = blist_property_wwn;
49190a
 	expect_condlog(3, "sdb: udev property ID_WWN blacklisted\n");
49190a
 	assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
49190a
 			 MATCH_PROPERTY_BLIST);
49190a
+	conf.blist_property = blist_property_wwn_inv;
49190a
+	expect_condlog(3, "sdb: udev property ID_FOO blacklisted\n");
49190a
+	assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
49190a
+			 MATCH_PROPERTY_BLIST);
49190a
+	assert_int_equal(filter_property(&conf, &udev_inv, 3, "ID_SERIAL"),
49190a
+			 MATCH_NOTHING);
49190a
 }
49190a
 
49190a
 /* the property check works different in that you check all the property
49190a
@@ -482,6 +591,7 @@ int test_blacklist(void)
49190a
 		cmocka_unit_test(test_devnode_blacklist),
49190a
 		cmocka_unit_test(test_devnode_whitelist),
49190a
 		cmocka_unit_test(test_devnode_missing),
49190a
+		cmocka_unit_test(test_devnode_default),
49190a
 		cmocka_unit_test(test_device_blacklist),
49190a
 		cmocka_unit_test(test_device_whitelist),
49190a
 		cmocka_unit_test(test_device_missing),
49190a
diff --git a/tests/test-lib.c b/tests/test-lib.c
49190a
index 59275163..08ff2d8d 100644
49190a
--- a/tests/test-lib.c
49190a
+++ b/tests/test-lib.c
49190a
@@ -15,7 +15,7 @@
49190a
 #include "test-lib.h"
49190a
 
49190a
 const int default_mask = (DI_SYSFS|DI_BLACKLIST|DI_WWID|DI_CHECKER|DI_PRIO);
49190a
-const char default_devnode[] = "sdTEST";
49190a
+const char default_devnode[] = "sdxTEST";
49190a
 const char default_wwid[] = "TEST-WWID";
49190a
 /* default_wwid should be a substring of default_wwid_1! */
49190a
 const char default_wwid_1[] = "TEST-WWID-1";
49190a
-- 
49190a
2.17.2
49190a