Linux Device Mapper development
 help / color / mirror / Atom feed
* [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework
@ 2023-09-01 18:02 mwilck
  2023-09-01 18:02 ` [dm-devel] [PATCH 01/21] libmultipath: sysfs_set_scsi_tmo: do nothing for ACT_DRY_RUN mwilck
                   ` (20 more replies)
  0 siblings, 21 replies; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

This patch set contains a two-step rework of the user-friendly
names code. Patch 2-5 change the current code such that it (well, almost)
never attempts to use an alias that is currently in use by another
map. We already have some checks for this, but they don't cover all
possible scenarios. Patch 6-10 add some units tests for
get_user_friendly_alias(), and restructure the unit tests somewhat.

In the second part of the set, Patch 11-18 restructure the alias code such
that we don't read the bindings file every time an alias is
requested. Instead, we just use a memory cache of the current bindings, and
re-write the file only if a new binding is added.  This reduces the number of
system calls and simplifies the code.

There are some side effects, which are discussed in the commit message of
patch 18. Most importantly, multipathd will not pick up changes to the
bindings file immediately after running "multipath -a" or "multipath -w".

Patch 19 applies the changes to the unit tests that become
necessary because of the previous patches. Patch 20/21 add another
optimization for the alias_already_taken() test.

Patch 1 is a separate, independent fix.

NOTE: my main test bed is currently unavailable, therefore this set has
yet reveived less testing than usual. I consider this a v1 series and
will do more testing while the review process is going on.

Reviews and comments welcome.

Martin Wilck (21):
  libmultipath: sysfs_set_scsi_tmo: do nothing for ACT_DRY_RUN
  libmultipath: add alias_already_taken()
  libmultipath: unify use_existing_alias() and get_user_friendly_alias()
  libmultipath: never allocate an alias that's already taken
  libmultipath: lookup_binding: add comment about the algorithm
  multipath-tools test: simplify debugging for condlog mismatch
  multipath-tools tests: add tests for get_user_friendly_alias()
  multipath-tools test: consistent use of macros in alias test
  multipath-tools tests: convert mock_{failed,used}_alias to macros
  multipath-tools test: use mock_bindings_file() consistently
  libmultipath: add global variable for current bindings
  libmultipath: rename fix_bindings_file() to update_bindings_file()
  libmultipath: alias.c: move bindings related code up
  libmultipath: update_bindings_file: take filename argument
  libmultipath: update_bindings_file: use a single write()
  libmultipath: update_bindings_file: don't log temp file name
  libmultipath: alias.c: factor out read_binding()
  libmultipath: keep bindings in memory
  multipath-tools tests: fix alias tests
  libmultipath: dm_get_uuid(): return emtpy UUID for non-existing maps
  libmultipath: adapt to new semantics of dm_get_uuid()

 libmultipath/alias.c              |  794 ++++++++---------
 libmultipath/alias.h              |   10 +-
 libmultipath/configure.c          |    7 +-
 libmultipath/devmapper.c          |   10 +-
 libmultipath/discovery.c          |    3 +
 libmultipath/libmultipath.version |    1 +
 libmultipath/propsel.c            |   19 +-
 multipath/main.c                  |    2 +
 multipathd/main.c                 |    1 +
 tests/alias.c                     | 1366 ++++++++++++++++++++++-------
 tests/test-log.c                  |    4 +-
 11 files changed, 1491 insertions(+), 726 deletions(-)

-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 01/21] libmultipath: sysfs_set_scsi_tmo: do nothing for ACT_DRY_RUN
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:42   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 02/21] libmultipath: add alias_already_taken() mwilck
                   ` (19 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: dm-devel, Jehan Singh, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

"multipath -d" might change sysfs timeouts of SCSI devices.
Make sure it doesn't.

Signed-off-by: Martin Wilck <mwilck@suse.com>
Cc: Jehan Singh <jehan.singh@suse.com>
---
 libmultipath/configure.c | 4 ++--
 libmultipath/discovery.c | 3 +++
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/libmultipath/configure.c b/libmultipath/configure.c
index 9513baa..029fbbd 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -1193,13 +1193,13 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
 
 		if (cmpp)
 			mpp->queue_mode = cmpp->queue_mode;
+		if (cmd == CMD_DRY_RUN && mpp->action == ACT_UNDEF)
+			mpp->action = ACT_DRY_RUN;
 		if (setup_map(mpp, &params, vecs)) {
 			remove_map(mpp, vecs->pathvec, NULL);
 			continue;
 		}
 
-		if (cmd == CMD_DRY_RUN)
-			mpp->action = ACT_DRY_RUN;
 		if (mpp->action == ACT_UNDEF)
 			select_action(mpp, curmp,
 				      force_reload == FORCE_RELOAD_YES ? 1 : 0);
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index e4de48e..84ce5fe 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -857,6 +857,9 @@ sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp)
 	bool warn_dev_loss = false;
 	bool warn_fast_io_fail = false;
 
+	if (mpp->action == ACT_DRY_RUN || mpp->action == ACT_REJECT)
+		return 0;
+
 	if (mpp->no_path_retry > 0) {
 		uint64_t no_path_retry_tmo =
 			(uint64_t)mpp->no_path_retry * conf->checkint;
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 02/21] libmultipath: add alias_already_taken()
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
  2023-09-01 18:02 ` [dm-devel] [PATCH 01/21] libmultipath: sysfs_set_scsi_tmo: do nothing for ACT_DRY_RUN mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:42   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 03/21] libmultipath: unify use_existing_alias() and get_user_friendly_alias() mwilck
                   ` (18 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck, David Bond

From: Martin Wilck <mwilck@suse.com>

Factor out a trivial helper function.

Signed-off-by: Martin Wilck <mwilck@suse.com>
Cc: David Bond <dbond@suse.com>
---
 libmultipath/alias.c | 34 ++++++++++++++++++++--------------
 1 file changed, 20 insertions(+), 14 deletions(-)

diff --git a/libmultipath/alias.c b/libmultipath/alias.c
index c0139a2..abde08c 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -8,6 +8,7 @@
 #include <string.h>
 #include <limits.h>
 #include <stdio.h>
+#include <stdbool.h>
 
 #include "debug.h"
 #include "util.h"
@@ -109,8 +110,24 @@ scan_devname(const char *alias, const char *prefix)
 	return n;
 }
 
-static int
-id_already_taken(int id, const char *prefix, const char *map_wwid)
+static bool alias_already_taken(const char *alias, const char *map_wwid)
+{
+
+	if (dm_map_present(alias)) {
+		char wwid[WWID_SIZE];
+
+		/* If both the name and the wwid match, then it's fine.*/
+		if (dm_get_uuid(alias, wwid, sizeof(wwid)) == 0 &&
+		    strncmp(map_wwid, wwid, sizeof(wwid)) == 0)
+			return false;
+		condlog(3, "%s: alias '%s' already taken, but not in bindings file. reselecting alias",
+			map_wwid, alias);
+		return true;
+	}
+	return false;
+}
+
+static bool id_already_taken(int id, const char *prefix, const char *map_wwid)
 {
 	STRBUF_ON_STACK(buf);
 	const char *alias;
@@ -120,20 +137,9 @@ id_already_taken(int id, const char *prefix, const char *map_wwid)
 		return 0;
 
 	alias = get_strbuf_str(&buf);
-	if (dm_map_present(alias)) {
-		char wwid[WWID_SIZE];
-
-		/* If both the name and the wwid match, then it's fine.*/
-		if (dm_get_uuid(alias, wwid, sizeof(wwid)) == 0 &&
-		    strncmp(map_wwid, wwid, sizeof(wwid)) == 0)
-			return 0;
-		condlog(3, "%s: alias '%s' already taken, but not in bindings file. reselecting alias", map_wwid, alias);
-		return 1;
-	}
-	return 0;
+	return alias_already_taken(alias, map_wwid);
 }
 
-
 /*
  * Returns: 0   if matching entry in WWIDs file found
  *         -1   if an error occurs
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 03/21] libmultipath: unify use_existing_alias() and get_user_friendly_alias()
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
  2023-09-01 18:02 ` [dm-devel] [PATCH 01/21] libmultipath: sysfs_set_scsi_tmo: do nothing for ACT_DRY_RUN mwilck
  2023-09-01 18:02 ` [dm-devel] [PATCH 02/21] libmultipath: add alias_already_taken() mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:42   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 04/21] libmultipath: never allocate an alias that's already taken mwilck
                   ` (17 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck, David Bond

From: Martin Wilck <mwilck@suse.com>

These functions are only called from select_alias(). The logic
is more obvious when unified in a single function.

Signed-off-by: Martin Wilck <mwilck@suse.com>
Cc: David Bond <dbond@suse.com>
---
 libmultipath/alias.c   | 82 ++++++++++++------------------------------
 libmultipath/alias.h   |  9 ++---
 libmultipath/propsel.c | 19 +++++-----
 3 files changed, 34 insertions(+), 76 deletions(-)

diff --git a/libmultipath/alias.c b/libmultipath/alias.c
index abde08c..9b9b789 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -329,13 +329,13 @@ allocate_binding(int fd, const char *wwid, int id, const char *prefix)
 	return alias;
 }
 
-char *
-use_existing_alias (const char *wwid, const char *file, const char *alias_old,
-		    const char *prefix, int bindings_read_only)
+char *get_user_friendly_alias(const char *wwid, const char *file, const char *alias_old,
+			      const char *prefix, bool bindings_read_only)
 {
 	char *alias = NULL;
 	int id = 0;
 	int fd, can_write;
+	bool new_binding = false;
 	char buff[WWID_SIZE];
 	FILE *f;
 
@@ -349,6 +349,10 @@ use_existing_alias (const char *wwid, const char *file, const char *alias_old,
 		close(fd);
 		return NULL;
 	}
+
+	if (!strlen(alias_old))
+		goto new_alias;
+
 	/* lookup the binding. if it exists, the wwid will be in buff
 	 * either way, id contains the id for the alias
 	 */
@@ -358,14 +362,14 @@ use_existing_alias (const char *wwid, const char *file, const char *alias_old,
 		/* if buff is our wwid, it's already
 		 * allocated correctly
 		 */
-		if (strcmp(buff, wwid) == 0)
+		if (strcmp(buff, wwid) == 0) {
 			alias = strdup(alias_old);
-		else {
-			alias = NULL;
+			goto out;
+		} else {
 			condlog(0, "alias %s already bound to wwid %s, cannot reuse",
 				alias_old, buff);
+			goto new_alias;
 		}
-		goto out;
 	}
 
 	id = lookup_binding(f, wwid, &alias, NULL, 0);
@@ -377,8 +381,15 @@ use_existing_alias (const char *wwid, const char *file, const char *alias_old,
 
 	/* allocate the existing alias in the bindings file */
 	id = scan_devname(alias_old, prefix);
-	if (id <= 0)
-		goto out;
+
+new_alias:
+	if (id <= 0) {
+		id = lookup_binding(f, wwid, &alias, prefix, 1);
+		if (id <= 0)
+			goto out;
+		else
+			new_binding = true;
+	}
 
 	if (fflush(f) != 0) {
 		condlog(0, "cannot fflush bindings file stream : %s",
@@ -388,8 +399,9 @@ use_existing_alias (const char *wwid, const char *file, const char *alias_old,
 
 	if (can_write && !bindings_read_only) {
 		alias = allocate_binding(fd, wwid, id, prefix);
-		condlog(0, "Allocated existing binding [%s] for WWID [%s]",
-			alias, wwid);
+		if (alias && !new_binding)
+			condlog(2, "Allocated existing binding [%s] for WWID [%s]",
+				alias, wwid);
 	}
 
 out:
@@ -399,54 +411,6 @@ out:
 	return alias;
 }
 
-char *
-get_user_friendly_alias(const char *wwid, const char *file, const char *prefix,
-			int bindings_read_only)
-{
-	char *alias;
-	int fd, id;
-	FILE *f;
-	int can_write;
-
-	if (!wwid || *wwid == '\0') {
-		condlog(3, "Cannot find binding for empty WWID");
-		return NULL;
-	}
-
-	fd = open_file(file, &can_write, bindings_file_header);
-	if (fd < 0)
-		return NULL;
-
-	f = fdopen(fd, "r");
-	if (!f) {
-		condlog(0, "cannot fdopen on bindings file descriptor : %s",
-			strerror(errno));
-		close(fd);
-		return NULL;
-	}
-
-	id = lookup_binding(f, wwid, &alias, prefix, 1);
-	if (id < 0) {
-		fclose(f);
-		return NULL;
-	}
-
-	pthread_cleanup_push(free, alias);
-
-	if (fflush(f) != 0) {
-		condlog(0, "cannot fflush bindings file stream : %s",
-			strerror(errno));
-		free(alias);
-		alias = NULL;
-	} else if (can_write && !bindings_read_only && !alias)
-		alias = allocate_binding(fd, wwid, id, prefix);
-
-	fclose(f);
-
-	pthread_cleanup_pop(0);
-	return alias;
-}
-
 int
 get_user_friendly_wwid(const char *alias, char *buff, const char *file)
 {
diff --git a/libmultipath/alias.h b/libmultipath/alias.h
index dbc950c..fa33223 100644
--- a/libmultipath/alias.h
+++ b/libmultipath/alias.h
@@ -2,13 +2,10 @@
 #define _ALIAS_H
 
 int valid_alias(const char *alias);
-char *get_user_friendly_alias(const char *wwid, const char *file,
-			      const char *prefix,
-			      int bindings_readonly);
 int get_user_friendly_wwid(const char *alias, char *buff, const char *file);
-char *use_existing_alias (const char *wwid, const char *file,
-			  const char *alias_old,
-			  const char *prefix, int bindings_read_only);
+char *get_user_friendly_alias(const char *wwid, const char *file,
+			      const char *alias_old,
+			      const char *prefix, bool bindings_read_only);
 
 struct config;
 int check_alias_settings(const struct config *);
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
index d6bce12..354e883 100644
--- a/libmultipath/propsel.c
+++ b/libmultipath/propsel.c
@@ -401,19 +401,16 @@ int select_alias(struct config *conf, struct multipath * mp)
 
 	select_alias_prefix(conf, mp);
 
-	if (strlen(mp->alias_old) > 0) {
-		mp->alias = use_existing_alias(mp->wwid, conf->bindings_file,
-				mp->alias_old, mp->alias_prefix,
-				conf->bindings_read_only);
-		memset (mp->alias_old, 0, WWID_SIZE);
-		origin = "(setting: using existing alias)";
-	}
+	mp->alias = get_user_friendly_alias(mp->wwid, conf->bindings_file,
+					    mp->alias_old, mp->alias_prefix,
+					    conf->bindings_read_only);
 
-	if (mp->alias == NULL) {
-		mp->alias = get_user_friendly_alias(mp->wwid,
-				conf->bindings_file, mp->alias_prefix, conf->bindings_read_only);
+	if (mp->alias && !strncmp(mp->alias, mp->alias_old, WWID_SIZE))
+		origin = "(setting: using existing alias)";
+	else if (mp->alias)
 		origin = "(setting: user_friendly_name)";
-	}
+	memset (mp->alias_old, 0, WWID_SIZE);
+
 out:
 	if (mp->alias == NULL) {
 		mp->alias = strdup(mp->wwid);
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 04/21] libmultipath: never allocate an alias that's already taken
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (2 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 03/21] libmultipath: unify use_existing_alias() and get_user_friendly_alias() mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:42   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 05/21] libmultipath: lookup_binding: add comment about the algorithm mwilck
                   ` (16 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck, David Bond

From: Martin Wilck <mwilck@suse.com>

If the bindings file is changed in a way that multipathd can't handle
(e.g. by swapping the aliases of two maps), multipathd must not try
to re-use an alias that is already used by another map. Creating
or renaming a map with such an alias will fail. We already avoid
this for some cases, but not for all. Fix it.

Signed-off-by: Martin Wilck <mwilck@suse.com>
Cc: David Bond <dbond@suse.com>
---
 libmultipath/alias.c | 36 +++++++++++++++++++++++++++++++-----
 tests/alias.c        |  2 +-
 2 files changed, 32 insertions(+), 6 deletions(-)

diff --git a/libmultipath/alias.c b/libmultipath/alias.c
index 9b9b789..f7834d1 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -120,7 +120,7 @@ static bool alias_already_taken(const char *alias, const char *map_wwid)
 		if (dm_get_uuid(alias, wwid, sizeof(wwid)) == 0 &&
 		    strncmp(map_wwid, wwid, sizeof(wwid)) == 0)
 			return false;
-		condlog(3, "%s: alias '%s' already taken, but not in bindings file. reselecting alias",
+		condlog(3, "%s: alias '%s' already taken, reselecting alias",
 			map_wwid, alias);
 		return true;
 	}
@@ -363,28 +363,54 @@ char *get_user_friendly_alias(const char *wwid, const char *file, const char *al
 		 * allocated correctly
 		 */
 		if (strcmp(buff, wwid) == 0) {
-			alias = strdup(alias_old);
+			if (!alias_already_taken(alias_old, wwid))
+				alias = strdup(alias_old);
+			else
+				condlog(0, "ERROR: old alias [%s] for wwid [%s] is used by other map",
+					alias_old, wwid);
 			goto out;
+
 		} else {
 			condlog(0, "alias %s already bound to wwid %s, cannot reuse",
 				alias_old, buff);
-			goto new_alias;
+			goto new_alias;		     ;
 		}
 	}
 
+	/*
+	 * Look for an existing alias in the bindings file.
+	 * Pass prefix = NULL, so lookup_binding() won't try to allocate a new id.
+	 */
 	id = lookup_binding(f, wwid, &alias, NULL, 0);
 	if (alias) {
-		condlog(3, "Use existing binding [%s] for WWID [%s]",
-			alias, wwid);
+		if (alias_already_taken(alias, wwid)) {
+			free(alias);
+			alias = NULL;
+		} else
+			condlog(3, "Use existing binding [%s] for WWID [%s]",
+				alias, wwid);
 		goto out;
 	}
 
 	/* allocate the existing alias in the bindings file */
 	id = scan_devname(alias_old, prefix);
+	if (id > 0 && id_already_taken(id, prefix, wwid)) {
+		condlog(0, "ERROR: old alias [%s] for wwid [%s] is used by other map",
+			alias_old, wwid);
+		goto out;
+	}
 
 new_alias:
 	if (id <= 0) {
+		/*
+		 * no existing alias was provided, or allocating it
+		 * failed. Try a new one.
+		 */
 		id = lookup_binding(f, wwid, &alias, prefix, 1);
+		if (id == 0 && alias_already_taken(alias, wwid)) {
+			free(alias);
+			alias = NULL;
+		}
 		if (id <= 0)
 			goto out;
 		else
diff --git a/tests/alias.c b/tests/alias.c
index 3ca6c28..11f209e 100644
--- a/tests/alias.c
+++ b/tests/alias.c
@@ -398,7 +398,7 @@ static void mock_self_alias(const char *alias, const char *wwid)
 	will_return(__wrap_dm_get_uuid, wwid);
 }
 
-#define USED_STR(alias_str, wwid_str) wwid_str ": alias '" alias_str "' already taken, but not in bindings file. reselecting alias\n"
+#define USED_STR(alias_str, wwid_str) wwid_str ": alias '" alias_str "' already taken, reselecting alias\n"
 
 static void mock_failed_alias(const char *alias, char *msg)
 {
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 05/21] libmultipath: lookup_binding: add comment about the algorithm
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (3 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 04/21] libmultipath: never allocate an alias that's already taken mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:43   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 06/21] multipath-tools test: simplify debugging for condlog mismatch mwilck
                   ` (15 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

When I read this code, I always get confused. Adding comments to
explain the algorithm.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/alias.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/libmultipath/alias.c b/libmultipath/alias.c
index f7834d1..e61eb91 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -172,6 +172,41 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
 		alias = strtok_r(buf, " \t", &saveptr);
 		if (!alias) /* blank line */
 			continue;
+
+		/*
+		 * Find an unused index - explanation of the algorithm
+		 *
+		 * ID: 1 = mpatha, 2 = mpathb, ...
+		 *
+		 * We assume the bindings are unsorted. The only constraint
+		 * is that no ID occurs more than once. IDs that occur in the
+		 * bindings are called "used".
+		 *
+		 * We call the list 1,2,3,..., exactly in this order, the list
+		 * of "expected" IDs. The variable "id" always holds the next
+		 * "expected" ID, IOW the last "expected" ID encountered plus 1.
+		 * Thus all IDs below "id" are known to be used. However, at the
+		 * end of the loop, the value of "id" isn't necessarily unused.
+		 *
+		 * "smallest_bigger_id" is the smallest used ID that was
+		 * encountered while it was larger than the next "expected" ID
+		 * at that iteration. Let X be some used ID. If all IDs below X
+		 * are used and encountered in the right sequence before X, "id"
+		 * will be > X when the loop ends. Otherwise, X was encountered
+		 * "out of order", the condition (X > id) holds when X is
+		 * encountered, and "smallest_bigger_id" will be set to X; i.e.
+		 * it will be less or equal than X when the loop ends.
+		 *
+		 * At the end of the loop, (id < smallest_bigger_id) means that
+		 * the value of "id" had been encountered neither in order nor
+		 * out of order, and is thus unused. (id >= smallest_bigger_id)
+		 * means that "id"'s value is in use. In this case, we play safe
+		 * and use "biggest_id + 1" as the next value to try.
+		 *
+		 * biggest_id is always > smallest_bigger_id, except in the
+		 * "perfectly ordered" case.
+		 */
+
 		curr_id = scan_devname(alias, prefix);
 		if (curr_id == id) {
 			if (id < INT_MAX)
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 06/21] multipath-tools test: simplify debugging for condlog mismatch
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (4 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 05/21] libmultipath: lookup_binding: add comment about the algorithm mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:43   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 07/21] multipath-tools tests: add tests for get_user_friendly_alias() mwilck
                   ` (14 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

If there's a mismatch between expected and actual log message,
print both messages.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 tests/test-log.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tests/test-log.c b/tests/test-log.c
index c174587..6351699 100644
--- a/tests/test-log.c
+++ b/tests/test-log.c
@@ -16,12 +16,14 @@ void __wrap_dlog (int prio, const char * fmt, ...)
 	va_list ap;
 	char *expected;
 
-	check_expected(prio);
 	va_start(ap, fmt);
 	vsnprintf(buff, MAX_MSG_SIZE, fmt, ap);
 	va_end(ap);
 	fprintf(stderr, "%s(%d): %s", __func__, prio, buff);
 	expected = mock_ptr_type(char *);
+	if (memcmp(expected, buff, strlen(expected)))
+		fprintf(stderr, "%s(expected): %s", __func__, expected);
+	check_expected(prio);
 	assert_memory_equal(buff, expected, strlen(expected));
 }
 
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 07/21] multipath-tools tests: add tests for get_user_friendly_alias()
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (5 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 06/21] multipath-tools test: simplify debugging for condlog mismatch mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:43   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 08/21] multipath-tools test: consistent use of macros in alias test mwilck
                   ` (13 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 tests/alias.c | 531 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 531 insertions(+)

diff --git a/tests/alias.c b/tests/alias.c
index 11f209e..e2372d1 100644
--- a/tests/alias.c
+++ b/tests/alias.c
@@ -81,6 +81,35 @@ int __wrap_dm_get_uuid(const char *name, char *uuid, int uuid_len)
 	return ret;
 }
 
+#define TEST_FDNO 1234
+#define TEST_FPTR ((FILE *) 0xaffe)
+
+int __wrap_open_file(const char *file, int *can_write, const char *header)
+{
+	int cw = mock_type(int);
+
+        *can_write = cw;
+	return TEST_FDNO;
+}
+
+FILE *__wrap_fdopen(int fd, const char *mode)
+{
+	assert_int_equal(fd, TEST_FDNO);
+	return TEST_FPTR;
+}
+
+int __wrap_fflush(FILE *f)
+{
+	assert_ptr_equal(f, TEST_FPTR);
+	return 0;
+}
+
+int __wrap_fclose(FILE *f)
+{
+	assert_ptr_equal(f, TEST_FPTR);
+	return 0;
+}
+
 /* strbuf wrapper for the old format_devname() */
 static int __format_devname(char *name, int id, size_t len, const char *prefix)
 {
@@ -399,6 +428,22 @@ static void mock_self_alias(const char *alias, const char *wwid)
 }
 
 #define USED_STR(alias_str, wwid_str) wwid_str ": alias '" alias_str "' already taken, reselecting alias\n"
+#define NOMATCH_STR(alias_str) ("No matching alias [" alias_str "] in bindings file.\n")
+#define FOUND_STR(alias_str, wwid_str)				\
+	"Found matching wwid [" wwid_str "] in bindings file."	\
+	" Setting alias to " alias_str "\n"
+#define FOUND_ALIAS_STR(alias_str, wwid_str)				\
+	"Found matching alias [" alias_str "] in bindings file."	\
+	" Setting wwid to " wwid_str "\n"
+#define NOMATCH_WWID_STR(wwid_str) ("No matching wwid [" wwid_str "] in bindings file.\n")
+#define NEW_STR(alias_str, wwid_str) ("Created new binding [" alias_str "] for WWID [" wwid_str "]\n")
+#define EXISTING_STR(alias_str, wwid_str) ("Use existing binding [" alias_str "] for WWID [" wwid_str "]\n")
+#define ALLOC_STR(alias_str, wwid_str) ("Allocated existing binding [" alias_str "] for WWID [" wwid_str "]\n")
+#define BINDING_STR(alias_str, wwid_str) (alias_str " " wwid_str "\n")
+#define BOUND_STR(alias_str, wwid_str) ("alias "alias_str " already bound to wwid " wwid_str ", cannot reuse")
+#define ERR_STR(alias_str, wwid_str) ("ERROR: old alias [" alias_str "] for wwid [" wwid_str "] is used by other map\n")
+#define REUSE_STR(alias_str, wwid_str) ("alias " alias_str " already bound to wwid " wwid_str ", cannot reuse\n")
+#define NOMORE_STR "no more available user_friendly_names\n"
 
 static void mock_failed_alias(const char *alias, char *msg)
 {
@@ -421,6 +466,24 @@ static void mock_used_alias(const char *alias, char *msg)
 	expect_condlog(3, msg);
 }
 
+static void mock_bindings_file(const char *content, int match_line)
+{
+	static char cnt[1024];
+	char *token;
+	int i;
+
+	assert_in_range(strlcpy(cnt, content, sizeof(cnt)), 0, sizeof(cnt) - 1);
+
+	for (token = strtok(cnt, "\n"), i = 0;
+	     token && *token;
+	     token = strtok(NULL, "\n"), i++) {
+		will_return(__wrap_fgets, token);
+		if (match_line == i)
+			return;
+	}
+	will_return(__wrap_fgets, NULL);
+}
+
 static void lb_empty(void **state)
 {
 	int rc;
@@ -1147,6 +1210,472 @@ static int test_allocate_binding(void)
 	return cmocka_run_group_tests(tests, NULL, NULL);
 }
 
+#define mock_allocate_binding(alias, wwid)				\
+	do {								\
+		static const char ln[] = BINDING_STR(alias, wwid);	\
+									\
+		will_return(__wrap_lseek, 0);				\
+		expect_value(__wrap_write, count, strlen(ln));		\
+		expect_string(__wrap_write, buf, ln);			\
+		will_return(__wrap_write, strlen(ln));			\
+		expect_condlog(3, NEW_STR(alias, wwid));		\
+	} while (0)
+
+static void gufa_empty_new_rw(void **state) {
+	char *alias;
+
+	will_return(__wrap_open_file, true);
+
+	will_return(__wrap_fgets, NULL);
+	mock_unused_alias("MPATHa");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+
+	mock_allocate_binding("MPATHa", "WWID0");
+	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", false);
+	assert_string_equal(alias, "MPATHa");
+	free(alias);
+}
+
+static void gufa_empty_new_ro_1(void **state) {
+	char *alias;
+	will_return(__wrap_open_file, false);
+	will_return(__wrap_fgets, NULL);
+	mock_unused_alias("MPATHa");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+
+	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", false);
+	assert_ptr_equal(alias, NULL);
+}
+
+static void gufa_empty_new_ro_2(void **state) {
+	char *alias;
+
+	will_return(__wrap_open_file, true);
+
+	will_return(__wrap_fgets, NULL);
+	mock_unused_alias("MPATHa");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+
+	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
+	assert_ptr_equal(alias, NULL);
+}
+
+static void gufa_match_a_unused(void **state) {
+	char *alias;
+
+	will_return(__wrap_open_file, true);
+
+	will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
+	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
+	mock_unused_alias("MPATHa");
+
+	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
+	assert_string_equal(alias, "MPATHa");
+	free(alias);
+}
+
+static void gufa_match_a_self(void **state) {
+	char *alias;
+
+	will_return(__wrap_open_file, true);
+
+	will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
+	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
+	mock_self_alias("MPATHa", "WWID0");
+
+	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
+	assert_string_equal(alias, "MPATHa");
+	free(alias);
+}
+
+static void gufa_match_a_used(void **state) {
+	char *alias;
+
+	will_return(__wrap_open_file, true);
+
+	will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
+	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
+	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
+
+	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
+	assert_ptr_equal(alias, NULL);
+}
+
+static void gufa_nomatch_a_c(void **state) {
+	char *alias;
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file("MPATHa WWID0\n"
+			   "MPATHc WWID2",
+			   -1);
+	mock_unused_alias("MPATHb");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
+
+	mock_allocate_binding("MPATHb", "WWID1");
+
+	alias = get_user_friendly_alias("WWID1", "x", "", "MPATH", false);
+	assert_string_equal(alias, "MPATHb");
+	free(alias);
+}
+
+static void gufa_nomatch_c_a(void **state) {
+	char *alias;
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file("MPATHc WWID2\n"
+			   "MPATHa WWID0",
+			   -1);
+	mock_unused_alias("MPATHb");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
+
+	mock_allocate_binding("MPATHb", "WWID1");
+
+	alias = get_user_friendly_alias("WWID1", "x", "", "MPATH", false);
+	assert_string_equal(alias, "MPATHb");
+	free(alias);
+}
+
+static void gufa_nomatch_c_b(void **state) {
+	char *alias;
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file("MPATHc WWID2\n"
+			   "MPATHb WWID1\n",
+			   -1);
+	mock_unused_alias("MPATHa");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+
+	mock_allocate_binding("MPATHa", "WWID0");
+
+	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", false);
+	assert_string_equal(alias, "MPATHa");
+	free(alias);
+}
+
+static void gufa_nomatch_c_b_used(void **state) {
+	char *alias;
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file("MPATHc WWID2\n"
+			   "MPATHb WWID1",
+			   -1);
+	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID4"));
+	expect_condlog(3, NOMATCH_WWID_STR("WWID4"));
+	mock_unused_alias("MPATHd");
+
+	mock_allocate_binding("MPATHd", "WWID4");
+
+	alias = get_user_friendly_alias("WWID4", "x", "", "MPATH", false);
+	assert_string_equal(alias, "MPATHd");
+	free(alias);
+}
+
+static void gufa_nomatch_b_f_a(void **state) {
+	char *alias;
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file("MPATHb WWID1\n"
+			   "MPATHf WWID6\n"
+			   "MPATHa WWID0\n",
+			   -1);
+	expect_condlog(3, NOMATCH_WWID_STR("WWID7"));
+	mock_unused_alias("MPATHg");
+
+	mock_allocate_binding("MPATHg", "WWID7");
+
+	alias = get_user_friendly_alias("WWID7", "x", "", "MPATH", false);
+	assert_string_equal(alias, "MPATHg");
+	free(alias);
+}
+
+static void gufa_old_empty(void **state) {
+	char *alias;
+	will_return(__wrap_open_file, true);
+
+	/* rlookup_binding for ALIAS */
+	will_return(__wrap_fgets, NULL);
+	expect_condlog(3, NOMATCH_STR("MPATHz"));
+
+	/* lookup_binding */
+	will_return(__wrap_fgets, NULL);
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+
+	mock_unused_alias("MPATHz");
+
+	mock_allocate_binding("MPATHz", "WWID0");
+	expect_condlog(2, ALLOC_STR("MPATHz", "WWID0"));
+
+	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
+	assert_string_equal(alias, "MPATHz");
+	free(alias);
+}
+
+static void gufa_old_empty_self(void **state) {
+	char *alias;
+	will_return(__wrap_open_file, true);
+
+	will_return(__wrap_fgets, NULL);
+	expect_condlog(3, NOMATCH_STR("MPATHz"));
+
+	will_return(__wrap_fgets, NULL);
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+	mock_self_alias("MPATHz", "WWID0");
+
+	mock_allocate_binding("MPATHz", "WWID0");
+	expect_condlog(2, ALLOC_STR("MPATHz", "WWID0"));
+
+	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
+	assert_string_equal(alias, "MPATHz");
+	free(alias);
+}
+
+static void gufa_old_empty_used(void **state) {
+	char *alias;
+	will_return(__wrap_open_file, true);
+
+	will_return(__wrap_fgets, NULL);
+	expect_condlog(3, NOMATCH_STR("MPATHz"));
+
+	will_return(__wrap_fgets, NULL);
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+	mock_used_alias("MPATHz", USED_STR("MPATHz", "WWID0"));
+	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
+
+	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
+	assert_ptr_equal(alias, NULL);
+}
+
+static void gufa_old_match(void **state) {
+	char *alias;
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file("MPATHb WWID1\n"
+			   "MPATHz WWID0",
+			   1);
+	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
+	mock_unused_alias("MPATHz");
+
+	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
+	assert_string_equal(alias, "MPATHz");
+	free(alias);
+}
+
+static void gufa_old_match_self(void **state) {
+	char *alias;
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file("MPATHz WWID0", 0);
+	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
+	mock_self_alias("MPATHz", "WWID0");
+
+	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
+	assert_string_equal(alias, "MPATHz");
+	free(alias);
+}
+
+static void gufa_old_match_used(void **state) {
+	char *alias;
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file("MPATHz WWID0", 0);
+	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
+	mock_used_alias("MPATHz", USED_STR("MPATHz", "WWID0"));
+	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
+
+	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
+	assert_ptr_equal(alias, NULL);
+}
+
+static void gufa_old_match_other(void **state) {
+	char *alias;
+	static const char bindings[] = "MPATHz WWID9";
+
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file(bindings, 0);
+	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
+	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
+
+	mock_bindings_file(bindings, -1);
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+	mock_unused_alias("MPATHa");
+
+	mock_allocate_binding("MPATHa", "WWID0");
+
+	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
+	assert_string_equal(alias, "MPATHa");
+	free(alias);
+}
+
+static void gufa_old_match_other_used(void **state) {
+	char *alias;
+	static const char bindings[] = "MPATHz WWID9";
+
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file(bindings, 0);
+	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
+	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
+
+	mock_bindings_file(bindings, -1);
+	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+	mock_unused_alias("MPATHb");
+
+	mock_allocate_binding("MPATHb", "WWID0");
+
+	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
+	assert_string_equal(alias, "MPATHb");
+	free(alias);
+}
+
+static void gufa_old_match_other_wwidmatch(void **state) {
+	char *alias;
+	static const char bindings[] = ("MPATHz WWID9\n"
+					"MPATHc WWID2");
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file(bindings, 0);
+	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
+	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
+
+	mock_bindings_file(bindings, 1);
+	expect_condlog(3, FOUND_STR("MPATHc", "WWID2"));
+	mock_unused_alias("MPATHc");
+
+	alias = get_user_friendly_alias("WWID2", "x", "MPATHz", "MPATH", false);
+	assert_string_equal(alias, "MPATHc");
+	free(alias);
+}
+
+static void gufa_old_match_other_wwidmatch_used(void **state) {
+	char *alias;
+	static const char bindings[] = ("MPATHz WWID9\n"
+					"MPATHc WWID2");
+
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file(bindings, 0);
+	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
+	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
+
+	mock_bindings_file(bindings, 1);
+	expect_condlog(3, FOUND_STR("MPATHc", "WWID2"));
+	mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID2"));
+
+	alias = get_user_friendly_alias("WWID2", "x", "MPATHz", "MPATH", false);
+	assert_ptr_equal(alias, NULL);
+}
+
+static void gufa_old_nomatch_wwidmatch(void **state) {
+	char *alias;
+	static const char bindings[] = "MPATHa WWID0";
+
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file(bindings, -1);
+	expect_condlog(3, NOMATCH_STR("MPATHz"));
+
+	mock_bindings_file(bindings, 0);
+	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
+	mock_unused_alias("MPATHa");
+	expect_condlog(3, EXISTING_STR("MPATHa", "WWID0"));
+
+	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
+	assert_string_equal(alias, "MPATHa");
+	free(alias);
+}
+
+static void gufa_old_nomatch_wwidmatch_used(void **state) {
+	char *alias;
+	static const char bindings[] = "MPATHa WWID0";
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file(bindings, -1);
+	expect_condlog(3, NOMATCH_STR("MPATHz"));
+
+	mock_bindings_file(bindings, 0);
+	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
+	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
+
+	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
+	assert_ptr_equal(alias, NULL);
+}
+
+static void gufa_old_nomatch_nowwidmatch(void **state) {
+	char *alias;
+	static const char bindings[] = "MPATHb WWID1";
+
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file(bindings, -1);
+	expect_condlog(3, NOMATCH_STR("MPATHz"));
+
+	mock_bindings_file(bindings, -1);
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+
+	mock_unused_alias("MPATHz");
+
+	mock_allocate_binding("MPATHz", "WWID0");
+	expect_condlog(2, ALLOC_STR("MPATHz", "WWID0"));
+
+	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
+	assert_string_equal(alias, "MPATHz");
+	free(alias);
+}
+
+static void gufa_old_nomatch_nowwidmatch_used(void **state) {
+	char *alias;
+	static const char bindings[] = "MPATHb WWID1";
+
+	will_return(__wrap_open_file, true);
+
+	mock_bindings_file(bindings, -1);
+	expect_condlog(3, NOMATCH_STR("MPATHz"));
+
+	mock_bindings_file(bindings, -1);
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+
+	mock_used_alias("MPATHz", USED_STR("MPATHz", "WWID0"));
+	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
+
+	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
+	assert_ptr_equal(alias, NULL);
+}
+
+static int test_get_user_friendly_alias()
+{
+	const struct CMUnitTest tests[] = {
+		cmocka_unit_test(gufa_empty_new_rw),
+		cmocka_unit_test(gufa_empty_new_ro_1),
+		cmocka_unit_test(gufa_empty_new_ro_2),
+		cmocka_unit_test(gufa_match_a_unused),
+		cmocka_unit_test(gufa_match_a_self),
+		cmocka_unit_test(gufa_match_a_used),
+		cmocka_unit_test(gufa_nomatch_a_c),
+		cmocka_unit_test(gufa_nomatch_c_a),
+		cmocka_unit_test(gufa_nomatch_c_b),
+		cmocka_unit_test(gufa_nomatch_c_b_used),
+		cmocka_unit_test(gufa_nomatch_b_f_a),
+		cmocka_unit_test(gufa_old_empty),
+		cmocka_unit_test(gufa_old_empty_self),
+		cmocka_unit_test(gufa_old_empty_used),
+		cmocka_unit_test(gufa_old_match),
+		cmocka_unit_test(gufa_old_match_self),
+		cmocka_unit_test(gufa_old_match_used),
+		cmocka_unit_test(gufa_old_match_other),
+		cmocka_unit_test(gufa_old_match_other_used),
+		cmocka_unit_test(gufa_old_match_other_wwidmatch),
+		cmocka_unit_test(gufa_old_match_other_wwidmatch_used),
+		cmocka_unit_test(gufa_old_nomatch_wwidmatch),
+		cmocka_unit_test(gufa_old_nomatch_wwidmatch_used),
+		cmocka_unit_test(gufa_old_nomatch_nowwidmatch),
+		cmocka_unit_test(gufa_old_nomatch_nowwidmatch_used),
+	};
+
+	return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
 int main(void)
 {
 	int ret = 0;
@@ -1157,6 +1686,8 @@ int main(void)
 	ret += test_lookup_binding();
 	ret += test_rlookup_binding();
 	ret += test_allocate_binding();
+	ret += test_allocate_binding();
+	ret += test_get_user_friendly_alias();
 
 	return ret;
 }
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 08/21] multipath-tools test: consistent use of macros in alias test
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (6 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 07/21] multipath-tools tests: add tests for get_user_friendly_alias() mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:43   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 09/21] multipath-tools tests: convert mock_{failed, used}_alias to macros mwilck
                   ` (12 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Used the macros introduced with the tests for get_user_friendly_alias()
also in the previously existing tests.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 tests/alias.c | 80 ++++++++++++++++++++++++---------------------------
 1 file changed, 38 insertions(+), 42 deletions(-)

diff --git a/tests/alias.c b/tests/alias.c
index e2372d1..1c9903c 100644
--- a/tests/alias.c
+++ b/tests/alias.c
@@ -490,7 +490,7 @@ static void lb_empty(void **state)
 	char *alias;
 
 	will_return(__wrap_fgets, NULL);
-	expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, NULL, 0);
 	assert_int_equal(rc, 1);
 	assert_ptr_equal(alias, NULL);
@@ -503,7 +503,7 @@ static void lb_empty_unused(void **state)
 
 	will_return(__wrap_fgets, NULL);
 	mock_unused_alias("MPATHa");
-	expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
 	assert_int_equal(rc, 1);
 	assert_ptr_equal(alias, NULL);
@@ -518,7 +518,7 @@ static void lb_empty_failed(void **state)
 	will_return(__wrap_fgets, NULL);
 	mock_failed_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
 	mock_unused_alias("MPATHb");
-	expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
@@ -533,7 +533,7 @@ static void lb_empty_1_used(void **state)
 	will_return(__wrap_fgets, NULL);
 	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
 	mock_unused_alias("MPATHb");
-	expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
@@ -548,7 +548,7 @@ static void lb_empty_1_used_self(void **state)
 	will_return(__wrap_fgets, NULL);
 	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
 	mock_self_alias("MPATHb", "WWID0");
-	expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
@@ -561,8 +561,7 @@ static void lb_match_a(void **state)
 	char *alias;
 
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	expect_condlog(3, "Found matching wwid [WWID0] in bindings file."
-		       " Setting alias to MPATHa\n");
+	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 0);
 	assert_int_equal(rc, 0);
 	assert_ptr_not_equal(alias, NULL);
@@ -577,7 +576,7 @@ static void lb_nomatch_a(void **state)
 
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, NULL);
-	expect_condlog(3, "No matching wwid [WWID1] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
 	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 0);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
@@ -590,7 +589,7 @@ static void lb_nomatch_a_bad_check(void **state)
 
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, NULL);
-	expect_condlog(0, "no more available user_friendly_names\n");
+	expect_condlog(0, NOMORE_STR);
 	rc = lookup_binding(NULL, "WWID1", &alias, NULL, 1);
 	assert_int_equal(rc, -1);
 	assert_ptr_equal(alias, NULL);
@@ -604,7 +603,7 @@ static void lb_nomatch_a_unused(void **state)
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, NULL);
 	mock_unused_alias("MPATHb");
-	expect_condlog(3, "No matching wwid [WWID1] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
 	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
@@ -622,7 +621,7 @@ static void lb_nomatch_a_3_used_failed_self(void **state)
 	mock_used_alias("MPATHd", USED_STR("MPATHd", "WWID1"));
 	mock_failed_alias("MPATHe", USED_STR("MPATHe", "WWID1"));
 	mock_self_alias("MPATHf", "WWID1");
-	expect_condlog(3, "No matching wwid [WWID1] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
 	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
 	assert_int_equal(rc, 6);
 	assert_ptr_equal(alias, NULL);
@@ -635,8 +634,7 @@ static void do_lb_match_c(void **state, int check_if_taken)
 
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, "MPATHc WWID1\n");
-	expect_condlog(3, "Found matching wwid [WWID1] in bindings file."
-		       " Setting alias to MPATHc\n");
+	expect_condlog(3, FOUND_STR("MPATHc", "WWID1"));
 	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", check_if_taken);
 	assert_int_equal(rc, 0);
 	assert_ptr_not_equal(alias, NULL);
@@ -662,7 +660,7 @@ static void lb_nomatch_a_c(void **state)
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, "MPATHc WWID1\n");
 	will_return(__wrap_fgets, NULL);
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
@@ -677,7 +675,7 @@ static void lb_nomatch_a_d_unused(void **state)
 	will_return(__wrap_fgets, "MPATHd WWID1\n");
 	will_return(__wrap_fgets, NULL);
 	mock_unused_alias("MPATHb");
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
@@ -693,7 +691,7 @@ static void lb_nomatch_a_d_1_used(void **state)
 	will_return(__wrap_fgets, NULL);
 	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
 	mock_unused_alias("MPATHc");
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 3);
 	assert_ptr_equal(alias, NULL);
@@ -710,7 +708,7 @@ static void lb_nomatch_a_d_2_used(void **state)
 	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
 	mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID2"));
 	mock_unused_alias("MPATHe");
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 5);
 	assert_ptr_equal(alias, NULL);
@@ -728,7 +726,7 @@ static void lb_nomatch_a_d_3_used(void **state)
 	mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID2"));
 	mock_used_alias("MPATHe", USED_STR("MPATHe", "WWID2"));
 	mock_unused_alias("MPATHf");
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 6);
 	assert_ptr_equal(alias, NULL);
@@ -742,7 +740,7 @@ static void lb_nomatch_c_a(void **state)
 	will_return(__wrap_fgets, "MPATHc WWID1\n");
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, NULL);
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
@@ -758,7 +756,7 @@ static void lb_nomatch_d_a_unused(void **state)
 	will_return(__wrap_fgets, "MPATHd WWID0\n");
 	will_return(__wrap_fgets, NULL);
 	mock_unused_alias("MPATHb");
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
@@ -775,7 +773,7 @@ static void lb_nomatch_d_a_1_used(void **state)
 	will_return(__wrap_fgets, NULL);
 	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
 	mock_unused_alias("MPATHe");
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 5);
 	assert_ptr_equal(alias, NULL);
@@ -790,7 +788,7 @@ static void lb_nomatch_a_b(void **state)
 	will_return(__wrap_fgets, "MPATHz WWID26\n");
 	will_return(__wrap_fgets, "MPATHb WWID1\n");
 	will_return(__wrap_fgets, NULL);
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
 	assert_int_equal(rc, 3);
 	assert_ptr_equal(alias, NULL);
@@ -806,7 +804,7 @@ static void lb_nomatch_a_b_bad(void **state)
 	will_return(__wrap_fgets, "MPATHb\n");
 	will_return(__wrap_fgets, NULL);
 	expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
 	assert_int_equal(rc, 3);
 	assert_ptr_equal(alias, NULL);
@@ -823,7 +821,7 @@ static void lb_nomatch_a_b_bad_self(void **state)
 	will_return(__wrap_fgets, NULL);
 	expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
 	mock_self_alias("MPATHc", "WWID2");
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 3);
 	assert_ptr_equal(alias, NULL);
@@ -838,7 +836,7 @@ static void lb_nomatch_b_a(void **state)
 	will_return(__wrap_fgets, "MPATHz WWID26\n");
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, NULL);
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
 	assert_int_equal(rc, 27);
 	assert_ptr_equal(alias, NULL);
@@ -857,7 +855,7 @@ static void lb_nomatch_b_a_3_used(void **state)
 	mock_used_alias("MPATHab", USED_STR("MPATHab", "WWID2"));
 	mock_used_alias("MPATHac", USED_STR("MPATHac", "WWID2"));
 	mock_unused_alias("MPATHad");
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 30);
 	assert_ptr_equal(alias, NULL);
@@ -873,7 +871,7 @@ static void do_lb_nomatch_int_max(void **state, int check_if_taken)
 	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n");
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, NULL);
-	expect_condlog(0, "no more available user_friendly_names\n");
+	expect_condlog(0, NOMORE_STR);
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", check_if_taken);
 	assert_int_equal(rc, -1);
 	assert_ptr_equal(alias, NULL);
@@ -898,7 +896,7 @@ static void lb_nomatch_int_max_used(void **state)
 	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n");
 	will_return(__wrap_fgets, NULL);
 	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
-	expect_condlog(0, "no more available user_friendly_names\n");
+	expect_condlog(0, NOMORE_STR);
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, -1);
 	assert_ptr_equal(alias, NULL);
@@ -913,7 +911,7 @@ static void lb_nomatch_int_max_m1(void **state)
 	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, NULL);
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
 	assert_int_equal(rc, INT_MAX);
 	assert_ptr_equal(alias, NULL);
@@ -929,7 +927,7 @@ static void lb_nomatch_int_max_m1_used(void **state)
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, NULL);
 	mock_used_alias("MPATH" MPATH_ID_INT_MAX, USED_STR("MPATH" MPATH_ID_INT_MAX, "WWID2"));
-	expect_condlog(0, "no more available user_friendly_names\n");
+	expect_condlog(0, NOMORE_STR);
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, -1);
 	assert_ptr_equal(alias, NULL);
@@ -945,7 +943,7 @@ static void lb_nomatch_int_max_m1_1_used(void **state)
 	will_return(__wrap_fgets, NULL);
 	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
 	mock_unused_alias("MPATH" MPATH_ID_INT_MAX);
-	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, INT_MAX);
 	assert_ptr_equal(alias, NULL);
@@ -961,7 +959,7 @@ static void lb_nomatch_int_max_m1_2_used(void **state)
 	will_return(__wrap_fgets, NULL);
 	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
 	mock_used_alias("MPATH" MPATH_ID_INT_MAX, USED_STR("MPATH" MPATH_ID_INT_MAX, "WWID2"));
-	expect_condlog(0, "no more available user_friendly_names\n");
+	expect_condlog(0, NOMORE_STR);
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, -1);
 	assert_ptr_equal(alias, NULL);
@@ -1017,7 +1015,7 @@ static void rl_empty(void **state)
 
 	buf[0] = '\0';
 	will_return(__wrap_fgets, NULL);
-	expect_condlog(3, "No matching alias [MPATHa] in bindings file.\n");
+	expect_condlog(3, NOMATCH_STR("MPATHa"));
 	rc = rlookup_binding(NULL, buf, "MPATHa");
 	assert_int_equal(rc, -1);
 	assert_string_equal(buf, "");
@@ -1030,8 +1028,7 @@ static void rl_match_a(void **state)
 
 	buf[0] = '\0';
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	expect_condlog(3, "Found matching alias [MPATHa] in bindings file. "
-		       "Setting wwid to WWID0\n");
+	expect_condlog(3, FOUND_ALIAS_STR("MPATHa", "WWID0"));
 	rc = rlookup_binding(NULL, buf, "MPATHa");
 	assert_int_equal(rc, 0);
 	assert_string_equal(buf, "WWID0");
@@ -1045,7 +1042,7 @@ static void rl_nomatch_a(void **state)
 	buf[0] = '\0';
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, NULL);
-	expect_condlog(3, "No matching alias [MPATHb] in bindings file.\n");
+	expect_condlog(3, NOMATCH_STR("MPATHb"));
 	rc = rlookup_binding(NULL, buf, "MPATHb");
 	assert_int_equal(rc, -1);
 	assert_string_equal(buf, "");
@@ -1060,7 +1057,7 @@ static void rl_malformed_a(void **state)
 	will_return(__wrap_fgets, "MPATHa     \n");
 	will_return(__wrap_fgets, NULL);
 	expect_condlog(3, "Ignoring malformed line 1 in bindings file\n");
-	expect_condlog(3, "No matching alias [MPATHa] in bindings file.\n");
+	expect_condlog(3, NOMATCH_STR("MPATHa"));
 	rc = rlookup_binding(NULL, buf, "MPATHa");
 	assert_int_equal(rc, -1);
 	assert_string_equal(buf, "");
@@ -1080,7 +1077,7 @@ static void rl_overlong_a(void **state)
 	will_return(__wrap_fgets, line);
 	will_return(__wrap_fgets, NULL);
 	expect_condlog(3, "Ignoring too large wwid at 1 in bindings file\n");
-	expect_condlog(3, "No matching alias [MPATHa] in bindings file.\n");
+	expect_condlog(3, NOMATCH_STR("MPATHa"));
 	rc = rlookup_binding(NULL, buf, "MPATHa");
 	assert_int_equal(rc, -1);
 	assert_string_equal(buf, "");
@@ -1095,8 +1092,7 @@ static void rl_match_b(void **state)
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, "MPATHz WWID26\n");
 	will_return(__wrap_fgets, "MPATHb WWID2\n");
-	expect_condlog(3, "Found matching alias [MPATHb] in bindings file. "
-		       "Setting wwid to WWID2\n");
+	expect_condlog(3, FOUND_ALIAS_STR("MPATHb", "WWID2"));
 	rc = rlookup_binding(NULL, buf, "MPATHb");
 	assert_int_equal(rc, 0);
 	assert_string_equal(buf, "WWID2");
@@ -1125,7 +1121,7 @@ static void al_a(void **state)
 	expect_value(__wrap_write, count, strlen(ln));
 	expect_string(__wrap_write, buf, ln);
 	will_return(__wrap_write, strlen(ln));
-	expect_condlog(3, "Created new binding [MPATHa] for WWID [WWIDa]\n");
+	expect_condlog(3, NEW_STR("MPATHa", "WWIDa"));
 
 	alias = allocate_binding(0, "WWIDa", 1, "MPATH");
 	assert_ptr_not_equal(alias, NULL);
@@ -1142,7 +1138,7 @@ static void al_zz(void **state)
 	expect_value(__wrap_write, count, strlen(ln));
 	expect_string(__wrap_write, buf, ln);
 	will_return(__wrap_write, strlen(ln));
-	expect_condlog(3, "Created new binding [MPATHzz] for WWID [WWIDzz]\n");
+	expect_condlog(3, NEW_STR("MPATHzz", "WWIDzz"));
 
 	alias = allocate_binding(0, "WWIDzz", 26*26 + 26, "MPATH");
 	assert_ptr_not_equal(alias, NULL);
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 09/21] multipath-tools tests: convert mock_{failed, used}_alias to macros
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (7 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 08/21] multipath-tools test: consistent use of macros in alias test mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:44   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 10/21] multipath-tools test: use mock_bindings_file() consistently mwilck
                   ` (11 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

This way we can further improve readability of the individual test
cases.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 tests/alias.c | 98 +++++++++++++++++++++++++--------------------------
 1 file changed, 49 insertions(+), 49 deletions(-)

diff --git a/tests/alias.c b/tests/alias.c
index 1c9903c..cb6695b 100644
--- a/tests/alias.c
+++ b/tests/alias.c
@@ -445,26 +445,26 @@ static void mock_self_alias(const char *alias, const char *wwid)
 #define REUSE_STR(alias_str, wwid_str) ("alias " alias_str " already bound to wwid " wwid_str ", cannot reuse\n")
 #define NOMORE_STR "no more available user_friendly_names\n"
 
-static void mock_failed_alias(const char *alias, char *msg)
-{
-	expect_string(__wrap_dm_map_present, str, alias);
-	will_return(__wrap_dm_map_present, 1);
-	expect_string(__wrap_dm_get_uuid, name, alias);
-	expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);
-	will_return(__wrap_dm_get_uuid, 1);
-	expect_condlog(3, msg);
-}
+#define mock_failed_alias(alias, wwid)					\
+	do {								\
+		expect_string(__wrap_dm_map_present, str, alias);	\
+		will_return(__wrap_dm_map_present, 1);			\
+		expect_string(__wrap_dm_get_uuid, name, alias);		\
+		expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);	\
+		will_return(__wrap_dm_get_uuid, 1);			\
+		expect_condlog(3, USED_STR(alias, wwid));		\
+	} while (0)
 
-static void mock_used_alias(const char *alias, char *msg)
-{
-	expect_string(__wrap_dm_map_present, str, alias);
-	will_return(__wrap_dm_map_present, 1);
-	expect_string(__wrap_dm_get_uuid, name, alias);
-	expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);
-	will_return(__wrap_dm_get_uuid, 0);
-	will_return(__wrap_dm_get_uuid, "WWID_USED");
-	expect_condlog(3, msg);
-}
+#define mock_used_alias(alias, wwid)					\
+	do {								\
+		expect_string(__wrap_dm_map_present, str, alias);	\
+		will_return(__wrap_dm_map_present, 1);			\
+		expect_string(__wrap_dm_get_uuid, name, alias);		\
+		expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);	\
+		will_return(__wrap_dm_get_uuid, 0);			\
+		will_return(__wrap_dm_get_uuid, "WWID_USED");		\
+		expect_condlog(3, USED_STR(alias, wwid));		\
+	} while(0)
 
 static void mock_bindings_file(const char *content, int match_line)
 {
@@ -516,7 +516,7 @@ static void lb_empty_failed(void **state)
 	char *alias;
 
 	will_return(__wrap_fgets, NULL);
-	mock_failed_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
+	mock_failed_alias("MPATHa", "WWID0");
 	mock_unused_alias("MPATHb");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
@@ -531,7 +531,7 @@ static void lb_empty_1_used(void **state)
 	char *alias;
 
 	will_return(__wrap_fgets, NULL);
-	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
+	mock_used_alias("MPATHa", "WWID0");
 	mock_unused_alias("MPATHb");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
@@ -546,7 +546,7 @@ static void lb_empty_1_used_self(void **state)
 	char *alias;
 
 	will_return(__wrap_fgets, NULL);
-	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
+	mock_used_alias("MPATHa", "WWID0");
 	mock_self_alias("MPATHb", "WWID0");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
@@ -616,10 +616,10 @@ static void lb_nomatch_a_3_used_failed_self(void **state)
 
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, NULL);
-	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID1"));
-	mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID1"));
-	mock_used_alias("MPATHd", USED_STR("MPATHd", "WWID1"));
-	mock_failed_alias("MPATHe", USED_STR("MPATHe", "WWID1"));
+	mock_used_alias("MPATHb", "WWID1");
+	mock_used_alias("MPATHc", "WWID1");
+	mock_used_alias("MPATHd", "WWID1");
+	mock_failed_alias("MPATHe", "WWID1");
 	mock_self_alias("MPATHf", "WWID1");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
 	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
@@ -689,7 +689,7 @@ static void lb_nomatch_a_d_1_used(void **state)
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, "MPATHd WWID1\n");
 	will_return(__wrap_fgets, NULL);
-	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
+	mock_used_alias("MPATHb", "WWID2");
 	mock_unused_alias("MPATHc");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
@@ -705,8 +705,8 @@ static void lb_nomatch_a_d_2_used(void **state)
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, "MPATHd WWID1\n");
 	will_return(__wrap_fgets, NULL);
-	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
-	mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID2"));
+	mock_used_alias("MPATHb", "WWID2");
+	mock_used_alias("MPATHc", "WWID2");
 	mock_unused_alias("MPATHe");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
@@ -722,9 +722,9 @@ static void lb_nomatch_a_d_3_used(void **state)
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, "MPATHd WWID1\n");
 	will_return(__wrap_fgets, NULL);
-	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
-	mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID2"));
-	mock_used_alias("MPATHe", USED_STR("MPATHe", "WWID2"));
+	mock_used_alias("MPATHb", "WWID2");
+	mock_used_alias("MPATHc", "WWID2");
+	mock_used_alias("MPATHe", "WWID2");
 	mock_unused_alias("MPATHf");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
@@ -771,7 +771,7 @@ static void lb_nomatch_d_a_1_used(void **state)
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, "MPATHd WWID0\n");
 	will_return(__wrap_fgets, NULL);
-	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
+	mock_used_alias("MPATHb", "WWID2");
 	mock_unused_alias("MPATHe");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
@@ -851,9 +851,9 @@ static void lb_nomatch_b_a_3_used(void **state)
 	will_return(__wrap_fgets, "MPATHz WWID26\n");
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, NULL);
-	mock_used_alias("MPATHaa", USED_STR("MPATHaa", "WWID2"));
-	mock_used_alias("MPATHab", USED_STR("MPATHab", "WWID2"));
-	mock_used_alias("MPATHac", USED_STR("MPATHac", "WWID2"));
+	mock_used_alias("MPATHaa", "WWID2");
+	mock_used_alias("MPATHab", "WWID2");
+	mock_used_alias("MPATHac", "WWID2");
 	mock_unused_alias("MPATHad");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
@@ -895,7 +895,7 @@ static void lb_nomatch_int_max_used(void **state)
 	will_return(__wrap_fgets, "MPATHb WWID1\n");
 	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n");
 	will_return(__wrap_fgets, NULL);
-	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
+	mock_used_alias("MPATHa", "WWID2");
 	expect_condlog(0, NOMORE_STR);
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, -1);
@@ -926,7 +926,7 @@ static void lb_nomatch_int_max_m1_used(void **state)
 	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
 	will_return(__wrap_fgets, "MPATHa WWID0\n");
 	will_return(__wrap_fgets, NULL);
-	mock_used_alias("MPATH" MPATH_ID_INT_MAX, USED_STR("MPATH" MPATH_ID_INT_MAX, "WWID2"));
+	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWID2");
 	expect_condlog(0, NOMORE_STR);
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, -1);
@@ -941,7 +941,7 @@ static void lb_nomatch_int_max_m1_1_used(void **state)
 	will_return(__wrap_fgets, "MPATHb WWID1\n");
 	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
 	will_return(__wrap_fgets, NULL);
-	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
+	mock_used_alias("MPATHa", "WWID2");
 	mock_unused_alias("MPATH" MPATH_ID_INT_MAX);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
@@ -957,8 +957,8 @@ static void lb_nomatch_int_max_m1_2_used(void **state)
 	will_return(__wrap_fgets, "MPATHb WWID1\n");
 	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
 	will_return(__wrap_fgets, NULL);
-	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
-	mock_used_alias("MPATH" MPATH_ID_INT_MAX, USED_STR("MPATH" MPATH_ID_INT_MAX, "WWID2"));
+	mock_used_alias("MPATHa", "WWID2");
+	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWID2");
 	expect_condlog(0, NOMORE_STR);
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, -1);
@@ -1291,7 +1291,7 @@ static void gufa_match_a_used(void **state) {
 
 	will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
 	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
-	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
+	mock_used_alias("MPATHa", "WWID0");
 
 	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
 	assert_ptr_equal(alias, NULL);
@@ -1355,7 +1355,7 @@ static void gufa_nomatch_c_b_used(void **state) {
 	mock_bindings_file("MPATHc WWID2\n"
 			   "MPATHb WWID1",
 			   -1);
-	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID4"));
+	mock_used_alias("MPATHa", "WWID4");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID4"));
 	mock_unused_alias("MPATHd");
 
@@ -1434,7 +1434,7 @@ static void gufa_old_empty_used(void **state) {
 
 	will_return(__wrap_fgets, NULL);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
-	mock_used_alias("MPATHz", USED_STR("MPATHz", "WWID0"));
+	mock_used_alias("MPATHz", "WWID0");
 	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
 
 	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
@@ -1475,7 +1475,7 @@ static void gufa_old_match_used(void **state) {
 
 	mock_bindings_file("MPATHz WWID0", 0);
 	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
-	mock_used_alias("MPATHz", USED_STR("MPATHz", "WWID0"));
+	mock_used_alias("MPATHz", "WWID0");
 	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
 
 	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
@@ -1514,7 +1514,7 @@ static void gufa_old_match_other_used(void **state) {
 	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
 
 	mock_bindings_file(bindings, -1);
-	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
+	mock_used_alias("MPATHa", "WWID0");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	mock_unused_alias("MPATHb");
 
@@ -1557,7 +1557,7 @@ static void gufa_old_match_other_wwidmatch_used(void **state) {
 
 	mock_bindings_file(bindings, 1);
 	expect_condlog(3, FOUND_STR("MPATHc", "WWID2"));
-	mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID2"));
+	mock_used_alias("MPATHc", "WWID2");
 
 	alias = get_user_friendly_alias("WWID2", "x", "MPATHz", "MPATH", false);
 	assert_ptr_equal(alias, NULL);
@@ -1592,7 +1592,7 @@ static void gufa_old_nomatch_wwidmatch_used(void **state) {
 
 	mock_bindings_file(bindings, 0);
 	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
-	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
+	mock_used_alias("MPATHa", "WWID0");
 
 	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
 	assert_ptr_equal(alias, NULL);
@@ -1632,7 +1632,7 @@ static void gufa_old_nomatch_nowwidmatch_used(void **state) {
 	mock_bindings_file(bindings, -1);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 
-	mock_used_alias("MPATHz", USED_STR("MPATHz", "WWID0"));
+	mock_used_alias("MPATHz", "WWID0");
 	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
 
 	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 10/21] multipath-tools test: use mock_bindings_file() consistently
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (8 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 09/21] multipath-tools tests: convert mock_{failed, used}_alias to macros mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:43   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 11/21] libmultipath: add global variable for current bindings mwilck
                   ` (10 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Further improve test readablity.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 tests/alias.c | 186 ++++++++++++++++++++++----------------------------
 1 file changed, 80 insertions(+), 106 deletions(-)

diff --git a/tests/alias.c b/tests/alias.c
index cb6695b..a1415c6 100644
--- a/tests/alias.c
+++ b/tests/alias.c
@@ -489,7 +489,7 @@ static void lb_empty(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, NULL, 0);
 	assert_int_equal(rc, 1);
@@ -501,7 +501,7 @@ static void lb_empty_unused(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	mock_unused_alias("MPATHa");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
@@ -515,7 +515,7 @@ static void lb_empty_failed(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	mock_failed_alias("MPATHa", "WWID0");
 	mock_unused_alias("MPATHb");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
@@ -530,7 +530,7 @@ static void lb_empty_1_used(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	mock_used_alias("MPATHa", "WWID0");
 	mock_unused_alias("MPATHb");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
@@ -545,7 +545,7 @@ static void lb_empty_1_used_self(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	mock_used_alias("MPATHa", "WWID0");
 	mock_self_alias("MPATHb", "WWID0");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
@@ -560,7 +560,7 @@ static void lb_match_a(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
+	mock_bindings_file("MPATHa WWID0\n", 0);
 	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 0);
 	assert_int_equal(rc, 0);
@@ -574,8 +574,7 @@ static void lb_nomatch_a(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHa WWID0\n", -1);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
 	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 0);
 	assert_int_equal(rc, 2);
@@ -587,8 +586,7 @@ static void lb_nomatch_a_bad_check(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHa WWID0\n", -1);
 	expect_condlog(0, NOMORE_STR);
 	rc = lookup_binding(NULL, "WWID1", &alias, NULL, 1);
 	assert_int_equal(rc, -1);
@@ -600,8 +598,7 @@ static void lb_nomatch_a_unused(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHa WWID0\n", -1);
 	mock_unused_alias("MPATHb");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
 	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
@@ -614,8 +611,7 @@ static void lb_nomatch_a_3_used_failed_self(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHa WWID0\n", -1);
 	mock_used_alias("MPATHb", "WWID1");
 	mock_used_alias("MPATHc", "WWID1");
 	mock_used_alias("MPATHd", "WWID1");
@@ -632,8 +628,8 @@ static void do_lb_match_c(void **state, int check_if_taken)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, "MPATHc WWID1\n");
+	mock_bindings_file("MPATHa WWID0\n"
+			   "MPATHc WWID1", 1);
 	expect_condlog(3, FOUND_STR("MPATHc", "WWID1"));
 	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", check_if_taken);
 	assert_int_equal(rc, 0);
@@ -657,9 +653,8 @@ static void lb_nomatch_a_c(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, "MPATHc WWID1\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHa WWID0\n"
+			   "MPATHc WWID1", -1);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
 	assert_int_equal(rc, 2);
@@ -671,9 +666,8 @@ static void lb_nomatch_a_d_unused(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, "MPATHd WWID1\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHa WWID0\n"
+			   "MPATHd WWID1", -1);
 	mock_unused_alias("MPATHb");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
@@ -686,9 +680,8 @@ static void lb_nomatch_a_d_1_used(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, "MPATHd WWID1\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHa WWID0\n"
+			   "MPATHd WWID1", -1);
 	mock_used_alias("MPATHb", "WWID2");
 	mock_unused_alias("MPATHc");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
@@ -702,9 +695,8 @@ static void lb_nomatch_a_d_2_used(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, "MPATHd WWID1\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHa WWID0\n"
+			   "MPATHd WWID1", -1);
 	mock_used_alias("MPATHb", "WWID2");
 	mock_used_alias("MPATHc", "WWID2");
 	mock_unused_alias("MPATHe");
@@ -719,9 +711,8 @@ static void lb_nomatch_a_d_3_used(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, "MPATHd WWID1\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHa WWID0\n"
+			   "MPATHd WWID1", -1);
 	mock_used_alias("MPATHb", "WWID2");
 	mock_used_alias("MPATHc", "WWID2");
 	mock_used_alias("MPATHe", "WWID2");
@@ -737,9 +728,8 @@ static void lb_nomatch_c_a(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHc WWID1\n");
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHc WWID1\n"
+			   "MPATHa WWID0\n", -1);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
 	assert_int_equal(rc, 2);
@@ -751,10 +741,9 @@ static void lb_nomatch_d_a_unused(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHc WWID1\n");
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, "MPATHd WWID0\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHc WWID1\n"
+			   "MPATHa WWID0\n"
+			   "MPATHd WWID0\n", -1);
 	mock_unused_alias("MPATHb");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
@@ -767,10 +756,9 @@ static void lb_nomatch_d_a_1_used(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHc WWID1\n");
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, "MPATHd WWID0\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHc WWID1\n"
+			   "MPATHa WWID0\n"
+			   "MPATHd WWID0\n", -1);
 	mock_used_alias("MPATHb", "WWID2");
 	mock_unused_alias("MPATHe");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
@@ -784,10 +772,9 @@ static void lb_nomatch_a_b(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, "MPATHz WWID26\n");
-	will_return(__wrap_fgets, "MPATHb WWID1\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHa WWID0\n"
+			   "MPATHz WWID26\n"
+			   "MPATHb WWID1\n", -1);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
 	assert_int_equal(rc, 3);
@@ -799,10 +786,9 @@ static void lb_nomatch_a_b_bad(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, "MPATHz WWID26\n");
-	will_return(__wrap_fgets, "MPATHb\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHa WWID0\n"
+			   "MPATHz WWID26\n"
+			   "MPATHb\n", -1);
 	expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
@@ -815,10 +801,9 @@ static void lb_nomatch_a_b_bad_self(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, "MPATHz WWID26\n");
-	will_return(__wrap_fgets, "MPATHb\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHa WWID0\n"
+			   "MPATHz WWID26\n"
+			   "MPATHb\n", -1);
 	expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
 	mock_self_alias("MPATHc", "WWID2");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
@@ -832,10 +817,9 @@ static void lb_nomatch_b_a(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHb WWID1\n");
-	will_return(__wrap_fgets, "MPATHz WWID26\n");
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHb WWID1\n"
+			   "MPATHz WWID26\n"
+			   "MPATHa WWID0\n", -1);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
 	assert_int_equal(rc, 27);
@@ -847,10 +831,9 @@ static void lb_nomatch_b_a_3_used(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHb WWID1\n");
-	will_return(__wrap_fgets, "MPATHz WWID26\n");
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHb WWID1\n"
+			   "MPATHz WWID26\n"
+			   "MPATHa WWID0\n", -1);
 	mock_used_alias("MPATHaa", "WWID2");
 	mock_used_alias("MPATHab", "WWID2");
 	mock_used_alias("MPATHac", "WWID2");
@@ -867,10 +850,9 @@ static void do_lb_nomatch_int_max(void **state, int check_if_taken)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHb WWID1\n");
-	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n");
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHb WWID1\n"
+			   "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n"
+			   "MPATHa WWID0\n", -1);
 	expect_condlog(0, NOMORE_STR);
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", check_if_taken);
 	assert_int_equal(rc, -1);
@@ -892,9 +874,8 @@ static void lb_nomatch_int_max_used(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHb WWID1\n");
-	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHb WWID1\n"
+			   "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n", -1);
 	mock_used_alias("MPATHa", "WWID2");
 	expect_condlog(0, NOMORE_STR);
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
@@ -907,10 +888,9 @@ static void lb_nomatch_int_max_m1(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHb WWID1\n");
-	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHb WWID1\n"
+			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n"
+			   "MPATHa WWID0\n", -1);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
 	assert_int_equal(rc, INT_MAX);
@@ -922,10 +902,9 @@ static void lb_nomatch_int_max_m1_used(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHb WWID1\n");
-	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHb WWID1\n"
+			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n"
+			   "MPATHa WWID0\n", -1);
 	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWID2");
 	expect_condlog(0, NOMORE_STR);
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
@@ -938,9 +917,8 @@ static void lb_nomatch_int_max_m1_1_used(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHb WWID1\n");
-	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHb WWID1\n"
+			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n", -1);
 	mock_used_alias("MPATHa", "WWID2");
 	mock_unused_alias("MPATH" MPATH_ID_INT_MAX);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
@@ -954,9 +932,8 @@ static void lb_nomatch_int_max_m1_2_used(void **state)
 	int rc;
 	char *alias;
 
-	will_return(__wrap_fgets, "MPATHb WWID1\n");
-	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHb WWID1\n"
+			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n", -1);
 	mock_used_alias("MPATHa", "WWID2");
 	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWID2");
 	expect_condlog(0, NOMORE_STR);
@@ -1014,7 +991,7 @@ static void rl_empty(void **state)
 	char buf[WWID_SIZE];
 
 	buf[0] = '\0';
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	expect_condlog(3, NOMATCH_STR("MPATHa"));
 	rc = rlookup_binding(NULL, buf, "MPATHa");
 	assert_int_equal(rc, -1);
@@ -1027,7 +1004,7 @@ static void rl_match_a(void **state)
 	char buf[WWID_SIZE];
 
 	buf[0] = '\0';
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
+	mock_bindings_file("MPATHa WWID0\n", 0);
 	expect_condlog(3, FOUND_ALIAS_STR("MPATHa", "WWID0"));
 	rc = rlookup_binding(NULL, buf, "MPATHa");
 	assert_int_equal(rc, 0);
@@ -1040,8 +1017,7 @@ static void rl_nomatch_a(void **state)
 	char buf[WWID_SIZE];
 
 	buf[0] = '\0';
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHa WWID0\n", -1);
 	expect_condlog(3, NOMATCH_STR("MPATHb"));
 	rc = rlookup_binding(NULL, buf, "MPATHb");
 	assert_int_equal(rc, -1);
@@ -1054,8 +1030,7 @@ static void rl_malformed_a(void **state)
 	char buf[WWID_SIZE];
 
 	buf[0] = '\0';
-	will_return(__wrap_fgets, "MPATHa     \n");
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("MPATHa     \n", -1);
 	expect_condlog(3, "Ignoring malformed line 1 in bindings file\n");
 	expect_condlog(3, NOMATCH_STR("MPATHa"));
 	rc = rlookup_binding(NULL, buf, "MPATHa");
@@ -1074,8 +1049,7 @@ static void rl_overlong_a(void **state)
 	snprintf(line + sizeof(line) - 2, 2, "\n");
 
 	buf[0] = '\0';
-	will_return(__wrap_fgets, line);
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file(line, -1);
 	expect_condlog(3, "Ignoring too large wwid at 1 in bindings file\n");
 	expect_condlog(3, NOMATCH_STR("MPATHa"));
 	rc = rlookup_binding(NULL, buf, "MPATHa");
@@ -1089,9 +1063,9 @@ static void rl_match_b(void **state)
 	char buf[WWID_SIZE];
 
 	buf[0] = '\0';
-	will_return(__wrap_fgets, "MPATHa WWID0\n");
-	will_return(__wrap_fgets, "MPATHz WWID26\n");
-	will_return(__wrap_fgets, "MPATHb WWID2\n");
+	mock_bindings_file("MPATHa WWID0\n"
+			   "MPATHz WWID26\n"
+			   "MPATHb WWID2\n", 2);
 	expect_condlog(3, FOUND_ALIAS_STR("MPATHb", "WWID2"));
 	rc = rlookup_binding(NULL, buf, "MPATHb");
 	assert_int_equal(rc, 0);
@@ -1222,7 +1196,7 @@ static void gufa_empty_new_rw(void **state) {
 
 	will_return(__wrap_open_file, true);
 
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	mock_unused_alias("MPATHa");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 
@@ -1235,7 +1209,7 @@ static void gufa_empty_new_rw(void **state) {
 static void gufa_empty_new_ro_1(void **state) {
 	char *alias;
 	will_return(__wrap_open_file, false);
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	mock_unused_alias("MPATHa");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 
@@ -1248,7 +1222,7 @@ static void gufa_empty_new_ro_2(void **state) {
 
 	will_return(__wrap_open_file, true);
 
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	mock_unused_alias("MPATHa");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 
@@ -1261,7 +1235,7 @@ static void gufa_match_a_unused(void **state) {
 
 	will_return(__wrap_open_file, true);
 
-	will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
+	mock_bindings_file("MPATHa WWID0", 0);
 	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
 	mock_unused_alias("MPATHa");
 
@@ -1275,7 +1249,7 @@ static void gufa_match_a_self(void **state) {
 
 	will_return(__wrap_open_file, true);
 
-	will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
+	mock_bindings_file("MPATHa WWID0", 0);
 	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
 	mock_self_alias("MPATHa", "WWID0");
 
@@ -1289,7 +1263,7 @@ static void gufa_match_a_used(void **state) {
 
 	will_return(__wrap_open_file, true);
 
-	will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
+	mock_bindings_file("MPATHa WWID0", 0);
 	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
 	mock_used_alias("MPATHa", "WWID0");
 
@@ -1389,11 +1363,11 @@ static void gufa_old_empty(void **state) {
 	will_return(__wrap_open_file, true);
 
 	/* rlookup_binding for ALIAS */
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	expect_condlog(3, NOMATCH_STR("MPATHz"));
 
 	/* lookup_binding */
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 
 	mock_unused_alias("MPATHz");
@@ -1410,10 +1384,10 @@ static void gufa_old_empty_self(void **state) {
 	char *alias;
 	will_return(__wrap_open_file, true);
 
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	expect_condlog(3, NOMATCH_STR("MPATHz"));
 
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	mock_self_alias("MPATHz", "WWID0");
 
@@ -1429,10 +1403,10 @@ static void gufa_old_empty_used(void **state) {
 	char *alias;
 	will_return(__wrap_open_file, true);
 
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	expect_condlog(3, NOMATCH_STR("MPATHz"));
 
-	will_return(__wrap_fgets, NULL);
+	mock_bindings_file("", -1);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	mock_used_alias("MPATHz", "WWID0");
 	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 11/21] libmultipath: add global variable for current bindings
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (9 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 10/21] multipath-tools test: use mock_bindings_file() consistently mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:44   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 12/21] libmultipath: rename fix_bindings_file() to update_bindings_file() mwilck
                   ` (9 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Add a variable global_bindings that holds the currently active vector of
bindings. This variable is freed at program exit.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/alias.c              | 11 +++++++++--
 libmultipath/alias.h              |  1 +
 libmultipath/libmultipath.version |  1 +
 multipath/main.c                  |  2 ++
 multipathd/main.c                 |  1 +
 5 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/libmultipath/alias.c b/libmultipath/alias.c
index e61eb91..0759643 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -522,6 +522,7 @@ static void _free_binding(struct binding *bdg)
  * an abstract type.
  */
 typedef struct _vector Bindings;
+static Bindings global_bindings = { .allocated = 0 };
 
 static void free_bindings(Bindings *bindings)
 {
@@ -533,6 +534,11 @@ static void free_bindings(Bindings *bindings)
 	vector_reset(bindings);
 }
 
+void cleanup_bindings(void)
+{
+	free_bindings(&global_bindings);
+}
+
 enum {
 	BINDING_EXISTS,
 	BINDING_CONFLICT,
@@ -762,7 +768,6 @@ int check_alias_settings(const struct config *conf)
 	pthread_cleanup_pop(1);
 	pthread_cleanup_pop(1);
 
-	pthread_cleanup_push_cast(free_bindings, &bindings);
 	fd = open_file(conf->bindings_file, &can_write, BINDINGS_FILE_HEADER);
 	if (fd != -1) {
 		FILE *file = fdopen(fd, "r");
@@ -782,6 +787,8 @@ int check_alias_settings(const struct config *conf)
 			close(fd);
 		}
 	}
-	pthread_cleanup_pop(1);
+
+	cleanup_bindings();
+	global_bindings = bindings;
 	return rc;
 }
diff --git a/libmultipath/alias.h b/libmultipath/alias.h
index fa33223..37b49d9 100644
--- a/libmultipath/alias.h
+++ b/libmultipath/alias.h
@@ -9,5 +9,6 @@ char *get_user_friendly_alias(const char *wwid, const char *file,
 
 struct config;
 int check_alias_settings(const struct config *);
+void cleanup_bindings(void);
 
 #endif /* _ALIAS_H */
diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index a7b8c33..ddd302f 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -64,6 +64,7 @@ global:
 	checker_name;
 	checker_state_name;
 	check_foreign;
+	cleanup_bindings;
 	cleanup_lock;
 	coalesce_paths;
 	count_active_paths;
diff --git a/multipath/main.c b/multipath/main.c
index b78f316..45e9745 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -843,6 +843,8 @@ main (int argc, char *argv[])
 	conf->force_sync = 1;
 	if (atexit(cleanup_vecs))
 		condlog(1, "failed to register cleanup handler for vecs: %m");
+	if (atexit(cleanup_bindings))
+		condlog(1, "failed to register cleanup handler for bindings: %m");
 	while ((arg = getopt(argc, argv, ":adDcChl::eFfM:v:p:b:BrR:itTquUwW")) != EOF ) {
 		switch(arg) {
 		case 'v':
diff --git a/multipathd/main.c b/multipathd/main.c
index 2e02a54..214ed4a 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3325,6 +3325,7 @@ static void cleanup_child(void)
 {
 	cleanup_threads();
 	cleanup_vecs();
+	cleanup_bindings();
 	if (poll_dmevents)
 		cleanup_dmevent_waiter();
 
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 12/21] libmultipath: rename fix_bindings_file() to update_bindings_file()
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (10 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 11/21] libmultipath: add global variable for current bindings mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:44   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 13/21] libmultipath: alias.c: move bindings related code up mwilck
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

We will use this function in a more generic way, give it a more
generic name.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/alias.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libmultipath/alias.c b/libmultipath/alias.c
index 0759643..af2f647 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -606,8 +606,8 @@ static int write_bindings_file(const Bindings *bindings, int fd)
 	return 0;
 }
 
-static int fix_bindings_file(const struct config *conf,
-			     const Bindings *bindings)
+static int update_bindings_file(const struct config *conf,
+				const Bindings *bindings)
 {
 	int rc;
 	int fd = -1;
@@ -777,7 +777,7 @@ int check_alias_settings(const struct config *conf)
 			rc = _check_bindings_file(conf, file, &bindings);
 			pthread_cleanup_pop(1);
 			if (rc == -1 && can_write && !conf->bindings_read_only)
-				rc = fix_bindings_file(conf, &bindings);
+				rc = update_bindings_file(conf, &bindings);
 			else if (rc == -1)
 				condlog(0, "ERROR: bad settings in read-only bindings file %s",
 					conf->bindings_file);
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 13/21] libmultipath: alias.c: move bindings related code up
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (11 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 12/21] libmultipath: rename fix_bindings_file() to update_bindings_file() mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:44   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 14/21] libmultipath: update_bindings_file: take filename argument mwilck
                   ` (7 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

No code changes, just moving code.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/alias.c | 239 ++++++++++++++++++++++---------------------
 1 file changed, 120 insertions(+), 119 deletions(-)

diff --git a/libmultipath/alias.c b/libmultipath/alias.c
index af2f647..5a6cdee 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -9,6 +9,7 @@
 #include <limits.h>
 #include <stdio.h>
 #include <stdbool.h>
+#include <assert.h>
 
 #include "debug.h"
 #include "util.h"
@@ -51,6 +52,125 @@
 
 static const char bindings_file_header[] = BINDINGS_FILE_HEADER;
 
+struct binding {
+	char *alias;
+	char *wwid;
+};
+
+/*
+ * Perhaps one day we'll implement this more efficiently, thus use
+ * an abstract type.
+ */
+typedef struct _vector Bindings;
+static Bindings global_bindings = { .allocated = 0 };
+
+enum {
+	BINDING_EXISTS,
+	BINDING_CONFLICT,
+	BINDING_ADDED,
+	BINDING_DELETED,
+	BINDING_NOTFOUND,
+	BINDING_ERROR,
+};
+
+static void _free_binding(struct binding *bdg)
+{
+	free(bdg->wwid);
+	free(bdg->alias);
+	free(bdg);
+}
+
+static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
+{
+	struct binding *bdg;
+	int i, cmp = 0;
+
+	/*
+	 * Keep the bindings array sorted by alias.
+	 * Optimization: Search backwards, assuming that the bindings file is
+	 * sorted already.
+	 */
+	vector_foreach_slot_backwards(bindings, bdg, i) {
+		if ((cmp = strcmp(bdg->alias, alias)) <= 0)
+			break;
+	}
+
+	/* Check for exact match */
+	if (i >= 0 && cmp == 0)
+		return strcmp(bdg->wwid, wwid) ?
+			BINDING_CONFLICT : BINDING_EXISTS;
+
+	i++;
+	bdg = calloc(1, sizeof(*bdg));
+	if (bdg) {
+		bdg->wwid = strdup(wwid);
+		bdg->alias = strdup(alias);
+		if (bdg->wwid && bdg->alias &&
+		    vector_insert_slot(bindings, i, bdg))
+			return BINDING_ADDED;
+		else
+			_free_binding(bdg);
+	}
+
+	return BINDING_ERROR;
+}
+
+static int write_bindings_file(const Bindings *bindings, int fd)
+{
+	struct binding *bnd;
+	STRBUF_ON_STACK(line);
+	int i;
+
+	if (write(fd, BINDINGS_FILE_HEADER, sizeof(BINDINGS_FILE_HEADER) - 1)
+	    != sizeof(BINDINGS_FILE_HEADER) - 1)
+		return -1;
+
+	vector_foreach_slot(bindings, bnd, i) {
+		int len;
+
+		if ((len = print_strbuf(&line, "%s %s\n",
+					bnd->alias, bnd->wwid)) < 0)
+			return -1;
+		if (write(fd, get_strbuf_str(&line), len) != len)
+			return -1;
+		truncate_strbuf(&line, 0);
+	}
+	return 0;
+}
+
+static int update_bindings_file(const struct config *conf,
+				const Bindings *bindings)
+{
+	int rc;
+	int fd = -1;
+	char tempname[PATH_MAX];
+	mode_t old_umask;
+
+	if (safe_sprintf(tempname, "%s.XXXXXX", conf->bindings_file))
+		return -1;
+	/* coverity: SECURE_TEMP */
+	old_umask = umask(0077);
+	if ((fd = mkstemp(tempname)) == -1) {
+		condlog(1, "%s: mkstemp: %m", __func__);
+		return -1;
+	}
+	umask(old_umask);
+	pthread_cleanup_push(cleanup_fd_ptr, &fd);
+	rc = write_bindings_file(bindings, fd);
+	pthread_cleanup_pop(1);
+	if (rc == -1) {
+		condlog(1, "failed to write new bindings file %s",
+			tempname);
+		unlink(tempname);
+		return rc;
+	}
+	if ((rc = rename(tempname, conf->bindings_file)) == -1)
+		condlog(0, "%s: rename: %m", __func__);
+	else
+		condlog(1, "updated bindings file %s", conf->bindings_file);
+	return rc;
+}
+
 int
 valid_alias(const char *alias)
 {
@@ -505,25 +625,6 @@ get_user_friendly_wwid(const char *alias, char *buff, const char *file)
 	return 0;
 }
 
-struct binding {
-	char *alias;
-	char *wwid;
-};
-
-static void _free_binding(struct binding *bdg)
-{
-	free(bdg->wwid);
-	free(bdg->alias);
-	free(bdg);
-}
-
-/*
- * Perhaps one day we'll implement this more efficiently, thus use
- * an abstract type.
- */
-typedef struct _vector Bindings;
-static Bindings global_bindings = { .allocated = 0 };
-
 static void free_bindings(Bindings *bindings)
 {
 	struct binding *bdg;
@@ -539,106 +640,6 @@ void cleanup_bindings(void)
 	free_bindings(&global_bindings);
 }
 
-enum {
-	BINDING_EXISTS,
-	BINDING_CONFLICT,
-	BINDING_ADDED,
-	BINDING_DELETED,
-	BINDING_NOTFOUND,
-	BINDING_ERROR,
-};
-
-static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
-{
-	struct binding *bdg;
-	int i, cmp = 0;
-
-	/*
-	 * Keep the bindings array sorted by alias.
-	 * Optimization: Search backwards, assuming that the bindings file is
-	 * sorted already.
-	 */
-	vector_foreach_slot_backwards(bindings, bdg, i) {
-		if ((cmp = strcmp(bdg->alias, alias)) <= 0)
-			break;
-	}
-
-	/* Check for exact match */
-	if (i >= 0 && cmp == 0)
-		return strcmp(bdg->wwid, wwid) ?
-			BINDING_CONFLICT : BINDING_EXISTS;
-
-	i++;
-	bdg = calloc(1, sizeof(*bdg));
-	if (bdg) {
-		bdg->wwid = strdup(wwid);
-		bdg->alias = strdup(alias);
-		if (bdg->wwid && bdg->alias &&
-		    vector_insert_slot(bindings, i, bdg))
-			return BINDING_ADDED;
-		else
-			_free_binding(bdg);
-	}
-
-	return BINDING_ERROR;
-}
-
-static int write_bindings_file(const Bindings *bindings, int fd)
-{
-	struct binding *bnd;
-	STRBUF_ON_STACK(line);
-	int i;
-
-	if (write(fd, BINDINGS_FILE_HEADER, sizeof(BINDINGS_FILE_HEADER) - 1)
-	    != sizeof(BINDINGS_FILE_HEADER) - 1)
-		return -1;
-
-	vector_foreach_slot(bindings, bnd, i) {
-		int len;
-
-		if ((len = print_strbuf(&line, "%s %s\n",
-					bnd->alias, bnd->wwid)) < 0)
-			return -1;
-		if (write(fd, get_strbuf_str(&line), len) != len)
-			return -1;
-		truncate_strbuf(&line, 0);
-	}
-	return 0;
-}
-
-static int update_bindings_file(const struct config *conf,
-				const Bindings *bindings)
-{
-	int rc;
-	int fd = -1;
-	char tempname[PATH_MAX];
-	mode_t old_umask;
-
-	if (safe_sprintf(tempname, "%s.XXXXXX", conf->bindings_file))
-		return -1;
-	/* coverity: SECURE_TEMP */
-	old_umask = umask(0077);
-	if ((fd = mkstemp(tempname)) == -1) {
-		condlog(1, "%s: mkstemp: %m", __func__);
-		return -1;
-	}
-	umask(old_umask);
-	pthread_cleanup_push(cleanup_fd_ptr, &fd);
-	rc = write_bindings_file(bindings, fd);
-	pthread_cleanup_pop(1);
-	if (rc == -1) {
-		condlog(1, "failed to write new bindings file %s",
-			tempname);
-		unlink(tempname);
-		return rc;
-	}
-	if ((rc = rename(tempname, conf->bindings_file)) == -1)
-		condlog(0, "%s: rename: %m", __func__);
-	else
-		condlog(1, "updated bindings file %s", conf->bindings_file);
-	return rc;
-}
-
 static int _check_bindings_file(const struct config *conf, FILE *file,
 				 Bindings *bindings)
 {
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 14/21] libmultipath: update_bindings_file: take filename argument
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (12 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 13/21] libmultipath: alias.c: move bindings related code up mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:45   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 15/21] libmultipath: update_bindings_file: use a single write() mwilck
                   ` (6 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

This function just uses the file name, no other configuration
parameters. Also, pass the Bindings argument first to use the
same convention as the other functions in this file.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/alias.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libmultipath/alias.c b/libmultipath/alias.c
index 5a6cdee..76d852f 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -138,15 +138,15 @@ static int write_bindings_file(const Bindings *bindings, int fd)
 	return 0;
 }
 
-static int update_bindings_file(const struct config *conf,
-				const Bindings *bindings)
+static int update_bindings_file(const Bindings *bindings,
+				const char *bindings_file)
 {
 	int rc;
 	int fd = -1;
 	char tempname[PATH_MAX];
 	mode_t old_umask;
 
-	if (safe_sprintf(tempname, "%s.XXXXXX", conf->bindings_file))
+	if (safe_sprintf(tempname, "%s.XXXXXX", bindings_file))
 		return -1;
 	/* coverity: SECURE_TEMP */
 	old_umask = umask(0077);
@@ -164,10 +164,10 @@ static int update_bindings_file(const struct config *conf,
 		unlink(tempname);
 		return rc;
 	}
-	if ((rc = rename(tempname, conf->bindings_file)) == -1)
+	if ((rc = rename(tempname, bindings_file)) == -1)
 		condlog(0, "%s: rename: %m", __func__);
 	else
-		condlog(1, "updated bindings file %s", conf->bindings_file);
+		condlog(1, "updated bindings file %s", bindings_file);
 	return rc;
 }
 
@@ -778,7 +778,7 @@ int check_alias_settings(const struct config *conf)
 			rc = _check_bindings_file(conf, file, &bindings);
 			pthread_cleanup_pop(1);
 			if (rc == -1 && can_write && !conf->bindings_read_only)
-				rc = update_bindings_file(conf, &bindings);
+				rc = update_bindings_file(&bindings, conf->bindings_file);
 			else if (rc == -1)
 				condlog(0, "ERROR: bad settings in read-only bindings file %s",
 					conf->bindings_file);
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 15/21] libmultipath: update_bindings_file: use a single write()
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (13 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 14/21] libmultipath: update_bindings_file: take filename argument mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:45   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 16/21] libmultipath: update_bindings_file: don't log temp file name mwilck
                   ` (5 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Save code and syscalls by assembling the content in memory first.
write() may return less bytes written than expected. Deal with it.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/alias.c | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/libmultipath/alias.c b/libmultipath/alias.c
index 76d852f..c26f37c 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -118,22 +118,30 @@ static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
 static int write_bindings_file(const Bindings *bindings, int fd)
 {
 	struct binding *bnd;
-	STRBUF_ON_STACK(line);
+	STRBUF_ON_STACK(content);
 	int i;
+	size_t len;
 
-	if (write(fd, BINDINGS_FILE_HEADER, sizeof(BINDINGS_FILE_HEADER) - 1)
-	    != sizeof(BINDINGS_FILE_HEADER) - 1)
+	if (__append_strbuf_str(&content, BINDINGS_FILE_HEADER,
+				sizeof(BINDINGS_FILE_HEADER) - 1) == -1)
 		return -1;
 
 	vector_foreach_slot(bindings, bnd, i) {
-		int len;
+		if (print_strbuf(&content, "%s %s\n",
+					bnd->alias, bnd->wwid) < 0)
+			return -1;
+	}
+	len = get_strbuf_len(&content);
+	while (len > 0) {
+		ssize_t n = write(fd, get_strbuf_str(&content), len);
 
-		if ((len = print_strbuf(&line, "%s %s\n",
-					bnd->alias, bnd->wwid)) < 0)
+		if (n < 0)
+			return n;
+		else if (n == 0) {
+			condlog(2, "%s: short write", __func__);
 			return -1;
-		if (write(fd, get_strbuf_str(&line), len) != len)
-			return -1;
-		truncate_strbuf(&line, 0);
+		}
+		len -= n;
 	}
 	return 0;
 }
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 16/21] libmultipath: update_bindings_file: don't log temp file name
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (14 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 15/21] libmultipath: update_bindings_file: use a single write() mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:45   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 17/21] libmultipath: alias.c: factor out read_binding() mwilck
                   ` (4 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

The name of the temp file is unlikely to be helpful for uses,
and hard to predict in the unit test. Omit it.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/alias.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libmultipath/alias.c b/libmultipath/alias.c
index c26f37c..9ca5da8 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -167,8 +167,7 @@ static int update_bindings_file(const Bindings *bindings,
 	rc = write_bindings_file(bindings, fd);
 	pthread_cleanup_pop(1);
 	if (rc == -1) {
-		condlog(1, "failed to write new bindings file %s",
-			tempname);
+		condlog(1, "failed to write new bindings file");
 		unlink(tempname);
 		return rc;
 	}
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 17/21] libmultipath: alias.c: factor out read_binding()
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (15 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 16/21] libmultipath: update_bindings_file: don't log temp file name mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:45   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 18/21] libmultipath: keep bindings in memory mwilck
                   ` (3 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

This way we can test the parsing of input lines from the bindings
file more easily.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/alias.c | 58 ++++++++++++++++++++++++++++++--------------
 1 file changed, 40 insertions(+), 18 deletions(-)

diff --git a/libmultipath/alias.c b/libmultipath/alias.c
index 9ca5da8..2f9bc82 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -647,6 +647,43 @@ void cleanup_bindings(void)
 	free_bindings(&global_bindings);
 }
 
+enum {
+	READ_BINDING_OK,
+	READ_BINDING_SKIP,
+};
+
+static int read_binding(char *line, unsigned int linenr, char **alias,
+			char **wwid) {
+	char *c, *saveptr;
+
+	c = strpbrk(line, "#\n\r");
+	if (c)
+		*c = '\0';
+
+	*alias = strtok_r(line, " \t", &saveptr);
+	if (!*alias) /* blank line */
+		return READ_BINDING_SKIP;
+
+	*wwid = strtok_r(NULL, " \t", &saveptr);
+	if (!*wwid) {
+		condlog(1, "invalid line %u in bindings file, missing WWID",
+			linenr);
+		return READ_BINDING_SKIP;
+	}
+	if (strlen(*wwid) > WWID_SIZE - 1) {
+		condlog(3,
+			"Ignoring too large wwid at %u in bindings file",
+			linenr);
+		return READ_BINDING_SKIP;
+	}
+	c = strtok_r(NULL, " \t", &saveptr);
+	if (c)
+		/* This is non-fatal */
+		condlog(1, "invalid line %d in bindings file, extra args \"%s\"",
+			linenr, c);
+	return READ_BINDING_OK;
+}
+
 static int _check_bindings_file(const struct config *conf, FILE *file,
 				 Bindings *bindings)
 {
@@ -658,27 +695,12 @@ static int _check_bindings_file(const struct config *conf, FILE *file,
 
 	pthread_cleanup_push(cleanup_free_ptr, &line);
 	while ((n = getline(&line, &line_len, file)) >= 0) {
-		char *c, *alias, *wwid, *saveptr;
+		char *alias, *wwid;
 		const char *mpe_wwid;
 
-		linenr++;
-		c = strpbrk(line, "#\n\r");
-		if (c)
-			*c = '\0';
-		alias = strtok_r(line, " \t", &saveptr);
-		if (!alias) /* blank line */
+		if (read_binding(line, ++linenr, &alias, &wwid)
+		    == READ_BINDING_SKIP)
 			continue;
-		wwid = strtok_r(NULL, " \t", &saveptr);
-		if (!wwid) {
-			condlog(1, "invalid line %d in bindings file, missing WWID",
-				linenr);
-			continue;
-		}
-		c = strtok_r(NULL, " \t", &saveptr);
-		if (c)
-			/* This is non-fatal */
-			condlog(1, "invalid line %d in bindings file, extra args \"%s\"",
-				linenr, c);
 
 		mpe_wwid = get_mpe_wwid(conf->mptable, alias);
 		if (mpe_wwid && strcmp(mpe_wwid, wwid)) {
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 18/21] libmultipath: keep bindings in memory
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (16 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 17/21] libmultipath: alias.c: factor out read_binding() mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:47   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 19/21] multipath-tools tests: fix alias tests mwilck
                   ` (2 subsequent siblings)
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Rather than opening the bindings file every time we must retrieve
a binding, keep the contents in memory and write the file only
if additions have been made. This simplifies the code, and should speed up
alias lookups significantly. As a side effect, the aliases will be stored
sorted by alias, which changes the way aliases are allocated if there are
unused "holes" in the sequence of aliases. For example, if the bindings file
contains mpathb, mpathy, and mpatha, in this order, the next new alias used to
be mpathz and is now mpathc.

Another side effect is that multipathd will not automatically pick up changes
to the bindings file at runtime without a reconfigure operation. It is
questionable whether these on-the-fly changes were a good idea in the first
place, as inconsistent configurations may easily come to pass. It desired,
it would be feasible to implement automatic update of the bindings using the
existing inotify approach.

The new implementation of get_user_friendly_alias() is slightly different
than before. The logic is summarized in a comment in the code. Unit tests
will be provided that illustrate the changes.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/alias.c     | 369 ++++++++++++++++-----------------------
 libmultipath/alias.h     |   2 +-
 libmultipath/configure.c |   3 +-
 3 files changed, 157 insertions(+), 217 deletions(-)

diff --git a/libmultipath/alias.c b/libmultipath/alias.c
index 2f9bc82..6003df0 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -50,8 +50,6 @@
 "# alias wwid\n" \
 "#\n"
 
-static const char bindings_file_header[] = BINDINGS_FILE_HEADER;
-
 struct binding {
 	char *alias;
 	char *wwid;
@@ -80,6 +78,45 @@ static void _free_binding(struct binding *bdg)
 	free(bdg);
 }
 
+static const struct binding *get_binding_for_alias(const Bindings *bindings,
+						   const char *alias)
+{
+	const struct binding *bdg;
+	int i;
+
+	if (!alias)
+		return NULL;
+	vector_foreach_slot(bindings, bdg, i) {
+		if (!strncmp(bdg->alias, alias, WWID_SIZE)) {
+			condlog(3, "Found matching alias [%s] in bindings file."
+				" Setting wwid to %s", alias, bdg->wwid);
+			return bdg;
+		}
+	}
+
+	condlog(3, "No matching alias [%s] in bindings file.", alias);
+	return NULL;
+}
+
+static const struct binding *get_binding_for_wwid(const Bindings *bindings,
+						  const char *wwid)
+{
+	const struct binding *bdg;
+	int i;
+
+	if (!wwid)
+		return NULL;
+	vector_foreach_slot(bindings, bdg, i) {
+		if (!strncmp(bdg->wwid, wwid, WWID_SIZE)) {
+			condlog(3, "Found matching wwid [%s] in bindings file."
+				" Setting alias to %s", wwid, bdg->alias);
+			return bdg;
+		}
+	}
+	condlog(3, "No matching wwid [%s] in bindings file.", wwid);
+	return NULL;
+}
+
 static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
 {
 	struct binding *bdg;
@@ -115,6 +152,24 @@ static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
 	return BINDING_ERROR;
 }
 
+static int delete_binding(Bindings *bindings, const char *wwid)
+{
+	struct binding *bdg;
+	int i;
+
+	vector_foreach_slot(bindings, bdg, i) {
+		if (!strncmp(bdg->wwid, wwid, WWID_SIZE)) {
+			_free_binding(bdg);
+			break;
+		}
+	}
+	if (i >= VECTOR_SIZE(bindings))
+		return BINDING_NOTFOUND;
+
+	vector_del_slot(bindings, i);
+	return BINDING_DELETED;
+}
+
 static int write_bindings_file(const Bindings *bindings, int fd)
 {
 	struct binding *bnd;
@@ -267,38 +322,15 @@ static bool id_already_taken(int id, const char *prefix, const char *map_wwid)
 	return alias_already_taken(alias, map_wwid);
 }
 
-/*
- * Returns: 0   if matching entry in WWIDs file found
- *         -1   if an error occurs
- *         >0   a free ID that could be used for the WWID at hand
- * *map_alias is set to a freshly allocated string with the matching alias if
- * the function returns 0, or to NULL otherwise.
- */
-static int
-lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
-	       const char *prefix, int check_if_taken)
+int get_free_id(const Bindings *bindings, const char *prefix, const char *map_wwid)
 {
-	char buf[LINE_MAX];
-	unsigned int line_nr = 0;
-	int id = 1;
+	const struct binding *bdg;
+	int i, id = 1;
 	int biggest_id = 1;
 	int smallest_bigger_id = INT_MAX;
 
-	*map_alias = NULL;
-
-	rewind(f);
-	while (fgets(buf, LINE_MAX, f)) {
-		const char *alias, *wwid;
-		char *c, *saveptr;
-		int curr_id;
-
-		line_nr++;
-		c = strpbrk(buf, "#\n\r");
-		if (c)
-			*c = '\0';
-		alias = strtok_r(buf, " \t", &saveptr);
-		if (!alias) /* blank line */
-			continue;
+	vector_foreach_slot(bindings, bdg, i) {
+		int curr_id = scan_devname(bdg->alias, prefix);
 
 		/*
 		 * Find an unused index - explanation of the algorithm
@@ -333,8 +365,6 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
 		 * biggest_id is always > smallest_bigger_id, except in the
 		 * "perfectly ordered" case.
 		 */
-
-		curr_id = scan_devname(alias, prefix);
 		if (curr_id == id) {
 			if (id < INT_MAX)
 				id++;
@@ -345,36 +375,15 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
 		}
 		if (curr_id > biggest_id)
 			biggest_id = curr_id;
+
 		if (curr_id > id && curr_id < smallest_bigger_id)
 			smallest_bigger_id = curr_id;
-		wwid = strtok_r(NULL, " \t", &saveptr);
-		if (!wwid){
-			condlog(3,
-				"Ignoring malformed line %u in bindings file",
-				line_nr);
-			continue;
-		}
-		if (strcmp(wwid, map_wwid) == 0){
-			condlog(3, "Found matching wwid [%s] in bindings file."
-				" Setting alias to %s", wwid, alias);
-			*map_alias = strdup(alias);
-			if (*map_alias == NULL) {
-				condlog(0, "Cannot copy alias from bindings "
-					"file: out of memory");
-				return -1;
-			}
-			return 0;
-		}
 	}
-	if (!prefix && check_if_taken)
-		id = -1;
-	if (id >= smallest_bigger_id) {
-		if (biggest_id < INT_MAX)
-			id = biggest_id + 1;
-		else
-			id = -1;
-	}
-	if (id > 0 && check_if_taken) {
+
+	if (id >= smallest_bigger_id)
+		id = biggest_id < INT_MAX ? biggest_id + 1 : -1;
+
+	if (id > 0) {
 		while(id_already_taken(id, prefix, map_wwid)) {
 			if (id == INT_MAX) {
 				id = -1;
@@ -391,63 +400,16 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
 			}
 		}
 	}
-	if (id < 0) {
+
+	if (id < 0)
 		condlog(0, "no more available user_friendly_names");
-		return -1;
-	} else
-		condlog(3, "No matching wwid [%s] in bindings file.", map_wwid);
 	return id;
 }
 
-static int
-rlookup_binding(FILE *f, char *buff, const char *map_alias)
-{
-	char line[LINE_MAX];
-	unsigned int line_nr = 0;
-
-	buff[0] = '\0';
-
-	while (fgets(line, LINE_MAX, f)) {
-		char *c, *saveptr;
-		const char *alias, *wwid;
-
-		line_nr++;
-		c = strpbrk(line, "#\n\r");
-		if (c)
-			*c = '\0';
-		alias = strtok_r(line, " \t", &saveptr);
-		if (!alias) /* blank line */
-			continue;
-		wwid = strtok_r(NULL, " \t", &saveptr);
-		if (!wwid){
-			condlog(3,
-				"Ignoring malformed line %u in bindings file",
-				line_nr);
-			continue;
-		}
-		if (strlen(wwid) > WWID_SIZE - 1) {
-			condlog(3,
-				"Ignoring too large wwid at %u in bindings file", line_nr);
-			continue;
-		}
-		if (strcmp(alias, map_alias) == 0){
-			condlog(3, "Found matching alias [%s] in bindings file."
-				" Setting wwid to %s", alias, wwid);
-			strlcpy(buff, wwid, WWID_SIZE);
-			return 0;
-		}
-	}
-	condlog(3, "No matching alias [%s] in bindings file.", map_alias);
-
-	return -1;
-}
-
 static char *
-allocate_binding(int fd, const char *wwid, int id, const char *prefix)
+allocate_binding(const char *filename, const char *wwid, int id, const char *prefix)
 {
 	STRBUF_ON_STACK(buf);
-	off_t offset;
-	ssize_t len;
 	char *alias, *c;
 
 	if (id <= 0) {
@@ -460,29 +422,22 @@ allocate_binding(int fd, const char *wwid, int id, const char *prefix)
 	    format_devname(&buf, id) == -1)
 		return NULL;
 
-	if (print_strbuf(&buf, " %s\n", wwid) < 0)
-		return NULL;
-
-	offset = lseek(fd, 0, SEEK_END);
-	if (offset < 0){
-		condlog(0, "Cannot seek to end of bindings file : %s",
-			strerror(errno));
-		return NULL;
-	}
-
-	len = get_strbuf_len(&buf);
 	alias = steal_strbuf_str(&buf);
 
-	if (write(fd, alias, len) != len) {
-		condlog(0, "Cannot write binding to bindings file : %s",
-			strerror(errno));
-		/* clear partial write */
-		if (ftruncate(fd, offset))
-			condlog(0, "Cannot truncate the header : %s",
-				strerror(errno));
+	if (add_binding(&global_bindings, alias, wwid) != BINDING_ADDED) {
+		condlog(0, "%s: cannot allocate new binding %s for %s",
+			__func__, alias, wwid);
 		free(alias);
 		return NULL;
 	}
+
+	if (update_bindings_file(&global_bindings, filename) == -1) {
+		condlog(1, "%s: deleting binding %s for %s", __func__, alias, wwid);
+		delete_binding(&global_bindings, wwid);
+		free(alias);
+		return NULL;
+	}
+
 	c = strchr(alias, ' ');
 	if (c)
 		*c = '\0';
@@ -491,144 +446,130 @@ allocate_binding(int fd, const char *wwid, int id, const char *prefix)
 	return alias;
 }
 
+/*
+ * get_user_friendly_alias() action table
+ *
+ * The table shows the various cases, the actions taken, and the CI
+ * functions from tests/alias.c that represent them.
+ *
+ *  - O: old alias given
+ *  - A: old alias in table (y: yes, correct WWID; X: yes, wrong WWID)
+ *  - W: wwid in table
+ *  - U: old alias is used
+ *
+ *  | No | O | A | W | U | action                                     | function gufa_X              |
+ *  |----+---+---+---+---+--------------------------------------------+------------------------------|
+ *  |  1 | n | - | n | - | get new alias                              | nomatch_Y                    |
+ *  |  2 | n | - | y | - | use alias from bindings                    | match_a_Y                    |
+ *  |  3 | y | n | n | n | add binding for old alias                  | old_nomatch_nowwidmatch      |
+ *  |  4 | y | n | n | y | error, no alias (can't add entry)          | old_nomatch_nowwidmatch_used |
+ *  |  5 | y | n | y | - | use alias from bindings (avoid duplicates) | old_nomatch_wwidmatch        |
+ *  |  6 | y | y | n | - | [ impossible ]                             | -                            |
+ *  |  7 | y | y | y | n | use old alias == alias from bindings       | old_match                    |
+ *  |  8 | y | y | y | y | error, no alias (would be duplicate)       | old_match_used               |
+ *  |  9 | y | X | n | - | get new alias                              | old_match_other              |
+ *  | 10 | y | X | y | - | use alias from bindings                    | old_match_other_wwidmatch    |
+ *
+ * Notes:
+ *  - "use alias from bindings" means that the alias from the bindings file will
+ *     be tried; if it is in use, the alias selection will fail. No other
+ *     bindings will be attempted.
+ *  - "get new alias" fails if all aliases are used up, or if writing the
+ *     bindings file fails.
+ */
+
 char *get_user_friendly_alias(const char *wwid, const char *file, const char *alias_old,
 			      const char *prefix, bool bindings_read_only)
 {
 	char *alias = NULL;
 	int id = 0;
-	int fd, can_write;
 	bool new_binding = false;
-	char buff[WWID_SIZE];
-	FILE *f;
+	bool old_alias_taken = false;
+	const struct binding *bdg;
 
-	fd = open_file(file, &can_write, bindings_file_header);
-	if (fd < 0)
-		return NULL;
-
-	f = fdopen(fd, "r");
-	if (!f) {
-		condlog(0, "cannot fdopen on bindings file descriptor");
-		close(fd);
-		return NULL;
-	}
-
-	if (!strlen(alias_old))
+	if (!*alias_old)
 		goto new_alias;
 
-	/* lookup the binding. if it exists, the wwid will be in buff
-	 * either way, id contains the id for the alias
-	 */
-	rlookup_binding(f, buff, alias_old);
-
-	if (strlen(buff) > 0) {
-		/* if buff is our wwid, it's already
-		 * allocated correctly
-		 */
-		if (strcmp(buff, wwid) == 0) {
+	/* See if there's a binding matching both alias_old and wwid */
+	bdg = get_binding_for_alias(&global_bindings, alias_old);
+	if (bdg) {
+		if (!strcmp(bdg->wwid, wwid)) {
 			if (!alias_already_taken(alias_old, wwid))
 				alias = strdup(alias_old);
 			else
 				condlog(0, "ERROR: old alias [%s] for wwid [%s] is used by other map",
 					alias_old, wwid);
 			goto out;
-
 		} else {
 			condlog(0, "alias %s already bound to wwid %s, cannot reuse",
-				alias_old, buff);
-			goto new_alias;		     ;
+				alias_old, bdg->wwid);
+			goto new_alias;
 		}
 	}
 
-	/*
-	 * Look for an existing alias in the bindings file.
-	 * Pass prefix = NULL, so lookup_binding() won't try to allocate a new id.
-	 */
-	id = lookup_binding(f, wwid, &alias, NULL, 0);
-	if (alias) {
-		if (alias_already_taken(alias, wwid)) {
-			free(alias);
-			alias = NULL;
-		} else
-			condlog(3, "Use existing binding [%s] for WWID [%s]",
-				alias, wwid);
-		goto out;
-	}
-
 	/* allocate the existing alias in the bindings file */
-	id = scan_devname(alias_old, prefix);
-	if (id > 0 && id_already_taken(id, prefix, wwid)) {
+	if (alias_already_taken(alias_old, wwid)) {
 		condlog(0, "ERROR: old alias [%s] for wwid [%s] is used by other map",
 			alias_old, wwid);
+		old_alias_taken = true;
+	} else
+		id = scan_devname(alias_old, prefix);
+
+new_alias:
+	/* Check for existing binding of WWID */
+	bdg = get_binding_for_wwid(&global_bindings, wwid);
+	if (bdg) {
+		if (!alias_already_taken(bdg->alias, wwid)) {
+			condlog(3, "Use existing binding [%s] for WWID [%s]",
+				bdg->alias, wwid);
+			alias = strdup(bdg->alias);
+		}
 		goto out;
 	}
 
-new_alias:
-	if (id <= 0) {
+	/*
+	 * old_alias_taken means that the WWID is not in the bindings file, but
+	 * alias_old is currently taken by a different WWID. We shouldn't added
+	 * a new binding in this case.
+	 */
+	if (id <= 0 && !old_alias_taken) {
 		/*
 		 * no existing alias was provided, or allocating it
 		 * failed. Try a new one.
 		 */
-		id = lookup_binding(f, wwid, &alias, prefix, 1);
-		if (id == 0 && alias_already_taken(alias, wwid)) {
-			free(alias);
-			alias = NULL;
-		}
+		id = get_free_id(&global_bindings, prefix, wwid);
 		if (id <= 0)
 			goto out;
 		else
 			new_binding = true;
 	}
 
-	if (fflush(f) != 0) {
-		condlog(0, "cannot fflush bindings file stream : %s",
-			strerror(errno));
-		goto out;
-	}
+	if (!bindings_read_only && id > 0)
+		alias = allocate_binding(file, wwid, id, prefix);
 
-	if (can_write && !bindings_read_only) {
-		alias = allocate_binding(fd, wwid, id, prefix);
-		if (alias && !new_binding)
-			condlog(2, "Allocated existing binding [%s] for WWID [%s]",
-				alias, wwid);
-	}
+	if (alias && !new_binding)
+		condlog(2, "Allocated existing binding [%s] for WWID [%s]",
+			alias, wwid);
 
 out:
-	pthread_cleanup_push(free, alias);
-	fclose(f);
-	pthread_cleanup_pop(0);
 	return alias;
 }
 
-int
-get_user_friendly_wwid(const char *alias, char *buff, const char *file)
+int get_user_friendly_wwid(const char *alias, char *buff)
 {
-	int fd, unused;
-	FILE *f;
+	const struct binding *bdg;
 
 	if (!alias || *alias == '\0') {
 		condlog(3, "Cannot find binding for empty alias");
 		return -1;
 	}
 
-	fd = open_file(file, &unused, bindings_file_header);
-	if (fd < 0)
-		return -1;
-
-	f = fdopen(fd, "r");
-	if (!f) {
-		condlog(0, "cannot fdopen on bindings file descriptor : %s",
-			strerror(errno));
-		close(fd);
+	bdg = get_binding_for_alias(&global_bindings, alias);
+	if (!bdg) {
+		*buff = '\0';
 		return -1;
 	}
-
-	rlookup_binding(f, buff, alias);
-	if (!strlen(buff)) {
-		fclose(f);
-		return -1;
-	}
-
-	fclose(f);
+	strlcpy(buff, bdg->alias, WWID_SIZE);
 	return 0;
 }
 
diff --git a/libmultipath/alias.h b/libmultipath/alias.h
index 37b49d9..5ef6720 100644
--- a/libmultipath/alias.h
+++ b/libmultipath/alias.h
@@ -2,7 +2,7 @@
 #define _ALIAS_H
 
 int valid_alias(const char *alias);
-int get_user_friendly_wwid(const char *alias, char *buff, const char *file);
+int get_user_friendly_wwid(const char *alias, char *buff);
 char *get_user_friendly_alias(const char *wwid, const char *file,
 			      const char *alias_old,
 			      const char *prefix, bool bindings_read_only);
diff --git a/libmultipath/configure.c b/libmultipath/configure.c
index 029fbbd..d809490 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -1378,8 +1378,7 @@ static int _get_refwwid(enum mpath_cmds cmd, const char *dev,
 			refwwid = tmpwwid;
 
 		/* or may be a binding */
-		else if (get_user_friendly_wwid(dev, tmpwwid,
-						conf->bindings_file) == 0)
+		else if (get_user_friendly_wwid(dev, tmpwwid) == 0)
 			refwwid = tmpwwid;
 
 		/* or may be an alias */
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 19/21] multipath-tools tests: fix alias tests
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (17 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 18/21] libmultipath: keep bindings in memory mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:47   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 20/21] libmultipath: dm_get_uuid(): return emtpy UUID for non-existing maps mwilck
  2023-09-01 18:02 ` [dm-devel] [PATCH 21/21] libmultipath: adapt to new semantics of dm_get_uuid() mwilck
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

The different implementation of get_user_friendly_alias() and its helpers
necessitates changes in the unit tests. It would be nice if it didn't, but the
unit tests are too closely bound to the implementation to make this possible.

- The bindings table is held in memory in alphabetically sorted order, which
  may change the result of looking for free alias IDs if the entries in the
  bindings file were unordered initially. In particular, if only a small
  number of bindings exists, "holes" in the file will be detected more easily.
  But because the sort order of the aliases differs from simple alphabetic
  sorting ("mpathz" precedes "mpathaa"), a bindings file that contains all
  bindings from "a" to "aa" (or more) will appear unsorted.
  As an extra check, some of the unit tests deliberately use a different
  implementation of add_binding() that does not order the bindings
  table.

- Broken lines in the bindings file never make it to the in-memory
  representation. An alias that appeard "used" because it occurred in a broken
  line will not appear used any more. Warnings about malformed lines will only
  be printed while the bindings file is read, not from get_user_friendly_alias().

- The match_line argument of mock_bindings_file() is obsolete.

- lookup_binding() and rlookup_binding() have been removed from
  libmultipath. They are now emulated in the unit test code.

- lookup_binding() didn't check for used alias in all cases previously, but it does now.

- prefix != NULL and check_if_taken == false is not supported any more
  in lookup_binding().

- allocate_binding() uses a very different sequence of systems calls now, as
  it's implemented using update_bindings_file(). In particular, it's now more
  difficult to predict the content of the write() call that creates the
  bindings file. See comments for __wrap_write().

- some unit tests for get_user_friendly_alias() had to call
  mock_bindings_file() twice, because the old implementation would read the
  file twice (first rlookup_binding() and then lookup_binding()). This is not
  necessary any more.

- The unit tests need a teardown function to clear the bindings table in memory.

- Minor changes are necessary because of changed ordering of the log messages.
  Previously, lookup_binding() combined check for an existing entry and the
  search for a new ID. The new algorithm does this in two separate steps and
  tests for used aliases in between, which causes a change in the order in which
  log messages are emitted.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 tests/alias.c | 1013 +++++++++++++++++++++++++++++++------------------
 1 file changed, 638 insertions(+), 375 deletions(-)

diff --git a/tests/alias.c b/tests/alias.c
index a1415c6..4ac6425 100644
--- a/tests/alias.c
+++ b/tests/alias.c
@@ -3,10 +3,12 @@
 #include <setjmp.h>
 #include <stdio.h>
 #include <cmocka.h>
+#include "strbuf.h"
 #include "util.h"
 #include "alias.h"
 #include "test-log.h"
 #include <errno.h>
+#include <string.h>
 
 #include "globals.c"
 #include "../libmultipath/alias.c"
@@ -20,18 +22,6 @@
 #define MPATH_ID_INT_MAX_p1 "fxshrxx"
 #endif
 
-void __wrap_rewind(FILE *stream)
-{}
-
-char *__wrap_fgets(char *buf, int n, FILE *stream)
-{
-	char *val = mock_ptr_type(char *);
-	if (!val)
-		return NULL;
-	strlcpy(buf, val, n);
-	return buf;
-}
-
 static int __set_errno(int err)
 {
 	if (err >= 0) {
@@ -43,25 +33,46 @@ static int __set_errno(int err)
 	}
 }
 
-off_t __wrap_lseek(int fd, off_t offset, int whence)
-{
-	return __set_errno(mock_type(int));
-
-}
-
+/*
+ * allocate_binding -> write_bindings_file() writes the entire file, i.e. the
+ * header, any pre-existing bindings, and the new binding. The complete content
+ * depends on history and is different to predict here. Therefore we check only
+ * the newly added binding. Because add_binding() sorts entries, this new
+ * binding isn't necessarily the last one; receive it from will_return() and
+ * search for it with strstr().
+ * If the string to be written doesn't start with the bindings file
+ * header, it's a test of a partial write.
+ */
 ssize_t __wrap_write(int fd, const void *buf, size_t count)
 {
+	const char *binding, *start;
+
+#if DEBUG_WRITE
+	fprintf(stderr, "%s: %zx exp %zx\n===\n%s\n===\n", __func__, strlen(buf),
+		count, (const char *)buf);
+#endif
+	if (!strncmp((const char *)buf, BINDINGS_FILE_HEADER,
+		     sizeof(BINDINGS_FILE_HEADER) - 1))
+		start = (const char *)buf + sizeof(BINDINGS_FILE_HEADER) - 1;
+	else
+		start = buf;
+	binding = mock_ptr_type(char *);
+	start = strstr(start, binding);
 	check_expected(count);
-	check_expected(buf);
+	assert_ptr_not_equal(start, NULL);
 	return __set_errno(mock_type(int));
 }
 
-int __wrap_ftruncate(int fd, off_t length)
+int __wrap_rename(const char *old, const char *new)
 {
-	check_expected(length);
 	return __set_errno(mock_type(int));
 }
 
+int __wrap_mkstemp(char *template)
+{
+	return 10;
+}
+
 int __wrap_dm_map_present(const char * str)
 {
 	check_expected(str);
@@ -84,32 +95,6 @@ int __wrap_dm_get_uuid(const char *name, char *uuid, int uuid_len)
 #define TEST_FDNO 1234
 #define TEST_FPTR ((FILE *) 0xaffe)
 
-int __wrap_open_file(const char *file, int *can_write, const char *header)
-{
-	int cw = mock_type(int);
-
-        *can_write = cw;
-	return TEST_FDNO;
-}
-
-FILE *__wrap_fdopen(int fd, const char *mode)
-{
-	assert_int_equal(fd, TEST_FDNO);
-	return TEST_FPTR;
-}
-
-int __wrap_fflush(FILE *f)
-{
-	assert_ptr_equal(f, TEST_FPTR);
-	return 0;
-}
-
-int __wrap_fclose(FILE *f)
-{
-	assert_ptr_equal(f, TEST_FPTR);
-	return 0;
-}
-
 /* strbuf wrapper for the old format_devname() */
 static int __format_devname(char *name, int id, size_t len, const char *prefix)
 {
@@ -466,22 +451,85 @@ static void mock_self_alias(const char *alias, const char *wwid)
 		expect_condlog(3, USED_STR(alias, wwid));		\
 	} while(0)
 
-static void mock_bindings_file(const char *content, int match_line)
+static int add_binding_unsorted(Bindings *bindings,
+				const char *alias, const char *wwid)
 {
-	static char cnt[1024];
-	char *token;
+	struct binding *bdg = calloc(1, sizeof(*bdg));
+
+	if (!bdg)
+		return -1;
+	bdg->wwid = strdup(wwid);
+	bdg->alias = strdup(alias);
+	if (!bdg->wwid || !bdg->alias || !vector_alloc_slot(bindings)) {
+		free(bdg->alias);
+		free(bdg->wwid);
+		free(bdg);
+		return BINDING_ERROR;
+	}
+	vector_set_slot(bindings, bdg);
+	return BINDING_ADDED;
+}
+
+static void __mock_bindings_file(const char *content,
+				 int (*add)(Bindings *, const char *, const char *))
+{
+	char *cnt __attribute__((cleanup(cleanup_charp))) = NULL;
+	char *token, *savep = NULL;
 	int i;
 
-	assert_in_range(strlcpy(cnt, content, sizeof(cnt)), 0, sizeof(cnt) - 1);
+	cnt = strdup(content);
+	assert_ptr_not_equal(cnt, NULL);
 
-	for (token = strtok(cnt, "\n"), i = 0;
+	for (token = strtok_r(cnt, "\n", &savep), i = 0;
 	     token && *token;
-	     token = strtok(NULL, "\n"), i++) {
-		will_return(__wrap_fgets, token);
-		if (match_line == i)
-			return;
+	     token = strtok_r(NULL, "\n", &savep), i++) {
+		char *alias, *wwid;
+		int rc;
+
+		if (read_binding(token, i + 1, &alias, &wwid)
+		    == READ_BINDING_SKIP)
+			continue;
+
+		rc = add(&global_bindings, alias, wwid);
+		assert_int_equal(rc, BINDING_ADDED);
 	}
-	will_return(__wrap_fgets, NULL);
+}
+
+static void mock_bindings_file(const char *content) {
+	return __mock_bindings_file(content, add_binding);
+}
+
+static void mock_bindings_file_unsorted(const char *content) {
+	return __mock_bindings_file(content, add_binding_unsorted);
+}
+
+static int teardown_bindings(void **state)
+{
+	cleanup_bindings();
+	return 0;
+}
+
+static int lookup_binding(FILE *dummy, const char *wwid, char **alias,
+			  const char *prefix, int check_if_taken)
+{
+	const struct binding *bdg;
+	int id;
+
+	/*
+	 * get_free_id() always checks if aliases are taken.
+	 * Therefore if prefix is non-null, check_if_taken must be true.
+	 */
+	assert_true(!prefix || check_if_taken);
+	*alias = NULL;
+	bdg = get_binding_for_wwid(&global_bindings, wwid);
+	if (bdg) {
+		*alias = strdup(bdg->alias);
+		return 0;
+	} else if (!prefix && check_if_taken)
+		return -1;
+
+	id = get_free_id(&global_bindings, prefix, wwid);
+	return id;
 }
 
 static void lb_empty(void **state)
@@ -489,7 +537,7 @@ static void lb_empty(void **state)
 	int rc;
 	char *alias;
 
-	mock_bindings_file("", -1);
+	mock_bindings_file("");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, NULL, 0);
 	assert_int_equal(rc, 1);
@@ -501,7 +549,7 @@ static void lb_empty_unused(void **state)
 	int rc;
 	char *alias;
 
-	mock_bindings_file("", -1);
+	mock_bindings_file("");
 	mock_unused_alias("MPATHa");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
@@ -515,10 +563,10 @@ static void lb_empty_failed(void **state)
 	int rc;
 	char *alias;
 
-	mock_bindings_file("", -1);
+	mock_bindings_file("");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	mock_failed_alias("MPATHa", "WWID0");
 	mock_unused_alias("MPATHb");
-	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
@@ -530,10 +578,10 @@ static void lb_empty_1_used(void **state)
 	int rc;
 	char *alias;
 
-	mock_bindings_file("", -1);
+	mock_bindings_file("");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	mock_used_alias("MPATHa", "WWID0");
 	mock_unused_alias("MPATHb");
-	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
@@ -545,10 +593,10 @@ static void lb_empty_1_used_self(void **state)
 	int rc;
 	char *alias;
 
-	mock_bindings_file("", -1);
+	mock_bindings_file("");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	mock_used_alias("MPATHa", "WWID0");
 	mock_self_alias("MPATHb", "WWID0");
-	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
@@ -560,9 +608,9 @@ static void lb_match_a(void **state)
 	int rc;
 	char *alias;
 
-	mock_bindings_file("MPATHa WWID0\n", 0);
+	mock_bindings_file("MPATHa WWID0\n");
 	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
-	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 0);
+	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
 	assert_int_equal(rc, 0);
 	assert_ptr_not_equal(alias, NULL);
 	assert_string_equal(alias, "MPATHa");
@@ -574,9 +622,10 @@ static void lb_nomatch_a(void **state)
 	int rc;
 	char *alias;
 
-	mock_bindings_file("MPATHa WWID0\n", -1);
+	mock_bindings_file("MPATHa WWID0\n");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
-	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 0);
+	mock_unused_alias("MPATHb");
+	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
 }
@@ -586,8 +635,8 @@ static void lb_nomatch_a_bad_check(void **state)
 	int rc;
 	char *alias;
 
-	mock_bindings_file("MPATHa WWID0\n", -1);
-	expect_condlog(0, NOMORE_STR);
+	mock_bindings_file("MPATHa WWID0\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
 	rc = lookup_binding(NULL, "WWID1", &alias, NULL, 1);
 	assert_int_equal(rc, -1);
 	assert_ptr_equal(alias, NULL);
@@ -598,7 +647,7 @@ static void lb_nomatch_a_unused(void **state)
 	int rc;
 	char *alias;
 
-	mock_bindings_file("MPATHa WWID0\n", -1);
+	mock_bindings_file("MPATHa WWID0\n");
 	mock_unused_alias("MPATHb");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
 	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
@@ -611,27 +660,27 @@ static void lb_nomatch_a_3_used_failed_self(void **state)
 	int rc;
 	char *alias;
 
-	mock_bindings_file("MPATHa WWID0\n", -1);
+	mock_bindings_file("MPATHa WWID0\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
 	mock_used_alias("MPATHb", "WWID1");
 	mock_used_alias("MPATHc", "WWID1");
 	mock_used_alias("MPATHd", "WWID1");
 	mock_failed_alias("MPATHe", "WWID1");
 	mock_self_alias("MPATHf", "WWID1");
-	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
 	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
 	assert_int_equal(rc, 6);
 	assert_ptr_equal(alias, NULL);
 }
 
-static void do_lb_match_c(void **state, int check_if_taken)
+static void do_lb_match_c(void **state)
 {
 	int rc;
 	char *alias;
 
 	mock_bindings_file("MPATHa WWID0\n"
-			   "MPATHc WWID1", 1);
+			   "MPATHc WWID1");
 	expect_condlog(3, FOUND_STR("MPATHc", "WWID1"));
-	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", check_if_taken);
+	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
 	assert_int_equal(rc, 0);
 	assert_ptr_not_equal(alias, NULL);
 	assert_string_equal(alias, "MPATHc");
@@ -640,12 +689,12 @@ static void do_lb_match_c(void **state, int check_if_taken)
 
 static void lb_match_c(void **state)
 {
-	do_lb_match_c(state, 0);
+	do_lb_match_c(state);
 }
 
 static void lb_match_c_check(void **state)
 {
-	do_lb_match_c(state, 1);
+	do_lb_match_c(state);
 }
 
 static void lb_nomatch_a_c(void **state)
@@ -654,9 +703,10 @@ static void lb_nomatch_a_c(void **state)
 	char *alias;
 
 	mock_bindings_file("MPATHa WWID0\n"
-			   "MPATHc WWID1", -1);
+			   "MPATHc WWID1");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
-	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
+	mock_unused_alias("MPATHb");
+	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
 }
@@ -667,7 +717,7 @@ static void lb_nomatch_a_d_unused(void **state)
 	char *alias;
 
 	mock_bindings_file("MPATHa WWID0\n"
-			   "MPATHd WWID1", -1);
+			   "MPATHd WWID1");
 	mock_unused_alias("MPATHb");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
@@ -681,10 +731,10 @@ static void lb_nomatch_a_d_1_used(void **state)
 	char *alias;
 
 	mock_bindings_file("MPATHa WWID0\n"
-			   "MPATHd WWID1", -1);
+			   "MPATHd WWID1");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	mock_used_alias("MPATHb", "WWID2");
 	mock_unused_alias("MPATHc");
-	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 3);
 	assert_ptr_equal(alias, NULL);
@@ -696,11 +746,11 @@ static void lb_nomatch_a_d_2_used(void **state)
 	char *alias;
 
 	mock_bindings_file("MPATHa WWID0\n"
-			   "MPATHd WWID1", -1);
+			   "MPATHd WWID1");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	mock_used_alias("MPATHb", "WWID2");
 	mock_used_alias("MPATHc", "WWID2");
 	mock_unused_alias("MPATHe");
-	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 5);
 	assert_ptr_equal(alias, NULL);
@@ -712,12 +762,12 @@ static void lb_nomatch_a_d_3_used(void **state)
 	char *alias;
 
 	mock_bindings_file("MPATHa WWID0\n"
-			   "MPATHd WWID1", -1);
+			   "MPATHd WWID1");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	mock_used_alias("MPATHb", "WWID2");
 	mock_used_alias("MPATHc", "WWID2");
 	mock_used_alias("MPATHe", "WWID2");
 	mock_unused_alias("MPATHf");
-	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 6);
 	assert_ptr_equal(alias, NULL);
@@ -729,9 +779,10 @@ static void lb_nomatch_c_a(void **state)
 	char *alias;
 
 	mock_bindings_file("MPATHc WWID1\n"
-			   "MPATHa WWID0\n", -1);
+			   "MPATHa WWID0\n");
+	mock_unused_alias("MPATHb");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
-	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
+	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
 }
@@ -743,7 +794,7 @@ static void lb_nomatch_d_a_unused(void **state)
 
 	mock_bindings_file("MPATHc WWID1\n"
 			   "MPATHa WWID0\n"
-			   "MPATHd WWID0\n", -1);
+			   "MPATHd WWID0\n");
 	mock_unused_alias("MPATHb");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
@@ -758,10 +809,10 @@ static void lb_nomatch_d_a_1_used(void **state)
 
 	mock_bindings_file("MPATHc WWID1\n"
 			   "MPATHa WWID0\n"
-			   "MPATHd WWID0\n", -1);
+			   "MPATHd WWID0\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	mock_used_alias("MPATHb", "WWID2");
 	mock_unused_alias("MPATHe");
-	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 5);
 	assert_ptr_equal(alias, NULL);
@@ -774,9 +825,10 @@ static void lb_nomatch_a_b(void **state)
 
 	mock_bindings_file("MPATHa WWID0\n"
 			   "MPATHz WWID26\n"
-			   "MPATHb WWID1\n", -1);
+			   "MPATHb WWID1\n");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
-	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
+	mock_unused_alias("MPATHc");
+	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 3);
 	assert_ptr_equal(alias, NULL);
 }
@@ -786,13 +838,19 @@ static void lb_nomatch_a_b_bad(void **state)
 	int rc;
 	char *alias;
 
+	expect_condlog(1, "invalid line 3 in bindings file, missing WWID\n");
+	/*
+	 * The broken line will be ignored when constructing the bindings vector.
+	 * Thus in lookup_binding() MPATHb is never encountered,
+	 * and MPATHb appears usable.
+	 */
 	mock_bindings_file("MPATHa WWID0\n"
 			   "MPATHz WWID26\n"
-			   "MPATHb\n", -1);
-	expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
+			   "MPATHb\n");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
-	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
-	assert_int_equal(rc, 3);
+	mock_unused_alias("MPATHb");
+	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+	assert_int_equal(rc, 2);
 	assert_ptr_equal(alias, NULL);
 }
 
@@ -801,28 +859,140 @@ static void lb_nomatch_a_b_bad_self(void **state)
 	int rc;
 	char *alias;
 
+	expect_condlog(1, "invalid line 3 in bindings file, missing WWID\n");
 	mock_bindings_file("MPATHa WWID0\n"
 			   "MPATHz WWID26\n"
-			   "MPATHb\n", -1);
-	expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
-	mock_self_alias("MPATHc", "WWID2");
+			   "MPATHb\n");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+	mock_self_alias("MPATHb", "WWID2");
+	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+	assert_int_equal(rc, 2);
+	assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_b_z_a(void **state)
+{
+	int rc;
+	char *alias;
+
+	/*
+	 * add_bindings() sorts alphabetically. Therefore get_free_id()
+	 * finds MPATHc as a free entry.
+	 */
+	mock_bindings_file("MPATHb WWID1\n"
+			   "MPATHz WWID26\n"
+			   "MPATHa WWID0\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+	mock_unused_alias("MPATHc");
 	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
 	assert_int_equal(rc, 3);
 	assert_ptr_equal(alias, NULL);
 }
 
+static void lb_nomatch_b_aa_a(void **state)
+{
+	int rc;
+	char *alias;
+
+	/*
+	 * add_bindings() sorts alphabetically. ("a", "aa", b").
+	 * The get_free_id() algorithm finds the "hole" after "b".
+	 */
+	mock_bindings_file("MPATHb WWID1\n"
+			   "MPATHz WWID26\n"
+			   "MPATHa WWID0\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+	mock_unused_alias("MPATHc");
+	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+	assert_int_equal(rc, 3);
+	assert_ptr_equal(alias, NULL);
+}
+
+static void fill_bindings(struct strbuf *buf, int start, int end)
+{
+	int i;
+
+	for (i = start; i <= end; i++) {
+		print_strbuf(buf,  "MPATH");
+		format_devname(buf, i + 1);
+		print_strbuf(buf,  " WWID%d\n", i);
+	}
+}
+
+static void lb_nomatch_b_a_aa(void **state)
+{
+	int rc;
+	char *alias;
+	STRBUF_ON_STACK(buf);
+
+	/*
+	 * add_bindings() sorts alphabetically. ("a", "aa", "ab", "b", "c", ...)
+	 * lookup_binding finds MPATHac as next free entry.
+	 */
+	fill_bindings(&buf, 0, 26);
+	mock_bindings_file(get_strbuf_str(&buf));
+	expect_condlog(3, NOMATCH_WWID_STR("WWID28"));
+	mock_unused_alias("MPATHab");
+	rc = lookup_binding(NULL, "WWID28", &alias, "MPATH", 1);
+	assert_int_equal(rc, 28);
+	assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_b_a_aa_zz(void **state)
+{
+	int rc;
+	char *alias;
+	STRBUF_ON_STACK(buf);
+
+	/*
+	 * add_bindings() sorts alphabetically. ("a", "aa", "ab", "b", "c", ...)
+	 * lookup_binding finds MPATHaaa as next free entry, because MPATHaa is
+	 * found before MPATHb, and MPATHzz was in the bindings, too.
+	 */
+	for (i = 0; i <= 26; i++) {
+		print_strbuf(&buf,  "MPATH");
+		format_devname(&buf, i + 1);
+		print_strbuf(&buf,  " WWID%d\n", i);
+	}
+	print_strbuf(&buf, "MPATHzz WWID676\n");
+	mock_bindings_file(get_strbuf_str(&buf));
+	expect_condlog(3, NOMATCH_WWID_STR("WWID703"));
+	mock_unused_alias("MPATHaaa");
+	rc = lookup_binding(NULL, "WWID703", &alias, "MPATH", 1);
+	assert_int_equal(rc, 703);
+	assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_b_z_a_unsorted(void **state)
+{
+	int rc;
+	char *alias;
+
+	/*
+	 * With unsorted bindings (shouldn't happen normally), get_free_id()
+	 * plays safe and returns MPATHaa as first free entry.
+	 */
+	mock_bindings_file_unsorted("MPATHb WWID1\n"
+				    "MPATHz WWID26\n"
+				    "MPATHa WWID0\n");
+	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+	mock_unused_alias("MPATHaa");
+	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+	assert_int_equal(rc, 27);
+	assert_ptr_equal(alias, NULL);
+}
+
 static void lb_nomatch_b_a(void **state)
 {
 	int rc;
 	char *alias;
 
 	mock_bindings_file("MPATHb WWID1\n"
-			   "MPATHz WWID26\n"
-			   "MPATHa WWID0\n", -1);
+			   "MPATHa WWID0\n");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
-	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
-	assert_int_equal(rc, 27);
+	mock_unused_alias("MPATHc");
+	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+	assert_int_equal(rc, 3);
 	assert_ptr_equal(alias, NULL);
 }
 
@@ -830,55 +1000,59 @@ static void lb_nomatch_b_a_3_used(void **state)
 {
 	int rc;
 	char *alias;
+	STRBUF_ON_STACK(buf);
 
-	mock_bindings_file("MPATHb WWID1\n"
-			   "MPATHz WWID26\n"
-			   "MPATHa WWID0\n", -1);
-	mock_used_alias("MPATHaa", "WWID2");
-	mock_used_alias("MPATHab", "WWID2");
-	mock_used_alias("MPATHac", "WWID2");
-	mock_unused_alias("MPATHad");
-	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
-	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
-	assert_int_equal(rc, 30);
+	fill_bindings(&buf, 0, 26);
+	mock_bindings_file(get_strbuf_str(&buf));
+	expect_condlog(3, NOMATCH_WWID_STR("WWID31"));
+	mock_used_alias("MPATHab", "WWID31");
+	mock_used_alias("MPATHac", "WWID31");
+	mock_used_alias("MPATHad", "WWID31");
+	mock_unused_alias("MPATHae");
+	rc = lookup_binding(NULL, "WWID31", &alias, "MPATH", 1);
+	assert_int_equal(rc, 31);
 	assert_ptr_equal(alias, NULL);
 }
 
 #ifdef MPATH_ID_INT_MAX
-static void do_lb_nomatch_int_max(void **state, int check_if_taken)
+/*
+ * The bindings will be sorted by alias, alphabetically, which is not
+ * the same as the "numeric" sort order for user-friendly aliases.
+ * get_free_id() selects the highest used ID + 1 if an unsorted entry
+ * is encountered in the bindings table and it's id is equal to the
+ * next "expected" id. This happens if all IDs from "a" to "aa" are
+ * in the table. If the INT_MAX entry is in the table, too, it will
+ * overflow.
+ */
+static void lb_nomatch_int_max(void **state)
 {
 	int rc;
 	char *alias;
+	STRBUF_ON_STACK(buf);
 
-	mock_bindings_file("MPATHb WWID1\n"
-			   "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n"
-			   "MPATHa WWID0\n", -1);
+	fill_bindings(&buf, 0, 26);
+	print_strbuf(&buf, "MPATH%s WWIDMAX\n", MPATH_ID_INT_MAX);
+	mock_bindings_file(get_strbuf_str(&buf));
+	expect_condlog(3, NOMATCH_WWID_STR("WWIDNOMORE"));
 	expect_condlog(0, NOMORE_STR);
-	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", check_if_taken);
+	rc = lookup_binding(NULL, "WWIDNOMORE", &alias, "MPATH", 1);
 	assert_int_equal(rc, -1);
 	assert_ptr_equal(alias, NULL);
 }
 
-static void lb_nomatch_int_max(void **state)
-{
-	do_lb_nomatch_int_max(state, 0);
-}
-
-static void lb_nomatch_int_max_check(void **state)
-{
-	do_lb_nomatch_int_max(state, 1);
-}
-
 static void lb_nomatch_int_max_used(void **state)
 {
 	int rc;
 	char *alias;
+	STRBUF_ON_STACK(buf);
 
-	mock_bindings_file("MPATHb WWID1\n"
-			   "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n", -1);
-	mock_used_alias("MPATHa", "WWID2");
+	fill_bindings(&buf, 1, 26);
+	print_strbuf(&buf, "MPATH%s WWIDMAX\n", MPATH_ID_INT_MAX);
+	mock_bindings_file(get_strbuf_str(&buf));
+	expect_condlog(3, NOMATCH_WWID_STR("WWIDNOMORE"));
+	mock_used_alias("MPATHa", "WWIDNOMORE");
 	expect_condlog(0, NOMORE_STR);
-	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+	rc = lookup_binding(NULL, "WWIDNOMORE", &alias, "MPATH", 1);
 	assert_int_equal(rc, -1);
 	assert_ptr_equal(alias, NULL);
 }
@@ -887,12 +1061,14 @@ static void lb_nomatch_int_max_m1(void **state)
 {
 	int rc;
 	char *alias;
+	STRBUF_ON_STACK(buf);
 
-	mock_bindings_file("MPATHb WWID1\n"
-			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n"
-			   "MPATHa WWID0\n", -1);
-	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
-	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
+	fill_bindings(&buf, 0, 26);
+	print_strbuf(&buf, "MPATH%s WWIDMAXM1\n", MPATH_ID_INT_MAX_m1);
+	mock_bindings_file(get_strbuf_str(&buf));
+	expect_condlog(3, NOMATCH_WWID_STR("WWIDMAX"));
+	mock_unused_alias("MPATH" MPATH_ID_INT_MAX);
+	rc = lookup_binding(NULL, "WWIDMAX", &alias, "MPATH", 1);
 	assert_int_equal(rc, INT_MAX);
 	assert_ptr_equal(alias, NULL);
 }
@@ -901,13 +1077,15 @@ static void lb_nomatch_int_max_m1_used(void **state)
 {
 	int rc;
 	char *alias;
+	STRBUF_ON_STACK(buf);
 
-	mock_bindings_file("MPATHb WWID1\n"
-			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n"
-			   "MPATHa WWID0\n", -1);
-	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWID2");
+	fill_bindings(&buf, 0, 26);
+	print_strbuf(&buf, "MPATH%s WWIDMAXM1\n", MPATH_ID_INT_MAX_m1);
+	mock_bindings_file(get_strbuf_str(&buf));
+	expect_condlog(3, NOMATCH_WWID_STR("WWIDMAX"));
+	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWIDMAX");
 	expect_condlog(0, NOMORE_STR);
-	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+	rc = lookup_binding(NULL, "WWIDMAX", &alias, "MPATH", 1);
 	assert_int_equal(rc, -1);
 	assert_ptr_equal(alias, NULL);
 }
@@ -916,13 +1094,15 @@ static void lb_nomatch_int_max_m1_1_used(void **state)
 {
 	int rc;
 	char *alias;
+	STRBUF_ON_STACK(buf);
 
-	mock_bindings_file("MPATHb WWID1\n"
-			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n", -1);
-	mock_used_alias("MPATHa", "WWID2");
+	fill_bindings(&buf, 1, 26);
+	print_strbuf(&buf, "MPATH%s WWIDMAXM1\n", MPATH_ID_INT_MAX_m1);
+	mock_bindings_file(get_strbuf_str(&buf));
+	expect_condlog(3, NOMATCH_WWID_STR("WWIDMAX"));
+	mock_used_alias("MPATHa", "WWIDMAX");
 	mock_unused_alias("MPATH" MPATH_ID_INT_MAX);
-	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
-	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+	rc = lookup_binding(NULL, "WWIDMAX", &alias, "MPATH", 1);
 	assert_int_equal(rc, INT_MAX);
 	assert_ptr_equal(alias, NULL);
 }
@@ -931,13 +1111,17 @@ static void lb_nomatch_int_max_m1_2_used(void **state)
 {
 	int rc;
 	char *alias;
+	STRBUF_ON_STACK(buf);
 
-	mock_bindings_file("MPATHb WWID1\n"
-			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n", -1);
-	mock_used_alias("MPATHa", "WWID2");
-	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWID2");
+	fill_bindings(&buf, 1, 26);
+	print_strbuf(&buf, "MPATH%s WWIDMAXM1\n", MPATH_ID_INT_MAX_m1);
+	mock_bindings_file(get_strbuf_str(&buf));
+
+	expect_condlog(3, NOMATCH_WWID_STR("WWIDMAX"));
+	mock_used_alias("MPATHa", "WWIDMAX");
+	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWIDMAX");
 	expect_condlog(0, NOMORE_STR);
-	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+	rc = lookup_binding(NULL, "WWIDMAX", &alias, "MPATH", 1);
 	assert_int_equal(rc, -1);
 	assert_ptr_equal(alias, NULL);
 }
@@ -946,52 +1130,68 @@ static void lb_nomatch_int_max_m1_2_used(void **state)
 static int test_lookup_binding(void)
 {
 	const struct CMUnitTest tests[] = {
-		cmocka_unit_test(lb_empty),
-		cmocka_unit_test(lb_empty_unused),
-		cmocka_unit_test(lb_empty_failed),
-		cmocka_unit_test(lb_empty_1_used),
-		cmocka_unit_test(lb_empty_1_used_self),
-		cmocka_unit_test(lb_match_a),
-		cmocka_unit_test(lb_nomatch_a),
-		cmocka_unit_test(lb_nomatch_a_bad_check),
-		cmocka_unit_test(lb_nomatch_a_unused),
-		cmocka_unit_test(lb_nomatch_a_3_used_failed_self),
-		cmocka_unit_test(lb_match_c),
-		cmocka_unit_test(lb_match_c_check),
-		cmocka_unit_test(lb_nomatch_a_c),
-		cmocka_unit_test(lb_nomatch_a_d_unused),
-		cmocka_unit_test(lb_nomatch_a_d_1_used),
-		cmocka_unit_test(lb_nomatch_a_d_2_used),
-		cmocka_unit_test(lb_nomatch_a_d_3_used),
-		cmocka_unit_test(lb_nomatch_c_a),
-		cmocka_unit_test(lb_nomatch_d_a_unused),
-		cmocka_unit_test(lb_nomatch_d_a_1_used),
-		cmocka_unit_test(lb_nomatch_a_b),
-		cmocka_unit_test(lb_nomatch_a_b_bad),
-		cmocka_unit_test(lb_nomatch_a_b_bad_self),
-		cmocka_unit_test(lb_nomatch_b_a),
-		cmocka_unit_test(lb_nomatch_b_a_3_used),
+		cmocka_unit_test_teardown(lb_empty, teardown_bindings),
+		cmocka_unit_test_teardown(lb_empty_unused, teardown_bindings),
+		cmocka_unit_test_teardown(lb_empty_failed, teardown_bindings),
+		cmocka_unit_test_teardown(lb_empty_1_used, teardown_bindings),
+		cmocka_unit_test_teardown(lb_empty_1_used_self, teardown_bindings),
+		cmocka_unit_test_teardown(lb_match_a, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_a, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_a_bad_check, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_a_unused, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_a_3_used_failed_self, teardown_bindings),
+		cmocka_unit_test_teardown(lb_match_c, teardown_bindings),
+		cmocka_unit_test_teardown(lb_match_c_check, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_a_c, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_a_d_unused, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_a_d_1_used, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_a_d_2_used, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_a_d_3_used, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_c_a, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_d_a_unused, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_d_a_1_used, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_a_b, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_a_b_bad, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_a_b_bad_self, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_b_z_a, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_b_aa_a, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_b_a_aa, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_b_a_aa_zz, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_b_z_a_unsorted, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_b_a, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_b_a_3_used, teardown_bindings),
 #ifdef MPATH_ID_INT_MAX
-		cmocka_unit_test(lb_nomatch_int_max),
-		cmocka_unit_test(lb_nomatch_int_max_check),
-		cmocka_unit_test(lb_nomatch_int_max_used),
-		cmocka_unit_test(lb_nomatch_int_max_m1),
-		cmocka_unit_test(lb_nomatch_int_max_m1_used),
-		cmocka_unit_test(lb_nomatch_int_max_m1_1_used),
-		cmocka_unit_test(lb_nomatch_int_max_m1_2_used),
+		cmocka_unit_test_teardown(lb_nomatch_int_max, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_int_max_used, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_int_max_m1, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_int_max_m1_used, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_int_max_m1_1_used, teardown_bindings),
+		cmocka_unit_test_teardown(lb_nomatch_int_max_m1_2_used, teardown_bindings),
 #endif
 	};
 
 	return cmocka_run_group_tests(tests, NULL, NULL);
 }
 
+static int rlookup_binding(FILE *dummy, char *buf, const char *alias) {
+
+	const struct binding *bdg;
+
+	bdg = get_binding_for_alias(&global_bindings, alias);
+	if (!bdg) {
+		return -1;
+	}
+	strlcpy(buf, bdg->wwid, WWID_SIZE);
+	return 0;
+}
+
 static void rl_empty(void **state)
 {
 	int rc;
 	char buf[WWID_SIZE];
 
 	buf[0] = '\0';
-	mock_bindings_file("", -1);
+	mock_bindings_file("");
 	expect_condlog(3, NOMATCH_STR("MPATHa"));
 	rc = rlookup_binding(NULL, buf, "MPATHa");
 	assert_int_equal(rc, -1);
@@ -1004,7 +1204,7 @@ static void rl_match_a(void **state)
 	char buf[WWID_SIZE];
 
 	buf[0] = '\0';
-	mock_bindings_file("MPATHa WWID0\n", 0);
+	mock_bindings_file("MPATHa WWID0\n");
 	expect_condlog(3, FOUND_ALIAS_STR("MPATHa", "WWID0"));
 	rc = rlookup_binding(NULL, buf, "MPATHa");
 	assert_int_equal(rc, 0);
@@ -1017,7 +1217,7 @@ static void rl_nomatch_a(void **state)
 	char buf[WWID_SIZE];
 
 	buf[0] = '\0';
-	mock_bindings_file("MPATHa WWID0\n", -1);
+	mock_bindings_file("MPATHa WWID0\n");
 	expect_condlog(3, NOMATCH_STR("MPATHb"));
 	rc = rlookup_binding(NULL, buf, "MPATHb");
 	assert_int_equal(rc, -1);
@@ -1030,8 +1230,8 @@ static void rl_malformed_a(void **state)
 	char buf[WWID_SIZE];
 
 	buf[0] = '\0';
-	mock_bindings_file("MPATHa     \n", -1);
-	expect_condlog(3, "Ignoring malformed line 1 in bindings file\n");
+	expect_condlog(1, "invalid line 1 in bindings file, missing WWID\n");
+	mock_bindings_file("MPATHa     \n");
 	expect_condlog(3, NOMATCH_STR("MPATHa"));
 	rc = rlookup_binding(NULL, buf, "MPATHa");
 	assert_int_equal(rc, -1);
@@ -1049,8 +1249,8 @@ static void rl_overlong_a(void **state)
 	snprintf(line + sizeof(line) - 2, 2, "\n");
 
 	buf[0] = '\0';
-	mock_bindings_file(line, -1);
 	expect_condlog(3, "Ignoring too large wwid at 1 in bindings file\n");
+	mock_bindings_file(line);
 	expect_condlog(3, NOMATCH_STR("MPATHa"));
 	rc = rlookup_binding(NULL, buf, "MPATHa");
 	assert_int_equal(rc, -1);
@@ -1065,7 +1265,7 @@ static void rl_match_b(void **state)
 	buf[0] = '\0';
 	mock_bindings_file("MPATHa WWID0\n"
 			   "MPATHz WWID26\n"
-			   "MPATHb WWID2\n", 2);
+			   "MPATHb WWID2\n");
 	expect_condlog(3, FOUND_ALIAS_STR("MPATHb", "WWID2"));
 	rc = rlookup_binding(NULL, buf, "MPATHb");
 	assert_int_equal(rc, 0);
@@ -1075,31 +1275,41 @@ static void rl_match_b(void **state)
 static int test_rlookup_binding(void)
 {
 	const struct CMUnitTest tests[] = {
-		cmocka_unit_test(rl_empty),
-		cmocka_unit_test(rl_match_a),
-		cmocka_unit_test(rl_nomatch_a),
-		cmocka_unit_test(rl_malformed_a),
-		cmocka_unit_test(rl_overlong_a),
-		cmocka_unit_test(rl_match_b),
+		cmocka_unit_test_teardown(rl_empty, teardown_bindings),
+		cmocka_unit_test_teardown(rl_match_a, teardown_bindings),
+		cmocka_unit_test_teardown(rl_nomatch_a, teardown_bindings),
+		cmocka_unit_test_teardown(rl_malformed_a, teardown_bindings),
+		cmocka_unit_test_teardown(rl_overlong_a, teardown_bindings),
+		cmocka_unit_test_teardown(rl_match_b, teardown_bindings),
 	};
 
 	return cmocka_run_group_tests(tests, NULL, NULL);
 }
 
+void check_bindings_size(int n)
+{
+	/* avoid -Waddress problem */
+	Bindings *bindings = &global_bindings;
+
+	assert_int_equal(VECTOR_SIZE(bindings), n);
+}
+
 static void al_a(void **state)
 {
 	static const char ln[] = "MPATHa WWIDa\n";
 	char *alias;
 
-	will_return(__wrap_lseek, 0);
-	expect_value(__wrap_write, count, strlen(ln));
-	expect_string(__wrap_write, buf, ln);
-	will_return(__wrap_write, strlen(ln));
+	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	will_return(__wrap_write, ln);
+	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	will_return(__wrap_rename, 0);
+	expect_condlog(1, "updated bindings file foo");
 	expect_condlog(3, NEW_STR("MPATHa", "WWIDa"));
 
-	alias = allocate_binding(0, "WWIDa", 1, "MPATH");
+	alias = allocate_binding("foo", "WWIDa", 1, "MPATH");
 	assert_ptr_not_equal(alias, NULL);
 	assert_string_equal(alias, "MPATHa");
+	check_bindings_size(1);
 	free(alias);
 }
 
@@ -1108,15 +1318,17 @@ static void al_zz(void **state)
 	static const char ln[] = "MPATHzz WWIDzz\n";
 	char *alias;
 
-	will_return(__wrap_lseek, 0);
-	expect_value(__wrap_write, count, strlen(ln));
-	expect_string(__wrap_write, buf, ln);
-	will_return(__wrap_write, strlen(ln));
+	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	will_return(__wrap_write, ln);
+	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	will_return(__wrap_rename, 0);
+	expect_condlog(1, "updated bindings file foo");
 	expect_condlog(3, NEW_STR("MPATHzz", "WWIDzz"));
 
-	alias = allocate_binding(0, "WWIDzz", 26*26 + 26, "MPATH");
+	alias = allocate_binding("foo", "WWIDzz", 26*26 + 26, "MPATH");
 	assert_ptr_not_equal(alias, NULL);
 	assert_string_equal(alias, "MPATHzz");
+	check_bindings_size(1);
 	free(alias);
 }
 
@@ -1127,6 +1339,7 @@ static void al_0(void **state)
 	expect_condlog(0, "allocate_binding: cannot allocate new binding for id 0\n");
 	alias = allocate_binding(0, "WWIDa", 0, "MPATH");
 	assert_ptr_equal(alias, NULL);
+	check_bindings_size(0);
 }
 
 static void al_m2(void **state)
@@ -1136,71 +1349,137 @@ static void al_m2(void **state)
 	expect_condlog(0, "allocate_binding: cannot allocate new binding for id -2\n");
 	alias = allocate_binding(0, "WWIDa", -2, "MPATH");
 	assert_ptr_equal(alias, NULL);
+	check_bindings_size(0);
 }
 
-static void al_lseek_err(void **state)
+static void al_write_partial(void **state)
 {
+	static const char ln[] = "MPATHa WWIDa\n";
 	char *alias;
 
-	will_return(__wrap_lseek, -ENODEV);
-	expect_condlog(0, "Cannot seek to end of bindings file : No such device\n");
-	alias = allocate_binding(0, "WWIDa", 1, "MPATH");
+	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	will_return(__wrap_write, ln);
+	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln) - 1);
+	expect_value(__wrap_write, count, 1);
+	will_return(__wrap_write, ln + sizeof(ln) - 2);
+	will_return(__wrap_write, 1);
+	will_return(__wrap_rename, 0);
+	expect_condlog(1, "updated bindings file foo");
+	expect_condlog(3, "Created new binding [MPATHa] for WWID [WWIDa]\n");
+
+	alias = allocate_binding("foo", "WWIDa", 1, "MPATH");
+	assert_ptr_not_equal(alias, NULL);
+	assert_string_equal(alias, "MPATHa");
+	check_bindings_size(1);
+	free(alias);
+}
+
+static void al_write_short(void **state)
+{
+	static const char ln[] = "MPATHa WWIDa\n";
+	char *alias;
+
+	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	will_return(__wrap_write, ln);
+	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln) - 1);
+	expect_value(__wrap_write, count, 1);
+	will_return(__wrap_write, ln + sizeof(ln) - 2);
+	will_return(__wrap_write, 0);
+	expect_condlog(2, "write_bindings_file: short write");
+	expect_condlog(1, "failed to write new bindings file");
+	expect_condlog(1, "allocate_binding: deleting binding MPATHa for WWIDa");
+
+	alias = allocate_binding("foo", "WWIDa", 1, "MPATH");
 	assert_ptr_equal(alias, NULL);
+	check_bindings_size(0);
 }
 
 static void al_write_err(void **state)
 {
 	static const char ln[] = "MPATHa WWIDa\n";
-	const int offset = 20;
 	char *alias;
 
-	will_return(__wrap_lseek, offset);
-	expect_value(__wrap_write, count, strlen(ln));
-	expect_string(__wrap_write, buf, ln);
-	will_return(__wrap_write, strlen(ln) - 1);
-	expect_value(__wrap_ftruncate, length, offset);
-	will_return(__wrap_ftruncate, 0);
-	expect_condlog(0, "Cannot write binding to bindings file :");
+	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	will_return(__wrap_write, ln);
+	will_return(__wrap_write, -EPERM);
+	expect_condlog(1, "failed to write new bindings file");
+	expect_condlog(1, "allocate_binding: deleting binding MPATHa for WWIDa");
 
-	alias = allocate_binding(0, "WWIDa", 1, "MPATH");
+	alias = allocate_binding("foo", "WWIDa", 1, "MPATH");
 	assert_ptr_equal(alias, NULL);
+	check_bindings_size(0);
+}
+
+static void al_rename_err(void **state)
+{
+	static const char ln[] = "MPATHa WWIDa\n";
+	char *alias;
+
+	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	will_return(__wrap_write, ln);
+	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	will_return(__wrap_rename, -EROFS);
+
+	expect_condlog(0, "update_bindings_file: rename: Read-only file system");
+	expect_condlog(1, "allocate_binding: deleting binding MPATHa for WWIDa");
+	alias = allocate_binding("foo", "WWIDa", 1, "MPATH");
+	assert_ptr_equal(alias, NULL);
+	check_bindings_size(0);
 }
 
 static int test_allocate_binding(void)
 {
 	const struct CMUnitTest tests[] = {
-		cmocka_unit_test(al_a),
-		cmocka_unit_test(al_zz),
-		cmocka_unit_test(al_0),
-		cmocka_unit_test(al_m2),
-		cmocka_unit_test(al_lseek_err),
-		cmocka_unit_test(al_write_err),
+		cmocka_unit_test_teardown(al_a, teardown_bindings),
+		cmocka_unit_test_teardown(al_zz, teardown_bindings),
+		cmocka_unit_test_teardown(al_0, teardown_bindings),
+		cmocka_unit_test_teardown(al_m2, teardown_bindings),
+		cmocka_unit_test_teardown(al_write_partial, teardown_bindings),
+		cmocka_unit_test_teardown(al_write_short, teardown_bindings),
+		cmocka_unit_test_teardown(al_write_err, teardown_bindings),
+		cmocka_unit_test_teardown(al_rename_err, teardown_bindings),
 	};
 
 	return cmocka_run_group_tests(tests, NULL, NULL);
 }
 
-#define mock_allocate_binding(alias, wwid)				\
+#define mock_allocate_binding_err_len(alias, wwid, len, err, msg)	\
 	do {								\
 		static const char ln[] = BINDING_STR(alias, wwid);	\
 									\
-		will_return(__wrap_lseek, 0);				\
-		expect_value(__wrap_write, count, strlen(ln));		\
-		expect_string(__wrap_write, buf, ln);			\
-		will_return(__wrap_write, strlen(ln));			\
-		expect_condlog(3, NEW_STR(alias, wwid));		\
+		expect_value(__wrap_write, count,			\
+			     strlen(BINDINGS_FILE_HEADER) + (len) + strlen(ln)); \
+		will_return(__wrap_write, ln);				\
+		will_return(__wrap_write,				\
+			    strlen(BINDINGS_FILE_HEADER) + (len) + strlen(ln)); \
+		will_return(__wrap_rename, err);			\
+		if (err == 0) {						\
+			expect_condlog(1, "updated bindings file x\n");	\
+			expect_condlog(3, NEW_STR(alias, wwid));	\
+		} else {						\
+			expect_condlog(0, "update_bindings_file: rename: " msg "\n"); \
+			expect_condlog(1, "allocate_binding: deleting binding "	\
+				       alias " for " wwid "\n");	\
+		}							\
 	} while (0)
 
+#define mock_allocate_binding_err(alias, wwid, err, msg)	\
+	mock_allocate_binding_err_len(alias, wwid, 0, err, msg)
+
+#define mock_allocate_binding(alias, wwid)			\
+	mock_allocate_binding_err(alias, wwid, 0, "")
+
+#define mock_allocate_binding_len(alias, wwid, len)			\
+	mock_allocate_binding_err_len(alias, wwid, len, 0, "")
+
 static void gufa_empty_new_rw(void **state) {
 	char *alias;
 
-	will_return(__wrap_open_file, true);
-
-	mock_bindings_file("", -1);
+	mock_bindings_file("");
 	mock_unused_alias("MPATHa");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
-
 	mock_allocate_binding("MPATHa", "WWID0");
+
 	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", false);
 	assert_string_equal(alias, "MPATHa");
 	free(alias);
@@ -1208,10 +1487,11 @@ static void gufa_empty_new_rw(void **state) {
 
 static void gufa_empty_new_ro_1(void **state) {
 	char *alias;
-	will_return(__wrap_open_file, false);
-	mock_bindings_file("", -1);
+
+	mock_bindings_file("");
 	mock_unused_alias("MPATHa");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+	mock_allocate_binding_err("MPATHa", "WWID0", -EROFS, "Read-only file system");
 
 	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", false);
 	assert_ptr_equal(alias, NULL);
@@ -1220,11 +1500,9 @@ static void gufa_empty_new_ro_1(void **state) {
 static void gufa_empty_new_ro_2(void **state) {
 	char *alias;
 
-	will_return(__wrap_open_file, true);
-
-	mock_bindings_file("", -1);
-	mock_unused_alias("MPATHa");
+	mock_bindings_file("");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+	mock_unused_alias("MPATHa");
 
 	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
 	assert_ptr_equal(alias, NULL);
@@ -1233,11 +1511,10 @@ static void gufa_empty_new_ro_2(void **state) {
 static void gufa_match_a_unused(void **state) {
 	char *alias;
 
-	will_return(__wrap_open_file, true);
-
-	mock_bindings_file("MPATHa WWID0", 0);
+	mock_bindings_file("MPATHa WWID0");
 	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
 	mock_unused_alias("MPATHa");
+	expect_condlog(3, EXISTING_STR("MPATHa", "WWID0"));
 
 	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
 	assert_string_equal(alias, "MPATHa");
@@ -1247,11 +1524,10 @@ static void gufa_match_a_unused(void **state) {
 static void gufa_match_a_self(void **state) {
 	char *alias;
 
-	will_return(__wrap_open_file, true);
-
-	mock_bindings_file("MPATHa WWID0", 0);
+	mock_bindings_file("MPATHa WWID0");
 	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
 	mock_self_alias("MPATHa", "WWID0");
+	expect_condlog(3, EXISTING_STR("MPATHa", "WWID0"));
 
 	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
 	assert_string_equal(alias, "MPATHa");
@@ -1261,9 +1537,8 @@ static void gufa_match_a_self(void **state) {
 static void gufa_match_a_used(void **state) {
 	char *alias;
 
-	will_return(__wrap_open_file, true);
 
-	mock_bindings_file("MPATHa WWID0", 0);
+	mock_bindings_file("MPATHa WWID0");
 	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
 	mock_used_alias("MPATHa", "WWID0");
 
@@ -1273,15 +1548,14 @@ static void gufa_match_a_used(void **state) {
 
 static void gufa_nomatch_a_c(void **state) {
 	char *alias;
-	will_return(__wrap_open_file, true);
+	static const char bindings[] = ("MPATHa WWID0\n"
+					"MPATHc WWID2\n");
 
-	mock_bindings_file("MPATHa WWID0\n"
-			   "MPATHc WWID2",
-			   -1);
+	mock_bindings_file(bindings);
 	mock_unused_alias("MPATHb");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
 
-	mock_allocate_binding("MPATHb", "WWID1");
+	mock_allocate_binding_len("MPATHb", "WWID1", strlen(bindings));
 
 	alias = get_user_friendly_alias("WWID1", "x", "", "MPATH", false);
 	assert_string_equal(alias, "MPATHb");
@@ -1290,15 +1564,14 @@ static void gufa_nomatch_a_c(void **state) {
 
 static void gufa_nomatch_c_a(void **state) {
 	char *alias;
-	will_return(__wrap_open_file, true);
+	const char bindings[] = ("MPATHc WWID2\n"
+				 "MPATHa WWID0\n");
 
-	mock_bindings_file("MPATHc WWID2\n"
-			   "MPATHa WWID0",
-			   -1);
+	mock_bindings_file(bindings);
 	mock_unused_alias("MPATHb");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
 
-	mock_allocate_binding("MPATHb", "WWID1");
+	mock_allocate_binding_len("MPATHb", "WWID1", sizeof(bindings) - 1);
 
 	alias = get_user_friendly_alias("WWID1", "x", "", "MPATH", false);
 	assert_string_equal(alias, "MPATHb");
@@ -1307,15 +1580,14 @@ static void gufa_nomatch_c_a(void **state) {
 
 static void gufa_nomatch_c_b(void **state) {
 	char *alias;
-	will_return(__wrap_open_file, true);
+	const char bindings[] = ("MPATHc WWID2\n"
+				 "MPATHb WWID1\n");
 
-	mock_bindings_file("MPATHc WWID2\n"
-			   "MPATHb WWID1\n",
-			   -1);
-	mock_unused_alias("MPATHa");
+	mock_bindings_file(bindings);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+	mock_unused_alias("MPATHa");
 
-	mock_allocate_binding("MPATHa", "WWID0");
+	mock_allocate_binding_len("MPATHa", "WWID0", sizeof(bindings) - 1);
 
 	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", false);
 	assert_string_equal(alias, "MPATHa");
@@ -1324,16 +1596,15 @@ static void gufa_nomatch_c_b(void **state) {
 
 static void gufa_nomatch_c_b_used(void **state) {
 	char *alias;
-	will_return(__wrap_open_file, true);
+	const char bindings[] = ("MPATHc WWID2\n"
+				 "MPATHb WWID1\n");
 
-	mock_bindings_file("MPATHc WWID2\n"
-			   "MPATHb WWID1",
-			   -1);
-	mock_used_alias("MPATHa", "WWID4");
+	mock_bindings_file(bindings);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID4"));
+	mock_used_alias("MPATHa", "WWID4");
 	mock_unused_alias("MPATHd");
 
-	mock_allocate_binding("MPATHd", "WWID4");
+	mock_allocate_binding_len("MPATHd", "WWID4", sizeof(bindings) - 1);
 
 	alias = get_user_friendly_alias("WWID4", "x", "", "MPATH", false);
 	assert_string_equal(alias, "MPATHd");
@@ -1342,34 +1613,60 @@ static void gufa_nomatch_c_b_used(void **state) {
 
 static void gufa_nomatch_b_f_a(void **state) {
 	char *alias;
-	will_return(__wrap_open_file, true);
+	const char bindings[] = ("MPATHb WWID1\n"
+				 "MPATHf WWID6\n"
+				 "MPATHa WWID0\n");
 
-	mock_bindings_file("MPATHb WWID1\n"
-			   "MPATHf WWID6\n"
-			   "MPATHa WWID0\n",
-			   -1);
+	mock_bindings_file_unsorted(bindings);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID7"));
 	mock_unused_alias("MPATHg");
 
-	mock_allocate_binding("MPATHg", "WWID7");
+	mock_allocate_binding_len("MPATHg", "WWID7", sizeof(bindings) - 1);
 
 	alias = get_user_friendly_alias("WWID7", "x", "", "MPATH", false);
 	assert_string_equal(alias, "MPATHg");
 	free(alias);
 }
 
+static void gufa_nomatch_b_aa_a(void **state) {
+	char *alias;
+	STRBUF_ON_STACK(buf);
+
+	fill_bindings(&buf, 0, 26);
+	mock_bindings_file(get_strbuf_str(&buf));
+	expect_condlog(3, NOMATCH_WWID_STR("WWID28"));
+	mock_unused_alias("MPATHab");
+	mock_allocate_binding_len("MPATHab", "WWID28", get_strbuf_len(&buf));
+
+	alias = get_user_friendly_alias("WWID28", "x", "", "MPATH", false);
+	assert_string_equal(alias, "MPATHab");
+	free(alias);
+}
+
+static void gufa_nomatch_b_f_a_sorted(void **state) {
+	char *alias;
+	const char bindings[] = ("MPATHb WWID1\n"
+				 "MPATHf WWID6\n"
+				 "MPATHa WWID0\n");
+
+	mock_bindings_file(bindings);
+	expect_condlog(3, NOMATCH_WWID_STR("WWID7"));
+	mock_unused_alias("MPATHc");
+
+	mock_allocate_binding_len("MPATHc", "WWID7", sizeof(bindings) - 1);
+
+	alias = get_user_friendly_alias("WWID7", "x", "", "MPATH", false);
+	assert_string_equal(alias, "MPATHc");
+	free(alias);
+}
+
 static void gufa_old_empty(void **state) {
 	char *alias;
-	will_return(__wrap_open_file, true);
 
 	/* rlookup_binding for ALIAS */
-	mock_bindings_file("", -1);
+	mock_bindings_file("");
 	expect_condlog(3, NOMATCH_STR("MPATHz"));
-
-	/* lookup_binding */
-	mock_bindings_file("", -1);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
-
 	mock_unused_alias("MPATHz");
 
 	mock_allocate_binding("MPATHz", "WWID0");
@@ -1382,12 +1679,11 @@ static void gufa_old_empty(void **state) {
 
 static void gufa_old_empty_self(void **state) {
 	char *alias;
-	will_return(__wrap_open_file, true);
 
-	mock_bindings_file("", -1);
+	mock_bindings_file("");
 	expect_condlog(3, NOMATCH_STR("MPATHz"));
 
-	mock_bindings_file("", -1);
+	mock_bindings_file("");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	mock_self_alias("MPATHz", "WWID0");
 
@@ -1401,15 +1697,14 @@ static void gufa_old_empty_self(void **state) {
 
 static void gufa_old_empty_used(void **state) {
 	char *alias;
-	will_return(__wrap_open_file, true);
 
-	mock_bindings_file("", -1);
+	mock_bindings_file("");
 	expect_condlog(3, NOMATCH_STR("MPATHz"));
 
-	mock_bindings_file("", -1);
-	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+	mock_bindings_file("");
 	mock_used_alias("MPATHz", "WWID0");
 	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 
 	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
 	assert_ptr_equal(alias, NULL);
@@ -1417,11 +1712,9 @@ static void gufa_old_empty_used(void **state) {
 
 static void gufa_old_match(void **state) {
 	char *alias;
-	will_return(__wrap_open_file, true);
 
 	mock_bindings_file("MPATHb WWID1\n"
-			   "MPATHz WWID0",
-			   1);
+			   "MPATHz WWID0");
 	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
 	mock_unused_alias("MPATHz");
 
@@ -1432,9 +1725,8 @@ static void gufa_old_match(void **state) {
 
 static void gufa_old_match_self(void **state) {
 	char *alias;
-	will_return(__wrap_open_file, true);
 
-	mock_bindings_file("MPATHz WWID0", 0);
+	mock_bindings_file("MPATHz WWID0");
 	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
 	mock_self_alias("MPATHz", "WWID0");
 
@@ -1445,9 +1737,8 @@ static void gufa_old_match_self(void **state) {
 
 static void gufa_old_match_used(void **state) {
 	char *alias;
-	will_return(__wrap_open_file, true);
 
-	mock_bindings_file("MPATHz WWID0", 0);
+	mock_bindings_file("MPATHz WWID0");
 	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
 	mock_used_alias("MPATHz", "WWID0");
 	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
@@ -1458,19 +1749,15 @@ static void gufa_old_match_used(void **state) {
 
 static void gufa_old_match_other(void **state) {
 	char *alias;
-	static const char bindings[] = "MPATHz WWID9";
+	static const char bindings[] = "MPATHz WWID9\n";
 
-	will_return(__wrap_open_file, true);
-
-	mock_bindings_file(bindings, 0);
+	mock_bindings_file(bindings);
 	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
 	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
-
-	mock_bindings_file(bindings, -1);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	mock_unused_alias("MPATHa");
 
-	mock_allocate_binding("MPATHa", "WWID0");
+	mock_allocate_binding_len("MPATHa", "WWID0", sizeof(bindings) - 1);
 
 	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
 	assert_string_equal(alias, "MPATHa");
@@ -1479,21 +1766,16 @@ static void gufa_old_match_other(void **state) {
 
 static void gufa_old_match_other_used(void **state) {
 	char *alias;
-	static const char bindings[] = "MPATHz WWID9";
+	static const char bindings[] = "MPATHz WWID9\n";
 
-	will_return(__wrap_open_file, true);
-
-	mock_bindings_file(bindings, 0);
+	mock_bindings_file(bindings);
 	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
 	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
-
-	mock_bindings_file(bindings, -1);
-	mock_used_alias("MPATHa", "WWID0");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+	mock_used_alias("MPATHa", "WWID0");
 	mock_unused_alias("MPATHb");
 
-	mock_allocate_binding("MPATHb", "WWID0");
-
+	mock_allocate_binding_len("MPATHb", "WWID0", sizeof(bindings) - 1);
 	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
 	assert_string_equal(alias, "MPATHb");
 	free(alias);
@@ -1503,15 +1785,13 @@ static void gufa_old_match_other_wwidmatch(void **state) {
 	char *alias;
 	static const char bindings[] = ("MPATHz WWID9\n"
 					"MPATHc WWID2");
-	will_return(__wrap_open_file, true);
 
-	mock_bindings_file(bindings, 0);
+	mock_bindings_file(bindings);
 	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
 	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
-
-	mock_bindings_file(bindings, 1);
 	expect_condlog(3, FOUND_STR("MPATHc", "WWID2"));
 	mock_unused_alias("MPATHc");
+	expect_condlog(3, EXISTING_STR("MPATHc", "WWID2"));
 
 	alias = get_user_friendly_alias("WWID2", "x", "MPATHz", "MPATH", false);
 	assert_string_equal(alias, "MPATHc");
@@ -1523,13 +1803,9 @@ static void gufa_old_match_other_wwidmatch_used(void **state) {
 	static const char bindings[] = ("MPATHz WWID9\n"
 					"MPATHc WWID2");
 
-	will_return(__wrap_open_file, true);
-
-	mock_bindings_file(bindings, 0);
+	mock_bindings_file(bindings);
 	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
 	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
-
-	mock_bindings_file(bindings, 1);
 	expect_condlog(3, FOUND_STR("MPATHc", "WWID2"));
 	mock_used_alias("MPATHc", "WWID2");
 
@@ -1541,12 +1817,9 @@ static void gufa_old_nomatch_wwidmatch(void **state) {
 	char *alias;
 	static const char bindings[] = "MPATHa WWID0";
 
-	will_return(__wrap_open_file, true);
-
-	mock_bindings_file(bindings, -1);
+	mock_bindings_file(bindings);
+	mock_unused_alias("MPATHz");
 	expect_condlog(3, NOMATCH_STR("MPATHz"));
-
-	mock_bindings_file(bindings, 0);
 	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
 	mock_unused_alias("MPATHa");
 	expect_condlog(3, EXISTING_STR("MPATHa", "WWID0"));
@@ -1559,12 +1832,10 @@ static void gufa_old_nomatch_wwidmatch(void **state) {
 static void gufa_old_nomatch_wwidmatch_used(void **state) {
 	char *alias;
 	static const char bindings[] = "MPATHa WWID0";
-	will_return(__wrap_open_file, true);
 
-	mock_bindings_file(bindings, -1);
+	mock_bindings_file(bindings);
+	mock_unused_alias("MPATHz");
 	expect_condlog(3, NOMATCH_STR("MPATHz"));
-
-	mock_bindings_file(bindings, 0);
 	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
 	mock_used_alias("MPATHa", "WWID0");
 
@@ -1574,19 +1845,14 @@ static void gufa_old_nomatch_wwidmatch_used(void **state) {
 
 static void gufa_old_nomatch_nowwidmatch(void **state) {
 	char *alias;
-	static const char bindings[] = "MPATHb WWID1";
+	static const char bindings[] = "MPATHb WWID1\n";
 
-	will_return(__wrap_open_file, true);
-
-	mock_bindings_file(bindings, -1);
+	mock_bindings_file(bindings);
 	expect_condlog(3, NOMATCH_STR("MPATHz"));
-
-	mock_bindings_file(bindings, -1);
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
-
 	mock_unused_alias("MPATHz");
 
-	mock_allocate_binding("MPATHz", "WWID0");
+	mock_allocate_binding_len("MPATHz", "WWID0", sizeof(bindings) - 1);
 	expect_condlog(2, ALLOC_STR("MPATHz", "WWID0"));
 
 	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
@@ -1598,16 +1864,12 @@ static void gufa_old_nomatch_nowwidmatch_used(void **state) {
 	char *alias;
 	static const char bindings[] = "MPATHb WWID1";
 
-	will_return(__wrap_open_file, true);
-
-	mock_bindings_file(bindings, -1);
+	mock_bindings_file(bindings);
 	expect_condlog(3, NOMATCH_STR("MPATHz"));
 
-	mock_bindings_file(bindings, -1);
-	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
-
 	mock_used_alias("MPATHz", "WWID0");
 	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
+	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 
 	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
 	assert_ptr_equal(alias, NULL);
@@ -1616,31 +1878,33 @@ static void gufa_old_nomatch_nowwidmatch_used(void **state) {
 static int test_get_user_friendly_alias()
 {
 	const struct CMUnitTest tests[] = {
-		cmocka_unit_test(gufa_empty_new_rw),
-		cmocka_unit_test(gufa_empty_new_ro_1),
-		cmocka_unit_test(gufa_empty_new_ro_2),
-		cmocka_unit_test(gufa_match_a_unused),
-		cmocka_unit_test(gufa_match_a_self),
-		cmocka_unit_test(gufa_match_a_used),
-		cmocka_unit_test(gufa_nomatch_a_c),
-		cmocka_unit_test(gufa_nomatch_c_a),
-		cmocka_unit_test(gufa_nomatch_c_b),
-		cmocka_unit_test(gufa_nomatch_c_b_used),
-		cmocka_unit_test(gufa_nomatch_b_f_a),
-		cmocka_unit_test(gufa_old_empty),
-		cmocka_unit_test(gufa_old_empty_self),
-		cmocka_unit_test(gufa_old_empty_used),
-		cmocka_unit_test(gufa_old_match),
-		cmocka_unit_test(gufa_old_match_self),
-		cmocka_unit_test(gufa_old_match_used),
-		cmocka_unit_test(gufa_old_match_other),
-		cmocka_unit_test(gufa_old_match_other_used),
-		cmocka_unit_test(gufa_old_match_other_wwidmatch),
-		cmocka_unit_test(gufa_old_match_other_wwidmatch_used),
-		cmocka_unit_test(gufa_old_nomatch_wwidmatch),
-		cmocka_unit_test(gufa_old_nomatch_wwidmatch_used),
-		cmocka_unit_test(gufa_old_nomatch_nowwidmatch),
-		cmocka_unit_test(gufa_old_nomatch_nowwidmatch_used),
+		cmocka_unit_test_teardown(gufa_empty_new_rw, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_empty_new_ro_1, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_empty_new_ro_2, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_match_a_unused, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_match_a_self, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_match_a_used, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_nomatch_a_c, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_nomatch_c_a, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_nomatch_c_b, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_nomatch_c_b_used, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_nomatch_b_f_a, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_nomatch_b_aa_a, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_nomatch_b_f_a_sorted, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_old_empty, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_old_empty_self, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_old_empty_used, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_old_match, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_old_match_self, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_old_match_used, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_old_match_other, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_old_match_other_used, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_old_match_other_wwidmatch, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_old_match_other_wwidmatch_used, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_old_nomatch_wwidmatch, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_old_nomatch_wwidmatch_used, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_old_nomatch_nowwidmatch, teardown_bindings),
+		cmocka_unit_test_teardown(gufa_old_nomatch_nowwidmatch_used, teardown_bindings),
 	};
 
 	return cmocka_run_group_tests(tests, NULL, NULL);
@@ -1656,7 +1920,6 @@ int main(void)
 	ret += test_lookup_binding();
 	ret += test_rlookup_binding();
 	ret += test_allocate_binding();
-	ret += test_allocate_binding();
 	ret += test_get_user_friendly_alias();
 
 	return ret;
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 20/21] libmultipath: dm_get_uuid(): return emtpy UUID for non-existing maps
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (18 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 19/21] multipath-tools tests: fix alias tests mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:47   ` Benjamin Marzinski
  2023-09-01 18:02 ` [dm-devel] [PATCH 21/21] libmultipath: adapt to new semantics of dm_get_uuid() mwilck
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

libdevmapper will most probably not return a UUID for non-existing
maps anyway. But it's cheap to double-check here.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/devmapper.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index 248c373..9be82f4 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -706,12 +706,16 @@ dm_get_prefixed_uuid(const char *name, char *uuid, int uuid_len)
 {
 	struct dm_task *dmt;
 	const char *uuidtmp;
+	struct dm_info info;
 	int r = 1;
 
 	dmt = libmp_dm_task_create(DM_DEVICE_INFO);
 	if (!dmt)
 		return 1;
 
+	if (uuid_len > 0)
+		uuid[0] = '\0';
+
 	if (!dm_task_set_name (dmt, name))
 		goto uuidout;
 
@@ -720,11 +724,13 @@ dm_get_prefixed_uuid(const char *name, char *uuid, int uuid_len)
 		goto uuidout;
 	}
 
+	if (!dm_task_get_info(dmt, &info) ||
+	    !info.exists)
+		goto uuidout;
+
 	uuidtmp = dm_task_get_uuid(dmt);
 	if (uuidtmp)
 		strlcpy(uuid, uuidtmp, uuid_len);
-	else
-		uuid[0] = '\0';
 
 	r = 0;
 uuidout:
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [dm-devel] [PATCH 21/21] libmultipath: adapt to new semantics of dm_get_uuid()
  2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
                   ` (19 preceding siblings ...)
  2023-09-01 18:02 ` [dm-devel] [PATCH 20/21] libmultipath: dm_get_uuid(): return emtpy UUID for non-existing maps mwilck
@ 2023-09-01 18:02 ` mwilck
  2023-09-06 22:47   ` Benjamin Marzinski
  20 siblings, 1 reply; 55+ messages in thread
From: mwilck @ 2023-09-01 18:02 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

dm_get_uuid() will return 1 for non-existing maps. Thus we don't need
to call dm_map_present() any more in alias_already_taken(). This changes
our semantics: previously we'd avoid using an alias for which dm_get_uuid()
had failed. Now we treat failure in dm_get_uuid() as indication that the
map doesn't exist. This is not dangerous because dm_task_get_uuid() cannot
fail, and thus the modified dm_get_uuid() will fail if and only if
dm_map_present() would return false.

This makes the "failed alias" test mostly obsolete, as "failed" is now
treated as "unused".

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/alias.c | 23 ++++++++++++-----------
 tests/alias.c        | 30 ++++++------------------------
 2 files changed, 18 insertions(+), 35 deletions(-)

diff --git a/libmultipath/alias.c b/libmultipath/alias.c
index 6003df0..0f5af17 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -295,18 +295,19 @@ scan_devname(const char *alias, const char *prefix)
 static bool alias_already_taken(const char *alias, const char *map_wwid)
 {
 
-	if (dm_map_present(alias)) {
-		char wwid[WWID_SIZE];
+	char wwid[WWID_SIZE];
 
-		/* If both the name and the wwid match, then it's fine.*/
-		if (dm_get_uuid(alias, wwid, sizeof(wwid)) == 0 &&
-		    strncmp(map_wwid, wwid, sizeof(wwid)) == 0)
-			return false;
-		condlog(3, "%s: alias '%s' already taken, reselecting alias",
-			map_wwid, alias);
-		return true;
-	}
-	return false;
+	/* If the map doesn't exist, it's fine */
+	if (dm_get_uuid(alias, wwid, sizeof(wwid)) != 0)
+		return false;
+
+	/* If both the name and the wwid match, it's fine.*/
+	if (strncmp(map_wwid, wwid, sizeof(wwid)) == 0)
+		return false;
+
+	condlog(3, "%s: alias '%s' already taken, reselecting alias",
+		map_wwid, alias);
+	return true;
 }
 
 static bool id_already_taken(int id, const char *prefix, const char *map_wwid)
diff --git a/tests/alias.c b/tests/alias.c
index 4ac6425..4f54031 100644
--- a/tests/alias.c
+++ b/tests/alias.c
@@ -73,12 +73,6 @@ int __wrap_mkstemp(char *template)
 	return 10;
 }
 
-int __wrap_dm_map_present(const char * str)
-{
-	check_expected(str);
-	return mock_type(int);
-}
-
 int __wrap_dm_get_uuid(const char *name, char *uuid, int uuid_len)
 {
 	int ret;
@@ -398,14 +392,13 @@ static int test_scan_devname(void)
 
 static void mock_unused_alias(const char *alias)
 {
-	expect_string(__wrap_dm_map_present, str, alias);
-	will_return(__wrap_dm_map_present, 0);
+	expect_string(__wrap_dm_get_uuid, name, alias);
+	expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);
+	will_return(__wrap_dm_get_uuid, 1);
 }
 
 static void mock_self_alias(const char *alias, const char *wwid)
 {
-	expect_string(__wrap_dm_map_present, str, alias);
-	will_return(__wrap_dm_map_present, 1);
 	expect_string(__wrap_dm_get_uuid, name, alias);
 	expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);
 	will_return(__wrap_dm_get_uuid, 0);
@@ -432,18 +425,13 @@ static void mock_self_alias(const char *alias, const char *wwid)
 
 #define mock_failed_alias(alias, wwid)					\
 	do {								\
-		expect_string(__wrap_dm_map_present, str, alias);	\
-		will_return(__wrap_dm_map_present, 1);			\
 		expect_string(__wrap_dm_get_uuid, name, alias);		\
 		expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);	\
 		will_return(__wrap_dm_get_uuid, 1);			\
-		expect_condlog(3, USED_STR(alias, wwid));		\
 	} while (0)
 
 #define mock_used_alias(alias, wwid)					\
 	do {								\
-		expect_string(__wrap_dm_map_present, str, alias);	\
-		will_return(__wrap_dm_map_present, 1);			\
 		expect_string(__wrap_dm_get_uuid, name, alias);		\
 		expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);	\
 		will_return(__wrap_dm_get_uuid, 0);			\
@@ -566,9 +554,8 @@ static void lb_empty_failed(void **state)
 	mock_bindings_file("");
 	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
 	mock_failed_alias("MPATHa", "WWID0");
-	mock_unused_alias("MPATHb");
 	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
-	assert_int_equal(rc, 2);
+	assert_int_equal(rc, 1);
 	assert_ptr_equal(alias, NULL);
 	free(alias);
 }
@@ -666,9 +653,8 @@ static void lb_nomatch_a_3_used_failed_self(void **state)
 	mock_used_alias("MPATHc", "WWID1");
 	mock_used_alias("MPATHd", "WWID1");
 	mock_failed_alias("MPATHe", "WWID1");
-	mock_self_alias("MPATHf", "WWID1");
 	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
-	assert_int_equal(rc, 6);
+	assert_int_equal(rc, 5);
 	assert_ptr_equal(alias, NULL);
 }
 
@@ -949,11 +935,7 @@ static void lb_nomatch_b_a_aa_zz(void **state)
 	 * lookup_binding finds MPATHaaa as next free entry, because MPATHaa is
 	 * found before MPATHb, and MPATHzz was in the bindings, too.
 	 */
-	for (i = 0; i <= 26; i++) {
-		print_strbuf(&buf,  "MPATH");
-		format_devname(&buf, i + 1);
-		print_strbuf(&buf,  " WWID%d\n", i);
-	}
+	fill_bindings(&buf, 0, 26);
 	print_strbuf(&buf, "MPATHzz WWID676\n");
 	mock_bindings_file(get_strbuf_str(&buf));
 	expect_condlog(3, NOMATCH_WWID_STR("WWID703"));
-- 
2.41.0

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 01/21] libmultipath: sysfs_set_scsi_tmo: do nothing for ACT_DRY_RUN
  2023-09-01 18:02 ` [dm-devel] [PATCH 01/21] libmultipath: sysfs_set_scsi_tmo: do nothing for ACT_DRY_RUN mwilck
@ 2023-09-06 22:42   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:42 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel, Jehan Singh

On Fri, Sep 01, 2023 at 08:02:14PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> "multipath -d" might change sysfs timeouts of SCSI devices.
> Make sure it doesn't.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> Cc: Jehan Singh <jehan.singh@suse.com>
> ---
>  libmultipath/configure.c | 4 ++--
>  libmultipath/discovery.c | 3 +++
>  2 files changed, 5 insertions(+), 2 deletions(-)
> 
> diff --git a/libmultipath/configure.c b/libmultipath/configure.c
> index 9513baa..029fbbd 100644
> --- a/libmultipath/configure.c
> +++ b/libmultipath/configure.c
> @@ -1193,13 +1193,13 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
>  
>  		if (cmpp)
>  			mpp->queue_mode = cmpp->queue_mode;
> +		if (cmd == CMD_DRY_RUN && mpp->action == ACT_UNDEF)
> +			mpp->action = ACT_DRY_RUN;
>  		if (setup_map(mpp, &params, vecs)) {
>  			remove_map(mpp, vecs->pathvec, NULL);
>  			continue;
>  		}
>  
> -		if (cmd == CMD_DRY_RUN)
> -			mpp->action = ACT_DRY_RUN;
>  		if (mpp->action == ACT_UNDEF)
>  			select_action(mpp, curmp,
>  				      force_reload == FORCE_RELOAD_YES ? 1 : 0);
> diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
> index e4de48e..84ce5fe 100644
> --- a/libmultipath/discovery.c
> +++ b/libmultipath/discovery.c
> @@ -857,6 +857,9 @@ sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp)
>  	bool warn_dev_loss = false;
>  	bool warn_fast_io_fail = false;
>  
> +	if (mpp->action == ACT_DRY_RUN || mpp->action == ACT_REJECT)
> +		return 0;
> +
>  	if (mpp->no_path_retry > 0) {
>  		uint64_t no_path_retry_tmo =
>  			(uint64_t)mpp->no_path_retry * conf->checkint;
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 02/21] libmultipath: add alias_already_taken()
  2023-09-01 18:02 ` [dm-devel] [PATCH 02/21] libmultipath: add alias_already_taken() mwilck
@ 2023-09-06 22:42   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:42 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel, David Bond

On Fri, Sep 01, 2023 at 08:02:15PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Factor out a trivial helper function.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> Cc: David Bond <dbond@suse.com>
> ---
>  libmultipath/alias.c | 34 ++++++++++++++++++++--------------
>  1 file changed, 20 insertions(+), 14 deletions(-)
> @@ -120,20 +137,9 @@ id_already_taken(int id, const char *prefix, const char *map_wwid)
>  		return 0;
>  
>  	alias = get_strbuf_str(&buf);
> -	if (dm_map_present(alias)) {
> -		char wwid[WWID_SIZE];
> -
> -		/* If both the name and the wwid match, then it's fine.*/
> -		if (dm_get_uuid(alias, wwid, sizeof(wwid)) == 0 &&
> -		    strncmp(map_wwid, wwid, sizeof(wwid)) == 0)

Possibly this should return "false" to match the bool return type.
Otherwise, it looks fine.

-Ben

> -			return 0;
> -		condlog(3, "%s: alias '%s' already taken, but not in bindings file. reselecting alias", map_wwid, alias);
> -		return 1;
> -	}
> -	return 0;
> +	return alias_already_taken(alias, map_wwid);
>  }
>  
> -
>  /*
>   * Returns: 0   if matching entry in WWIDs file found
>   *         -1   if an error occurs
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 03/21] libmultipath: unify use_existing_alias() and get_user_friendly_alias()
  2023-09-01 18:02 ` [dm-devel] [PATCH 03/21] libmultipath: unify use_existing_alias() and get_user_friendly_alias() mwilck
@ 2023-09-06 22:42   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:42 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel, David Bond

On Fri, Sep 01, 2023 at 08:02:16PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> These functions are only called from select_alias(). The logic
> is more obvious when unified in a single function.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Cc: David Bond <dbond@suse.com>
> ---
>  libmultipath/alias.c   | 82 ++++++++++++------------------------------
>  libmultipath/alias.h   |  9 ++---
>  libmultipath/propsel.c | 19 +++++-----
>  3 files changed, 34 insertions(+), 76 deletions(-)
> 
> diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> index abde08c..9b9b789 100644
> --- a/libmultipath/alias.c
> +++ b/libmultipath/alias.c
> @@ -329,13 +329,13 @@ allocate_binding(int fd, const char *wwid, int id, const char *prefix)
>  	return alias;
>  }
>  
> -char *
> -use_existing_alias (const char *wwid, const char *file, const char *alias_old,
> -		    const char *prefix, int bindings_read_only)
> +char *get_user_friendly_alias(const char *wwid, const char *file, const char *alias_old,
> +			      const char *prefix, bool bindings_read_only)
>  {
>  	char *alias = NULL;
>  	int id = 0;
>  	int fd, can_write;
> +	bool new_binding = false;
>  	char buff[WWID_SIZE];
>  	FILE *f;
>  
> @@ -349,6 +349,10 @@ use_existing_alias (const char *wwid, const char *file, const char *alias_old,
>  		close(fd);
>  		return NULL;
>  	}
> +
> +	if (!strlen(alias_old))
> +		goto new_alias;
> +
>  	/* lookup the binding. if it exists, the wwid will be in buff
>  	 * either way, id contains the id for the alias
>  	 */
> @@ -358,14 +362,14 @@ use_existing_alias (const char *wwid, const char *file, const char *alias_old,
>  		/* if buff is our wwid, it's already
>  		 * allocated correctly
>  		 */
> -		if (strcmp(buff, wwid) == 0)
> +		if (strcmp(buff, wwid) == 0) {
>  			alias = strdup(alias_old);
> -		else {
> -			alias = NULL;
> +			goto out;
> +		} else {
>  			condlog(0, "alias %s already bound to wwid %s, cannot reuse",
>  				alias_old, buff);
> +			goto new_alias;
>  		}
> -		goto out;
>  	}
>  
>  	id = lookup_binding(f, wwid, &alias, NULL, 0);
> @@ -377,8 +381,15 @@ use_existing_alias (const char *wwid, const char *file, const char *alias_old,
>  
>  	/* allocate the existing alias in the bindings file */
>  	id = scan_devname(alias_old, prefix);
> -	if (id <= 0)
> -		goto out;
> +
> +new_alias:
> +	if (id <= 0) {
> +		id = lookup_binding(f, wwid, &alias, prefix, 1);
> +		if (id <= 0)
> +			goto out;
> +		else
> +			new_binding = true;
> +	}
>  
>  	if (fflush(f) != 0) {
>  		condlog(0, "cannot fflush bindings file stream : %s",
> @@ -388,8 +399,9 @@ use_existing_alias (const char *wwid, const char *file, const char *alias_old,
>  
>  	if (can_write && !bindings_read_only) {
>  		alias = allocate_binding(fd, wwid, id, prefix);
> -		condlog(0, "Allocated existing binding [%s] for WWID [%s]",
> -			alias, wwid);
> +		if (alias && !new_binding)
> +			condlog(2, "Allocated existing binding [%s] for WWID [%s]",
> +				alias, wwid);
>  	}
>  
>  out:
> @@ -399,54 +411,6 @@ out:
>  	return alias;
>  }
>  
> -char *
> -get_user_friendly_alias(const char *wwid, const char *file, const char *prefix,
> -			int bindings_read_only)
> -{
> -	char *alias;
> -	int fd, id;
> -	FILE *f;
> -	int can_write;
> -
> -	if (!wwid || *wwid == '\0') {
> -		condlog(3, "Cannot find binding for empty WWID");
> -		return NULL;
> -	}
> -
> -	fd = open_file(file, &can_write, bindings_file_header);
> -	if (fd < 0)
> -		return NULL;
> -
> -	f = fdopen(fd, "r");
> -	if (!f) {
> -		condlog(0, "cannot fdopen on bindings file descriptor : %s",
> -			strerror(errno));
> -		close(fd);
> -		return NULL;
> -	}
> -
> -	id = lookup_binding(f, wwid, &alias, prefix, 1);
> -	if (id < 0) {
> -		fclose(f);
> -		return NULL;
> -	}
> -
> -	pthread_cleanup_push(free, alias);
> -
> -	if (fflush(f) != 0) {
> -		condlog(0, "cannot fflush bindings file stream : %s",
> -			strerror(errno));
> -		free(alias);
> -		alias = NULL;
> -	} else if (can_write && !bindings_read_only && !alias)
> -		alias = allocate_binding(fd, wwid, id, prefix);
> -
> -	fclose(f);
> -
> -	pthread_cleanup_pop(0);
> -	return alias;
> -}
> -
>  int
>  get_user_friendly_wwid(const char *alias, char *buff, const char *file)
>  {
> diff --git a/libmultipath/alias.h b/libmultipath/alias.h
> index dbc950c..fa33223 100644
> --- a/libmultipath/alias.h
> +++ b/libmultipath/alias.h
> @@ -2,13 +2,10 @@
>  #define _ALIAS_H
>  
>  int valid_alias(const char *alias);
> -char *get_user_friendly_alias(const char *wwid, const char *file,
> -			      const char *prefix,
> -			      int bindings_readonly);
>  int get_user_friendly_wwid(const char *alias, char *buff, const char *file);
> -char *use_existing_alias (const char *wwid, const char *file,
> -			  const char *alias_old,
> -			  const char *prefix, int bindings_read_only);
> +char *get_user_friendly_alias(const char *wwid, const char *file,
> +			      const char *alias_old,
> +			      const char *prefix, bool bindings_read_only);
>  
>  struct config;
>  int check_alias_settings(const struct config *);
> diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
> index d6bce12..354e883 100644
> --- a/libmultipath/propsel.c
> +++ b/libmultipath/propsel.c
> @@ -401,19 +401,16 @@ int select_alias(struct config *conf, struct multipath * mp)
>  
>  	select_alias_prefix(conf, mp);
>  
> -	if (strlen(mp->alias_old) > 0) {
> -		mp->alias = use_existing_alias(mp->wwid, conf->bindings_file,
> -				mp->alias_old, mp->alias_prefix,
> -				conf->bindings_read_only);
> -		memset (mp->alias_old, 0, WWID_SIZE);
> -		origin = "(setting: using existing alias)";
> -	}
> +	mp->alias = get_user_friendly_alias(mp->wwid, conf->bindings_file,
> +					    mp->alias_old, mp->alias_prefix,
> +					    conf->bindings_read_only);
>  
> -	if (mp->alias == NULL) {
> -		mp->alias = get_user_friendly_alias(mp->wwid,
> -				conf->bindings_file, mp->alias_prefix, conf->bindings_read_only);
> +	if (mp->alias && !strncmp(mp->alias, mp->alias_old, WWID_SIZE))
> +		origin = "(setting: using existing alias)";
> +	else if (mp->alias)
>  		origin = "(setting: user_friendly_name)";
> -	}
> +	memset (mp->alias_old, 0, WWID_SIZE);
> +
>  out:
>  	if (mp->alias == NULL) {
>  		mp->alias = strdup(mp->wwid);
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 04/21] libmultipath: never allocate an alias that's already taken
  2023-09-01 18:02 ` [dm-devel] [PATCH 04/21] libmultipath: never allocate an alias that's already taken mwilck
@ 2023-09-06 22:42   ` Benjamin Marzinski
  2023-09-07  7:24     ` Martin Wilck
  0 siblings, 1 reply; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:42 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel, David Bond

On Fri, Sep 01, 2023 at 08:02:17PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> If the bindings file is changed in a way that multipathd can't handle
> (e.g. by swapping the aliases of two maps), multipathd must not try
> to re-use an alias that is already used by another map. Creating
> or renaming a map with such an alias will fail. We already avoid
> this for some cases, but not for all. Fix it.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> Cc: David Bond <dbond@suse.com>
> ---
>  libmultipath/alias.c | 36 +++++++++++++++++++++++++++++++-----
>  tests/alias.c        |  2 +-
>  2 files changed, 32 insertions(+), 6 deletions(-)
> 
> diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> index 9b9b789..f7834d1 100644
> --- a/libmultipath/alias.c
> +++ b/libmultipath/alias.c
> @@ -120,7 +120,7 @@ static bool alias_already_taken(const char *alias, const char *map_wwid)
>  		if (dm_get_uuid(alias, wwid, sizeof(wwid)) == 0 &&
>  		    strncmp(map_wwid, wwid, sizeof(wwid)) == 0)
>  			return false;
> -		condlog(3, "%s: alias '%s' already taken, but not in bindings file. reselecting alias",
> +		condlog(3, "%s: alias '%s' already taken, reselecting alias",
>  			map_wwid, alias);
>  		return true;
>  	}
> @@ -363,28 +363,54 @@ char *get_user_friendly_alias(const char *wwid, const char *file, const char *al
>  		 * allocated correctly
>  		 */
>  		if (strcmp(buff, wwid) == 0) {

I'm confused about this. We should only have alias_old set if there
already exists a device that matches this WWID, right? That's what I
though alias_old means. Am I missing some way that alias_old could get
set to something other than the alias of an already existing device with
a matching WWID? Otherwise, once we verify that that this mapping also
exists in the bindings file, we should be fine to use it?

> -			alias = strdup(alias_old);
> +			if (!alias_already_taken(alias_old, wwid))
> +				alias = strdup(alias_old);
> +			else
> +				condlog(0, "ERROR: old alias [%s] for wwid [%s] is used by other map",
> +					alias_old, wwid);
>  			goto out;
> +
>  		} else {
>  			condlog(0, "alias %s already bound to wwid %s, cannot reuse",
>  				alias_old, buff);
> -			goto new_alias;

extra semicolon.

> +			goto new_alias;		     ;
>  		}
>  	}
>  
> +	/*
> +	 * Look for an existing alias in the bindings file.
> +	 * Pass prefix = NULL, so lookup_binding() won't try to allocate a new id.
> +	 */

There's no point in saving the return value here. We don't use if for
anything.

>  	id = lookup_binding(f, wwid, &alias, NULL, 0);
>  	if (alias) {
> -		condlog(3, "Use existing binding [%s] for WWID [%s]",
> -			alias, wwid);
> +		if (alias_already_taken(alias, wwid)) {
> +			free(alias);
> +			alias = NULL;
> +		} else
> +			condlog(3, "Use existing binding [%s] for WWID [%s]",
> +				alias, wwid);
>  		goto out;
>  	}
>  
>  	/* allocate the existing alias in the bindings file */
>  	id = scan_devname(alias_old, prefix);

Again, unless I'm overlooking something, I don't think we need to
check if the alias is already taken here. Since we know that a device
already exists with alias_old and the correct WWID, as long as alias_old
is a valid user_friendly_name we can just use it.

> +	if (id > 0 && id_already_taken(id, prefix, wwid)) {
> +		condlog(0, "ERROR: old alias [%s] for wwid [%s] is used by other map",
> +			alias_old, wwid);
> +		goto out;
> +	}
>  
>  new_alias:
>  	if (id <= 0) {
> +		/*
> +		 * no existing alias was provided, or allocating it
> +		 * failed. Try a new one.
> +		 */
>  		id = lookup_binding(f, wwid, &alias, prefix, 1);

If lookup_binding() was going to return (id == 0) it already would have
when we called it earlier in this function, so I don't think this check
is necessary either.

-Ben

> +		if (id == 0 && alias_already_taken(alias, wwid)) {
> +			free(alias);
> +			alias = NULL;
> +		}
>  		if (id <= 0)
>  			goto out;
>  		else
> diff --git a/tests/alias.c b/tests/alias.c
> index 3ca6c28..11f209e 100644
> --- a/tests/alias.c
> +++ b/tests/alias.c
> @@ -398,7 +398,7 @@ static void mock_self_alias(const char *alias, const char *wwid)
>  	will_return(__wrap_dm_get_uuid, wwid);
>  }
>  
> -#define USED_STR(alias_str, wwid_str) wwid_str ": alias '" alias_str "' already taken, but not in bindings file. reselecting alias\n"
> +#define USED_STR(alias_str, wwid_str) wwid_str ": alias '" alias_str "' already taken, reselecting alias\n"
>  
>  static void mock_failed_alias(const char *alias, char *msg)
>  {
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 05/21] libmultipath: lookup_binding: add comment about the algorithm
  2023-09-01 18:02 ` [dm-devel] [PATCH 05/21] libmultipath: lookup_binding: add comment about the algorithm mwilck
@ 2023-09-06 22:43   ` Benjamin Marzinski
  2023-09-07  8:22     ` Martin Wilck
  0 siblings, 1 reply; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:43 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:18PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> When I read this code, I always get confused. Adding comments to
> explain the algorithm.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/alias.c | 35 +++++++++++++++++++++++++++++++++++
>  1 file changed, 35 insertions(+)
> 
> diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> index f7834d1..e61eb91 100644
> --- a/libmultipath/alias.c
> +++ b/libmultipath/alias.c
> @@ -172,6 +172,41 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
>  		alias = strtok_r(buf, " \t", &saveptr);
>  		if (!alias) /* blank line */
>  			continue;
> +
> +		/*
> +		 * Find an unused index - explanation of the algorithm
> +		 *
> +		 * ID: 1 = mpatha, 2 = mpathb, ...
> +		 *
> +		 * We assume the bindings are unsorted. The only constraint
> +		 * is that no ID occurs more than once. IDs that occur in the
> +		 * bindings are called "used".
> +		 *
> +		 * We call the list 1,2,3,..., exactly in this order, the list
> +		 * of "expected" IDs. The variable "id" always holds the next
> +		 * "expected" ID, IOW the last "expected" ID encountered plus 1.
> +		 * Thus all IDs below "id" are known to be used. However, at the
> +		 * end of the loop, the value of "id" isn't necessarily unused.
> +		 *
> +		 * "smallest_bigger_id" is the smallest used ID that was
> +		 * encountered while it was larger than the next "expected" ID
> +		 * at that iteration. Let X be some used ID. If all IDs below X
> +		 * are used and encountered in the right sequence before X, "id"
> +		 * will be > X when the loop ends. Otherwise, X was encountered
> +		 * "out of order", the condition (X > id) holds when X is
> +		 * encountered, and "smallest_bigger_id" will be set to X; i.e.
> +		 * it will be less or equal than X when the loop ends.
> +		 *
> +		 * At the end of the loop, (id < smallest_bigger_id) means that
> +		 * the value of "id" had been encountered neither in order nor
> +		 * out of order, and is thus unused. (id >= smallest_bigger_id)

I know the check is (id >= smallest_bigger_id), but as long as no ID
occurs more than once, id can never actually be bigger than
smallest_bigger_id since id only gets incremented when (curr_id == id)
and if smallest_bigger_id is not INT_MAX, then smallest_bigger_id
already occured once in the file before id was incremented to equal it.
This means it can't occur again, so id can never get incremented past
it. Not this this really matters, so

Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>

> +		 * means that "id"'s value is in use. In this case, we play safe
> +		 * and use "biggest_id + 1" as the next value to try.
> +		 *
> +		 * biggest_id is always > smallest_bigger_id, except in the
> +		 * "perfectly ordered" case.
> +		 */
> +
>  		curr_id = scan_devname(alias, prefix);
>  		if (curr_id == id) {
>  			if (id < INT_MAX)
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 06/21] multipath-tools test: simplify debugging for condlog mismatch
  2023-09-01 18:02 ` [dm-devel] [PATCH 06/21] multipath-tools test: simplify debugging for condlog mismatch mwilck
@ 2023-09-06 22:43   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:43 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:19PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> If there's a mismatch between expected and actual log message,
> print both messages.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  tests/test-log.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/tests/test-log.c b/tests/test-log.c
> index c174587..6351699 100644
> --- a/tests/test-log.c
> +++ b/tests/test-log.c
> @@ -16,12 +16,14 @@ void __wrap_dlog (int prio, const char * fmt, ...)
>  	va_list ap;
>  	char *expected;
>  
> -	check_expected(prio);
>  	va_start(ap, fmt);
>  	vsnprintf(buff, MAX_MSG_SIZE, fmt, ap);
>  	va_end(ap);
>  	fprintf(stderr, "%s(%d): %s", __func__, prio, buff);
>  	expected = mock_ptr_type(char *);
> +	if (memcmp(expected, buff, strlen(expected)))
> +		fprintf(stderr, "%s(expected): %s", __func__, expected);
> +	check_expected(prio);
>  	assert_memory_equal(buff, expected, strlen(expected));
>  }
>  
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 07/21] multipath-tools tests: add tests for get_user_friendly_alias()
  2023-09-01 18:02 ` [dm-devel] [PATCH 07/21] multipath-tools tests: add tests for get_user_friendly_alias() mwilck
@ 2023-09-06 22:43   ` Benjamin Marzinski
  2023-09-07  7:55     ` Martin Wilck
  0 siblings, 1 reply; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:43 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:20PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  tests/alias.c | 531 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 531 insertions(+)
> 
> diff --git a/tests/alias.c b/tests/alias.c
> index 11f209e..e2372d1 100644
> --- a/tests/alias.c
> +++ b/tests/alias.c
> @@ -81,6 +81,35 @@ int __wrap_dm_get_uuid(const char *name, char *uuid, int uuid_len)
>  	return ret;
>  }
>  
> +#define TEST_FDNO 1234
> +#define TEST_FPTR ((FILE *) 0xaffe)
> +
> +int __wrap_open_file(const char *file, int *can_write, const char *header)
> +{
> +	int cw = mock_type(int);
> +
> +        *can_write = cw;
> +	return TEST_FDNO;
> +}
> +
> +FILE *__wrap_fdopen(int fd, const char *mode)
> +{
> +	assert_int_equal(fd, TEST_FDNO);
> +	return TEST_FPTR;
> +}
> +
> +int __wrap_fflush(FILE *f)
> +{
> +	assert_ptr_equal(f, TEST_FPTR);
> +	return 0;
> +}
> +
> +int __wrap_fclose(FILE *f)
> +{
> +	assert_ptr_equal(f, TEST_FPTR);
> +	return 0;
> +}
> +
>  /* strbuf wrapper for the old format_devname() */
>  static int __format_devname(char *name, int id, size_t len, const char *prefix)
>  {
> @@ -399,6 +428,22 @@ static void mock_self_alias(const char *alias, const char *wwid)
>  }
>  
>  #define USED_STR(alias_str, wwid_str) wwid_str ": alias '" alias_str "' already taken, reselecting alias\n"
> +#define NOMATCH_STR(alias_str) ("No matching alias [" alias_str "] in bindings file.\n")
> +#define FOUND_STR(alias_str, wwid_str)				\
> +	"Found matching wwid [" wwid_str "] in bindings file."	\
> +	" Setting alias to " alias_str "\n"
> +#define FOUND_ALIAS_STR(alias_str, wwid_str)				\
> +	"Found matching alias [" alias_str "] in bindings file."	\
> +	" Setting wwid to " wwid_str "\n"
> +#define NOMATCH_WWID_STR(wwid_str) ("No matching wwid [" wwid_str "] in bindings file.\n")
> +#define NEW_STR(alias_str, wwid_str) ("Created new binding [" alias_str "] for WWID [" wwid_str "]\n")
> +#define EXISTING_STR(alias_str, wwid_str) ("Use existing binding [" alias_str "] for WWID [" wwid_str "]\n")
> +#define ALLOC_STR(alias_str, wwid_str) ("Allocated existing binding [" alias_str "] for WWID [" wwid_str "]\n")
> +#define BINDING_STR(alias_str, wwid_str) (alias_str " " wwid_str "\n")
> +#define BOUND_STR(alias_str, wwid_str) ("alias "alias_str " already bound to wwid " wwid_str ", cannot reuse")
> +#define ERR_STR(alias_str, wwid_str) ("ERROR: old alias [" alias_str "] for wwid [" wwid_str "] is used by other map\n")
> +#define REUSE_STR(alias_str, wwid_str) ("alias " alias_str " already bound to wwid " wwid_str ", cannot reuse\n")
> +#define NOMORE_STR "no more available user_friendly_names\n"
>  
>  static void mock_failed_alias(const char *alias, char *msg)
>  {
> @@ -421,6 +466,24 @@ static void mock_used_alias(const char *alias, char *msg)
>  	expect_condlog(3, msg);
>  }
>  
> +static void mock_bindings_file(const char *content, int match_line)
> +{
> +	static char cnt[1024];
> +	char *token;
> +	int i;
> +
> +	assert_in_range(strlcpy(cnt, content, sizeof(cnt)), 0, sizeof(cnt) - 1);
> +
> +	for (token = strtok(cnt, "\n"), i = 0;
> +	     token && *token;
> +	     token = strtok(NULL, "\n"), i++) {
> +		will_return(__wrap_fgets, token);
> +		if (match_line == i)
> +			return;
> +	}
> +	will_return(__wrap_fgets, NULL);
> +}
> +
>  static void lb_empty(void **state)
>  {
>  	int rc;
> @@ -1147,6 +1210,472 @@ static int test_allocate_binding(void)
>  	return cmocka_run_group_tests(tests, NULL, NULL);
>  }
>  
> +#define mock_allocate_binding(alias, wwid)				\
> +	do {								\
> +		static const char ln[] = BINDING_STR(alias, wwid);	\
> +									\
> +		will_return(__wrap_lseek, 0);				\
> +		expect_value(__wrap_write, count, strlen(ln));		\
> +		expect_string(__wrap_write, buf, ln);			\
> +		will_return(__wrap_write, strlen(ln));			\
> +		expect_condlog(3, NEW_STR(alias, wwid));		\
> +	} while (0)
> +
> +static void gufa_empty_new_rw(void **state) {
> +	char *alias;
> +
> +	will_return(__wrap_open_file, true);
> +
> +	will_return(__wrap_fgets, NULL);
> +	mock_unused_alias("MPATHa");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +
> +	mock_allocate_binding("MPATHa", "WWID0");
> +	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", false);
> +	assert_string_equal(alias, "MPATHa");
> +	free(alias);
> +}
> +
> +static void gufa_empty_new_ro_1(void **state) {
> +	char *alias;
> +	will_return(__wrap_open_file, false);
> +	will_return(__wrap_fgets, NULL);
> +	mock_unused_alias("MPATHa");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", false);
> +	assert_ptr_equal(alias, NULL);
> +}
> +
> +static void gufa_empty_new_ro_2(void **state) {
> +	char *alias;
> +
> +	will_return(__wrap_open_file, true);
> +
> +	will_return(__wrap_fgets, NULL);
> +	mock_unused_alias("MPATHa");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
> +	assert_ptr_equal(alias, NULL);
> +}
> +
> +static void gufa_match_a_unused(void **state) {
> +	char *alias;
> +
> +	will_return(__wrap_open_file, true);
> +
> +	will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
> +	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
> +	mock_unused_alias("MPATHa");
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
> +	assert_string_equal(alias, "MPATHa");
> +	free(alias);
> +}
> +
> +static void gufa_match_a_self(void **state) {
> +	char *alias;
> +
> +	will_return(__wrap_open_file, true);
> +
> +	will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
> +	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
> +	mock_self_alias("MPATHa", "WWID0");
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
> +	assert_string_equal(alias, "MPATHa");
> +	free(alias);
> +}
> +
> +static void gufa_match_a_used(void **state) {
> +	char *alias;
> +
> +	will_return(__wrap_open_file, true);
> +
> +	will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
> +	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
> +	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
> +	assert_ptr_equal(alias, NULL);
> +}
> +
> +static void gufa_nomatch_a_c(void **state) {
> +	char *alias;
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file("MPATHa WWID0\n"
> +			   "MPATHc WWID2",
> +			   -1);
> +	mock_unused_alias("MPATHb");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
> +
> +	mock_allocate_binding("MPATHb", "WWID1");
> +
> +	alias = get_user_friendly_alias("WWID1", "x", "", "MPATH", false);
> +	assert_string_equal(alias, "MPATHb");
> +	free(alias);
> +}
> +
> +static void gufa_nomatch_c_a(void **state) {
> +	char *alias;
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file("MPATHc WWID2\n"
> +			   "MPATHa WWID0",
> +			   -1);
> +	mock_unused_alias("MPATHb");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
> +
> +	mock_allocate_binding("MPATHb", "WWID1");
> +
> +	alias = get_user_friendly_alias("WWID1", "x", "", "MPATH", false);
> +	assert_string_equal(alias, "MPATHb");
> +	free(alias);
> +}
> +
> +static void gufa_nomatch_c_b(void **state) {
> +	char *alias;
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file("MPATHc WWID2\n"
> +			   "MPATHb WWID1\n",
> +			   -1);
> +	mock_unused_alias("MPATHa");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +
> +	mock_allocate_binding("MPATHa", "WWID0");
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", false);
> +	assert_string_equal(alias, "MPATHa");
> +	free(alias);
> +}
> +
> +static void gufa_nomatch_c_b_used(void **state) {
> +	char *alias;
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file("MPATHc WWID2\n"
> +			   "MPATHb WWID1",
> +			   -1);
> +	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID4"));
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID4"));
> +	mock_unused_alias("MPATHd");
> +
> +	mock_allocate_binding("MPATHd", "WWID4");
> +
> +	alias = get_user_friendly_alias("WWID4", "x", "", "MPATH", false);
> +	assert_string_equal(alias, "MPATHd");
> +	free(alias);
> +}
> +
> +static void gufa_nomatch_b_f_a(void **state) {
> +	char *alias;
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file("MPATHb WWID1\n"
> +			   "MPATHf WWID6\n"
> +			   "MPATHa WWID0\n",
> +			   -1);
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID7"));
> +	mock_unused_alias("MPATHg");
> +
> +	mock_allocate_binding("MPATHg", "WWID7");
> +
> +	alias = get_user_friendly_alias("WWID7", "x", "", "MPATH", false);
> +	assert_string_equal(alias, "MPATHg");
> +	free(alias);
> +}
> +
> +static void gufa_old_empty(void **state) {
> +	char *alias;
> +	will_return(__wrap_open_file, true);
> +
> +	/* rlookup_binding for ALIAS */
> +	will_return(__wrap_fgets, NULL);
> +	expect_condlog(3, NOMATCH_STR("MPATHz"));
> +
> +	/* lookup_binding */
> +	will_return(__wrap_fgets, NULL);
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +
> +	mock_unused_alias("MPATHz");
> +
> +	mock_allocate_binding("MPATHz", "WWID0");
> +	expect_condlog(2, ALLOC_STR("MPATHz", "WWID0"));
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> +	assert_string_equal(alias, "MPATHz");
> +	free(alias);
> +}
> +
> +static void gufa_old_empty_self(void **state) {
> +	char *alias;
> +	will_return(__wrap_open_file, true);
> +
> +	will_return(__wrap_fgets, NULL);
> +	expect_condlog(3, NOMATCH_STR("MPATHz"));
> +
> +	will_return(__wrap_fgets, NULL);
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +	mock_self_alias("MPATHz", "WWID0");
> +
> +	mock_allocate_binding("MPATHz", "WWID0");
> +	expect_condlog(2, ALLOC_STR("MPATHz", "WWID0"));
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> +	assert_string_equal(alias, "MPATHz");
> +	free(alias);
> +}
> +

I'm not sure this is a valid test. Neither are gufa_old_match_used() and
gufa_old_nomatch_nowwidmatch_used(). Like I said, If
get_user_friendly_alias() is called with a non-NULL alias_old, I think
that means that there is current a device named alias_old with the
expected wwid.  So I don't think mock_used_alias() returning a
non-matching WWID is possible in real life. Again, if I'm wrong and it
is possible to have alias_old set without there being a dm device, then
this is a reasonable test.

-Ben

> +static void gufa_old_empty_used(void **state) {
> +	char *alias;
> +	will_return(__wrap_open_file, true);
> +
> +	will_return(__wrap_fgets, NULL);
> +	expect_condlog(3, NOMATCH_STR("MPATHz"));
> +
> +	will_return(__wrap_fgets, NULL);
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +	mock_used_alias("MPATHz", USED_STR("MPATHz", "WWID0"));
> +	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> +	assert_ptr_equal(alias, NULL);
> +}
> +
> +static void gufa_old_match(void **state) {
> +	char *alias;
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file("MPATHb WWID1\n"
> +			   "MPATHz WWID0",
> +			   1);
> +	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
> +	mock_unused_alias("MPATHz");
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> +	assert_string_equal(alias, "MPATHz");
> +	free(alias);
> +}
> +
> +static void gufa_old_match_self(void **state) {
> +	char *alias;
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file("MPATHz WWID0", 0);
> +	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
> +	mock_self_alias("MPATHz", "WWID0");
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> +	assert_string_equal(alias, "MPATHz");
> +	free(alias);
> +}
> +
> +static void gufa_old_match_used(void **state) {
> +	char *alias;
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file("MPATHz WWID0", 0);
> +	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
> +	mock_used_alias("MPATHz", USED_STR("MPATHz", "WWID0"));
> +	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> +	assert_ptr_equal(alias, NULL);
> +}
> +
> +static void gufa_old_match_other(void **state) {
> +	char *alias;
> +	static const char bindings[] = "MPATHz WWID9";
> +
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file(bindings, 0);
> +	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
> +	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
> +
> +	mock_bindings_file(bindings, -1);
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +	mock_unused_alias("MPATHa");
> +
> +	mock_allocate_binding("MPATHa", "WWID0");
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> +	assert_string_equal(alias, "MPATHa");
> +	free(alias);
> +}
> +
> +static void gufa_old_match_other_used(void **state) {
> +	char *alias;
> +	static const char bindings[] = "MPATHz WWID9";
> +
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file(bindings, 0);
> +	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
> +	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
> +
> +	mock_bindings_file(bindings, -1);
> +	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +	mock_unused_alias("MPATHb");
> +
> +	mock_allocate_binding("MPATHb", "WWID0");
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> +	assert_string_equal(alias, "MPATHb");
> +	free(alias);
> +}
> +
> +static void gufa_old_match_other_wwidmatch(void **state) {
> +	char *alias;
> +	static const char bindings[] = ("MPATHz WWID9\n"
> +					"MPATHc WWID2");
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file(bindings, 0);
> +	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
> +	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
> +
> +	mock_bindings_file(bindings, 1);
> +	expect_condlog(3, FOUND_STR("MPATHc", "WWID2"));
> +	mock_unused_alias("MPATHc");
> +
> +	alias = get_user_friendly_alias("WWID2", "x", "MPATHz", "MPATH", false);
> +	assert_string_equal(alias, "MPATHc");
> +	free(alias);
> +}
> +
> +static void gufa_old_match_other_wwidmatch_used(void **state) {
> +	char *alias;
> +	static const char bindings[] = ("MPATHz WWID9\n"
> +					"MPATHc WWID2");
> +
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file(bindings, 0);
> +	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
> +	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
> +
> +	mock_bindings_file(bindings, 1);
> +	expect_condlog(3, FOUND_STR("MPATHc", "WWID2"));
> +	mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID2"));
> +
> +	alias = get_user_friendly_alias("WWID2", "x", "MPATHz", "MPATH", false);
> +	assert_ptr_equal(alias, NULL);
> +}
> +
> +static void gufa_old_nomatch_wwidmatch(void **state) {
> +	char *alias;
> +	static const char bindings[] = "MPATHa WWID0";
> +
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file(bindings, -1);
> +	expect_condlog(3, NOMATCH_STR("MPATHz"));
> +
> +	mock_bindings_file(bindings, 0);
> +	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
> +	mock_unused_alias("MPATHa");
> +	expect_condlog(3, EXISTING_STR("MPATHa", "WWID0"));
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> +	assert_string_equal(alias, "MPATHa");
> +	free(alias);
> +}
> +
> +static void gufa_old_nomatch_wwidmatch_used(void **state) {
> +	char *alias;
> +	static const char bindings[] = "MPATHa WWID0";
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file(bindings, -1);
> +	expect_condlog(3, NOMATCH_STR("MPATHz"));
> +
> +	mock_bindings_file(bindings, 0);
> +	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
> +	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> +	assert_ptr_equal(alias, NULL);
> +}
> +
> +static void gufa_old_nomatch_nowwidmatch(void **state) {
> +	char *alias;
> +	static const char bindings[] = "MPATHb WWID1";
> +
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file(bindings, -1);
> +	expect_condlog(3, NOMATCH_STR("MPATHz"));
> +
> +	mock_bindings_file(bindings, -1);
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +
> +	mock_unused_alias("MPATHz");
> +
> +	mock_allocate_binding("MPATHz", "WWID0");
> +	expect_condlog(2, ALLOC_STR("MPATHz", "WWID0"));
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> +	assert_string_equal(alias, "MPATHz");
> +	free(alias);
> +}
> +
> +static void gufa_old_nomatch_nowwidmatch_used(void **state) {
> +	char *alias;
> +	static const char bindings[] = "MPATHb WWID1";
> +
> +	will_return(__wrap_open_file, true);
> +
> +	mock_bindings_file(bindings, -1);
> +	expect_condlog(3, NOMATCH_STR("MPATHz"));
> +
> +	mock_bindings_file(bindings, -1);
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +
> +	mock_used_alias("MPATHz", USED_STR("MPATHz", "WWID0"));
> +	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
> +
> +	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> +	assert_ptr_equal(alias, NULL);
> +}
> +
> +static int test_get_user_friendly_alias()
> +{
> +	const struct CMUnitTest tests[] = {
> +		cmocka_unit_test(gufa_empty_new_rw),
> +		cmocka_unit_test(gufa_empty_new_ro_1),
> +		cmocka_unit_test(gufa_empty_new_ro_2),
> +		cmocka_unit_test(gufa_match_a_unused),
> +		cmocka_unit_test(gufa_match_a_self),
> +		cmocka_unit_test(gufa_match_a_used),
> +		cmocka_unit_test(gufa_nomatch_a_c),
> +		cmocka_unit_test(gufa_nomatch_c_a),
> +		cmocka_unit_test(gufa_nomatch_c_b),
> +		cmocka_unit_test(gufa_nomatch_c_b_used),
> +		cmocka_unit_test(gufa_nomatch_b_f_a),
> +		cmocka_unit_test(gufa_old_empty),
> +		cmocka_unit_test(gufa_old_empty_self),
> +		cmocka_unit_test(gufa_old_empty_used),
> +		cmocka_unit_test(gufa_old_match),
> +		cmocka_unit_test(gufa_old_match_self),
> +		cmocka_unit_test(gufa_old_match_used),
> +		cmocka_unit_test(gufa_old_match_other),
> +		cmocka_unit_test(gufa_old_match_other_used),
> +		cmocka_unit_test(gufa_old_match_other_wwidmatch),
> +		cmocka_unit_test(gufa_old_match_other_wwidmatch_used),
> +		cmocka_unit_test(gufa_old_nomatch_wwidmatch),
> +		cmocka_unit_test(gufa_old_nomatch_wwidmatch_used),
> +		cmocka_unit_test(gufa_old_nomatch_nowwidmatch),
> +		cmocka_unit_test(gufa_old_nomatch_nowwidmatch_used),
> +	};
> +
> +	return cmocka_run_group_tests(tests, NULL, NULL);
> +}
> +
>  int main(void)
>  {
>  	int ret = 0;
> @@ -1157,6 +1686,8 @@ int main(void)
>  	ret += test_lookup_binding();
>  	ret += test_rlookup_binding();
>  	ret += test_allocate_binding();
> +	ret += test_allocate_binding();
> +	ret += test_get_user_friendly_alias();
>  
>  	return ret;
>  }
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 08/21] multipath-tools test: consistent use of macros in alias test
  2023-09-01 18:02 ` [dm-devel] [PATCH 08/21] multipath-tools test: consistent use of macros in alias test mwilck
@ 2023-09-06 22:43   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:43 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:21PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Used the macros introduced with the tests for get_user_friendly_alias()
> also in the previously existing tests.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  tests/alias.c | 80 ++++++++++++++++++++++++---------------------------
>  1 file changed, 38 insertions(+), 42 deletions(-)
> 
> diff --git a/tests/alias.c b/tests/alias.c
> index e2372d1..1c9903c 100644
> --- a/tests/alias.c
> +++ b/tests/alias.c
> @@ -490,7 +490,7 @@ static void lb_empty(void **state)
>  	char *alias;
>  
>  	will_return(__wrap_fgets, NULL);
> -	expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, NULL, 0);
>  	assert_int_equal(rc, 1);
>  	assert_ptr_equal(alias, NULL);
> @@ -503,7 +503,7 @@ static void lb_empty_unused(void **state)
>  
>  	will_return(__wrap_fgets, NULL);
>  	mock_unused_alias("MPATHa");
> -	expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 1);
>  	assert_ptr_equal(alias, NULL);
> @@ -518,7 +518,7 @@ static void lb_empty_failed(void **state)
>  	will_return(__wrap_fgets, NULL);
>  	mock_failed_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
>  	mock_unused_alias("MPATHb");
> -	expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
> @@ -533,7 +533,7 @@ static void lb_empty_1_used(void **state)
>  	will_return(__wrap_fgets, NULL);
>  	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
>  	mock_unused_alias("MPATHb");
> -	expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
> @@ -548,7 +548,7 @@ static void lb_empty_1_used_self(void **state)
>  	will_return(__wrap_fgets, NULL);
>  	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
>  	mock_self_alias("MPATHb", "WWID0");
> -	expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
> @@ -561,8 +561,7 @@ static void lb_match_a(void **state)
>  	char *alias;
>  
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	expect_condlog(3, "Found matching wwid [WWID0] in bindings file."
> -		       " Setting alias to MPATHa\n");
> +	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 0);
>  	assert_int_equal(rc, 0);
>  	assert_ptr_not_equal(alias, NULL);
> @@ -577,7 +576,7 @@ static void lb_nomatch_a(void **state)
>  
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, NULL);
> -	expect_condlog(3, "No matching wwid [WWID1] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
>  	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 0);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
> @@ -590,7 +589,7 @@ static void lb_nomatch_a_bad_check(void **state)
>  
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, NULL);
> -	expect_condlog(0, "no more available user_friendly_names\n");
> +	expect_condlog(0, NOMORE_STR);
>  	rc = lookup_binding(NULL, "WWID1", &alias, NULL, 1);
>  	assert_int_equal(rc, -1);
>  	assert_ptr_equal(alias, NULL);
> @@ -604,7 +603,7 @@ static void lb_nomatch_a_unused(void **state)
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, NULL);
>  	mock_unused_alias("MPATHb");
> -	expect_condlog(3, "No matching wwid [WWID1] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
>  	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
> @@ -622,7 +621,7 @@ static void lb_nomatch_a_3_used_failed_self(void **state)
>  	mock_used_alias("MPATHd", USED_STR("MPATHd", "WWID1"));
>  	mock_failed_alias("MPATHe", USED_STR("MPATHe", "WWID1"));
>  	mock_self_alias("MPATHf", "WWID1");
> -	expect_condlog(3, "No matching wwid [WWID1] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
>  	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 6);
>  	assert_ptr_equal(alias, NULL);
> @@ -635,8 +634,7 @@ static void do_lb_match_c(void **state, int check_if_taken)
>  
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, "MPATHc WWID1\n");
> -	expect_condlog(3, "Found matching wwid [WWID1] in bindings file."
> -		       " Setting alias to MPATHc\n");
> +	expect_condlog(3, FOUND_STR("MPATHc", "WWID1"));
>  	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", check_if_taken);
>  	assert_int_equal(rc, 0);
>  	assert_ptr_not_equal(alias, NULL);
> @@ -662,7 +660,7 @@ static void lb_nomatch_a_c(void **state)
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, "MPATHc WWID1\n");
>  	will_return(__wrap_fgets, NULL);
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
> @@ -677,7 +675,7 @@ static void lb_nomatch_a_d_unused(void **state)
>  	will_return(__wrap_fgets, "MPATHd WWID1\n");
>  	will_return(__wrap_fgets, NULL);
>  	mock_unused_alias("MPATHb");
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
> @@ -693,7 +691,7 @@ static void lb_nomatch_a_d_1_used(void **state)
>  	will_return(__wrap_fgets, NULL);
>  	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
>  	mock_unused_alias("MPATHc");
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 3);
>  	assert_ptr_equal(alias, NULL);
> @@ -710,7 +708,7 @@ static void lb_nomatch_a_d_2_used(void **state)
>  	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
>  	mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID2"));
>  	mock_unused_alias("MPATHe");
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 5);
>  	assert_ptr_equal(alias, NULL);
> @@ -728,7 +726,7 @@ static void lb_nomatch_a_d_3_used(void **state)
>  	mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID2"));
>  	mock_used_alias("MPATHe", USED_STR("MPATHe", "WWID2"));
>  	mock_unused_alias("MPATHf");
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 6);
>  	assert_ptr_equal(alias, NULL);
> @@ -742,7 +740,7 @@ static void lb_nomatch_c_a(void **state)
>  	will_return(__wrap_fgets, "MPATHc WWID1\n");
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, NULL);
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
> @@ -758,7 +756,7 @@ static void lb_nomatch_d_a_unused(void **state)
>  	will_return(__wrap_fgets, "MPATHd WWID0\n");
>  	will_return(__wrap_fgets, NULL);
>  	mock_unused_alias("MPATHb");
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
> @@ -775,7 +773,7 @@ static void lb_nomatch_d_a_1_used(void **state)
>  	will_return(__wrap_fgets, NULL);
>  	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
>  	mock_unused_alias("MPATHe");
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 5);
>  	assert_ptr_equal(alias, NULL);
> @@ -790,7 +788,7 @@ static void lb_nomatch_a_b(void **state)
>  	will_return(__wrap_fgets, "MPATHz WWID26\n");
>  	will_return(__wrap_fgets, "MPATHb WWID1\n");
>  	will_return(__wrap_fgets, NULL);
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
>  	assert_int_equal(rc, 3);
>  	assert_ptr_equal(alias, NULL);
> @@ -806,7 +804,7 @@ static void lb_nomatch_a_b_bad(void **state)
>  	will_return(__wrap_fgets, "MPATHb\n");
>  	will_return(__wrap_fgets, NULL);
>  	expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
>  	assert_int_equal(rc, 3);
>  	assert_ptr_equal(alias, NULL);
> @@ -823,7 +821,7 @@ static void lb_nomatch_a_b_bad_self(void **state)
>  	will_return(__wrap_fgets, NULL);
>  	expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
>  	mock_self_alias("MPATHc", "WWID2");
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 3);
>  	assert_ptr_equal(alias, NULL);
> @@ -838,7 +836,7 @@ static void lb_nomatch_b_a(void **state)
>  	will_return(__wrap_fgets, "MPATHz WWID26\n");
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, NULL);
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
>  	assert_int_equal(rc, 27);
>  	assert_ptr_equal(alias, NULL);
> @@ -857,7 +855,7 @@ static void lb_nomatch_b_a_3_used(void **state)
>  	mock_used_alias("MPATHab", USED_STR("MPATHab", "WWID2"));
>  	mock_used_alias("MPATHac", USED_STR("MPATHac", "WWID2"));
>  	mock_unused_alias("MPATHad");
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 30);
>  	assert_ptr_equal(alias, NULL);
> @@ -873,7 +871,7 @@ static void do_lb_nomatch_int_max(void **state, int check_if_taken)
>  	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n");
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, NULL);
> -	expect_condlog(0, "no more available user_friendly_names\n");
> +	expect_condlog(0, NOMORE_STR);
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", check_if_taken);
>  	assert_int_equal(rc, -1);
>  	assert_ptr_equal(alias, NULL);
> @@ -898,7 +896,7 @@ static void lb_nomatch_int_max_used(void **state)
>  	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n");
>  	will_return(__wrap_fgets, NULL);
>  	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
> -	expect_condlog(0, "no more available user_friendly_names\n");
> +	expect_condlog(0, NOMORE_STR);
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, -1);
>  	assert_ptr_equal(alias, NULL);
> @@ -913,7 +911,7 @@ static void lb_nomatch_int_max_m1(void **state)
>  	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, NULL);
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
>  	assert_int_equal(rc, INT_MAX);
>  	assert_ptr_equal(alias, NULL);
> @@ -929,7 +927,7 @@ static void lb_nomatch_int_max_m1_used(void **state)
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, NULL);
>  	mock_used_alias("MPATH" MPATH_ID_INT_MAX, USED_STR("MPATH" MPATH_ID_INT_MAX, "WWID2"));
> -	expect_condlog(0, "no more available user_friendly_names\n");
> +	expect_condlog(0, NOMORE_STR);
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, -1);
>  	assert_ptr_equal(alias, NULL);
> @@ -945,7 +943,7 @@ static void lb_nomatch_int_max_m1_1_used(void **state)
>  	will_return(__wrap_fgets, NULL);
>  	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
>  	mock_unused_alias("MPATH" MPATH_ID_INT_MAX);
> -	expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, INT_MAX);
>  	assert_ptr_equal(alias, NULL);
> @@ -961,7 +959,7 @@ static void lb_nomatch_int_max_m1_2_used(void **state)
>  	will_return(__wrap_fgets, NULL);
>  	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
>  	mock_used_alias("MPATH" MPATH_ID_INT_MAX, USED_STR("MPATH" MPATH_ID_INT_MAX, "WWID2"));
> -	expect_condlog(0, "no more available user_friendly_names\n");
> +	expect_condlog(0, NOMORE_STR);
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, -1);
>  	assert_ptr_equal(alias, NULL);
> @@ -1017,7 +1015,7 @@ static void rl_empty(void **state)
>  
>  	buf[0] = '\0';
>  	will_return(__wrap_fgets, NULL);
> -	expect_condlog(3, "No matching alias [MPATHa] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_STR("MPATHa"));
>  	rc = rlookup_binding(NULL, buf, "MPATHa");
>  	assert_int_equal(rc, -1);
>  	assert_string_equal(buf, "");
> @@ -1030,8 +1028,7 @@ static void rl_match_a(void **state)
>  
>  	buf[0] = '\0';
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	expect_condlog(3, "Found matching alias [MPATHa] in bindings file. "
> -		       "Setting wwid to WWID0\n");
> +	expect_condlog(3, FOUND_ALIAS_STR("MPATHa", "WWID0"));
>  	rc = rlookup_binding(NULL, buf, "MPATHa");
>  	assert_int_equal(rc, 0);
>  	assert_string_equal(buf, "WWID0");
> @@ -1045,7 +1042,7 @@ static void rl_nomatch_a(void **state)
>  	buf[0] = '\0';
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, NULL);
> -	expect_condlog(3, "No matching alias [MPATHb] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_STR("MPATHb"));
>  	rc = rlookup_binding(NULL, buf, "MPATHb");
>  	assert_int_equal(rc, -1);
>  	assert_string_equal(buf, "");
> @@ -1060,7 +1057,7 @@ static void rl_malformed_a(void **state)
>  	will_return(__wrap_fgets, "MPATHa     \n");
>  	will_return(__wrap_fgets, NULL);
>  	expect_condlog(3, "Ignoring malformed line 1 in bindings file\n");
> -	expect_condlog(3, "No matching alias [MPATHa] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_STR("MPATHa"));
>  	rc = rlookup_binding(NULL, buf, "MPATHa");
>  	assert_int_equal(rc, -1);
>  	assert_string_equal(buf, "");
> @@ -1080,7 +1077,7 @@ static void rl_overlong_a(void **state)
>  	will_return(__wrap_fgets, line);
>  	will_return(__wrap_fgets, NULL);
>  	expect_condlog(3, "Ignoring too large wwid at 1 in bindings file\n");
> -	expect_condlog(3, "No matching alias [MPATHa] in bindings file.\n");
> +	expect_condlog(3, NOMATCH_STR("MPATHa"));
>  	rc = rlookup_binding(NULL, buf, "MPATHa");
>  	assert_int_equal(rc, -1);
>  	assert_string_equal(buf, "");
> @@ -1095,8 +1092,7 @@ static void rl_match_b(void **state)
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, "MPATHz WWID26\n");
>  	will_return(__wrap_fgets, "MPATHb WWID2\n");
> -	expect_condlog(3, "Found matching alias [MPATHb] in bindings file. "
> -		       "Setting wwid to WWID2\n");
> +	expect_condlog(3, FOUND_ALIAS_STR("MPATHb", "WWID2"));
>  	rc = rlookup_binding(NULL, buf, "MPATHb");
>  	assert_int_equal(rc, 0);
>  	assert_string_equal(buf, "WWID2");
> @@ -1125,7 +1121,7 @@ static void al_a(void **state)
>  	expect_value(__wrap_write, count, strlen(ln));
>  	expect_string(__wrap_write, buf, ln);
>  	will_return(__wrap_write, strlen(ln));
> -	expect_condlog(3, "Created new binding [MPATHa] for WWID [WWIDa]\n");
> +	expect_condlog(3, NEW_STR("MPATHa", "WWIDa"));
>  
>  	alias = allocate_binding(0, "WWIDa", 1, "MPATH");
>  	assert_ptr_not_equal(alias, NULL);
> @@ -1142,7 +1138,7 @@ static void al_zz(void **state)
>  	expect_value(__wrap_write, count, strlen(ln));
>  	expect_string(__wrap_write, buf, ln);
>  	will_return(__wrap_write, strlen(ln));
> -	expect_condlog(3, "Created new binding [MPATHzz] for WWID [WWIDzz]\n");
> +	expect_condlog(3, NEW_STR("MPATHzz", "WWIDzz"));
>  
>  	alias = allocate_binding(0, "WWIDzz", 26*26 + 26, "MPATH");
>  	assert_ptr_not_equal(alias, NULL);
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 10/21] multipath-tools test: use mock_bindings_file() consistently
  2023-09-01 18:02 ` [dm-devel] [PATCH 10/21] multipath-tools test: use mock_bindings_file() consistently mwilck
@ 2023-09-06 22:43   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:43 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:23PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Further improve test readablity.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  tests/alias.c | 186 ++++++++++++++++++++++----------------------------
>  1 file changed, 80 insertions(+), 106 deletions(-)
> 
> diff --git a/tests/alias.c b/tests/alias.c
> index cb6695b..a1415c6 100644
> --- a/tests/alias.c
> +++ b/tests/alias.c
> @@ -489,7 +489,7 @@ static void lb_empty(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, NULL, 0);
>  	assert_int_equal(rc, 1);
> @@ -501,7 +501,7 @@ static void lb_empty_unused(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	mock_unused_alias("MPATHa");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
> @@ -515,7 +515,7 @@ static void lb_empty_failed(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	mock_failed_alias("MPATHa", "WWID0");
>  	mock_unused_alias("MPATHb");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> @@ -530,7 +530,7 @@ static void lb_empty_1_used(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	mock_used_alias("MPATHa", "WWID0");
>  	mock_unused_alias("MPATHb");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> @@ -545,7 +545,7 @@ static void lb_empty_1_used_self(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	mock_used_alias("MPATHa", "WWID0");
>  	mock_self_alias("MPATHb", "WWID0");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> @@ -560,7 +560,7 @@ static void lb_match_a(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> +	mock_bindings_file("MPATHa WWID0\n", 0);
>  	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 0);
>  	assert_int_equal(rc, 0);
> @@ -574,8 +574,7 @@ static void lb_nomatch_a(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHa WWID0\n", -1);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
>  	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 0);
>  	assert_int_equal(rc, 2);
> @@ -587,8 +586,7 @@ static void lb_nomatch_a_bad_check(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHa WWID0\n", -1);
>  	expect_condlog(0, NOMORE_STR);
>  	rc = lookup_binding(NULL, "WWID1", &alias, NULL, 1);
>  	assert_int_equal(rc, -1);
> @@ -600,8 +598,7 @@ static void lb_nomatch_a_unused(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHa WWID0\n", -1);
>  	mock_unused_alias("MPATHb");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
>  	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
> @@ -614,8 +611,7 @@ static void lb_nomatch_a_3_used_failed_self(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHa WWID0\n", -1);
>  	mock_used_alias("MPATHb", "WWID1");
>  	mock_used_alias("MPATHc", "WWID1");
>  	mock_used_alias("MPATHd", "WWID1");
> @@ -632,8 +628,8 @@ static void do_lb_match_c(void **state, int check_if_taken)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, "MPATHc WWID1\n");
> +	mock_bindings_file("MPATHa WWID0\n"
> +			   "MPATHc WWID1", 1);
>  	expect_condlog(3, FOUND_STR("MPATHc", "WWID1"));
>  	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", check_if_taken);
>  	assert_int_equal(rc, 0);
> @@ -657,9 +653,8 @@ static void lb_nomatch_a_c(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, "MPATHc WWID1\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHa WWID0\n"
> +			   "MPATHc WWID1", -1);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
>  	assert_int_equal(rc, 2);
> @@ -671,9 +666,8 @@ static void lb_nomatch_a_d_unused(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, "MPATHd WWID1\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHa WWID0\n"
> +			   "MPATHd WWID1", -1);
>  	mock_unused_alias("MPATHb");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> @@ -686,9 +680,8 @@ static void lb_nomatch_a_d_1_used(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, "MPATHd WWID1\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHa WWID0\n"
> +			   "MPATHd WWID1", -1);
>  	mock_used_alias("MPATHb", "WWID2");
>  	mock_unused_alias("MPATHc");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> @@ -702,9 +695,8 @@ static void lb_nomatch_a_d_2_used(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, "MPATHd WWID1\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHa WWID0\n"
> +			   "MPATHd WWID1", -1);
>  	mock_used_alias("MPATHb", "WWID2");
>  	mock_used_alias("MPATHc", "WWID2");
>  	mock_unused_alias("MPATHe");
> @@ -719,9 +711,8 @@ static void lb_nomatch_a_d_3_used(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, "MPATHd WWID1\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHa WWID0\n"
> +			   "MPATHd WWID1", -1);
>  	mock_used_alias("MPATHb", "WWID2");
>  	mock_used_alias("MPATHc", "WWID2");
>  	mock_used_alias("MPATHe", "WWID2");
> @@ -737,9 +728,8 @@ static void lb_nomatch_c_a(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHc WWID1\n");
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHc WWID1\n"
> +			   "MPATHa WWID0\n", -1);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
>  	assert_int_equal(rc, 2);
> @@ -751,10 +741,9 @@ static void lb_nomatch_d_a_unused(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHc WWID1\n");
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, "MPATHd WWID0\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHc WWID1\n"
> +			   "MPATHa WWID0\n"
> +			   "MPATHd WWID0\n", -1);
>  	mock_unused_alias("MPATHb");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> @@ -767,10 +756,9 @@ static void lb_nomatch_d_a_1_used(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHc WWID1\n");
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, "MPATHd WWID0\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHc WWID1\n"
> +			   "MPATHa WWID0\n"
> +			   "MPATHd WWID0\n", -1);
>  	mock_used_alias("MPATHb", "WWID2");
>  	mock_unused_alias("MPATHe");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> @@ -784,10 +772,9 @@ static void lb_nomatch_a_b(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, "MPATHz WWID26\n");
> -	will_return(__wrap_fgets, "MPATHb WWID1\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHa WWID0\n"
> +			   "MPATHz WWID26\n"
> +			   "MPATHb WWID1\n", -1);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
>  	assert_int_equal(rc, 3);
> @@ -799,10 +786,9 @@ static void lb_nomatch_a_b_bad(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, "MPATHz WWID26\n");
> -	will_return(__wrap_fgets, "MPATHb\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHa WWID0\n"
> +			   "MPATHz WWID26\n"
> +			   "MPATHb\n", -1);
>  	expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
> @@ -815,10 +801,9 @@ static void lb_nomatch_a_b_bad_self(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, "MPATHz WWID26\n");
> -	will_return(__wrap_fgets, "MPATHb\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHa WWID0\n"
> +			   "MPATHz WWID26\n"
> +			   "MPATHb\n", -1);
>  	expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
>  	mock_self_alias("MPATHc", "WWID2");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> @@ -832,10 +817,9 @@ static void lb_nomatch_b_a(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHb WWID1\n");
> -	will_return(__wrap_fgets, "MPATHz WWID26\n");
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHb WWID1\n"
> +			   "MPATHz WWID26\n"
> +			   "MPATHa WWID0\n", -1);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
>  	assert_int_equal(rc, 27);
> @@ -847,10 +831,9 @@ static void lb_nomatch_b_a_3_used(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHb WWID1\n");
> -	will_return(__wrap_fgets, "MPATHz WWID26\n");
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHb WWID1\n"
> +			   "MPATHz WWID26\n"
> +			   "MPATHa WWID0\n", -1);
>  	mock_used_alias("MPATHaa", "WWID2");
>  	mock_used_alias("MPATHab", "WWID2");
>  	mock_used_alias("MPATHac", "WWID2");
> @@ -867,10 +850,9 @@ static void do_lb_nomatch_int_max(void **state, int check_if_taken)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHb WWID1\n");
> -	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n");
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHb WWID1\n"
> +			   "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n"
> +			   "MPATHa WWID0\n", -1);
>  	expect_condlog(0, NOMORE_STR);
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", check_if_taken);
>  	assert_int_equal(rc, -1);
> @@ -892,9 +874,8 @@ static void lb_nomatch_int_max_used(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHb WWID1\n");
> -	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHb WWID1\n"
> +			   "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n", -1);
>  	mock_used_alias("MPATHa", "WWID2");
>  	expect_condlog(0, NOMORE_STR);
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> @@ -907,10 +888,9 @@ static void lb_nomatch_int_max_m1(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHb WWID1\n");
> -	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHb WWID1\n"
> +			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n"
> +			   "MPATHa WWID0\n", -1);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
>  	assert_int_equal(rc, INT_MAX);
> @@ -922,10 +902,9 @@ static void lb_nomatch_int_max_m1_used(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHb WWID1\n");
> -	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHb WWID1\n"
> +			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n"
> +			   "MPATHa WWID0\n", -1);
>  	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWID2");
>  	expect_condlog(0, NOMORE_STR);
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> @@ -938,9 +917,8 @@ static void lb_nomatch_int_max_m1_1_used(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHb WWID1\n");
> -	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHb WWID1\n"
> +			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n", -1);
>  	mock_used_alias("MPATHa", "WWID2");
>  	mock_unused_alias("MPATH" MPATH_ID_INT_MAX);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> @@ -954,9 +932,8 @@ static void lb_nomatch_int_max_m1_2_used(void **state)
>  	int rc;
>  	char *alias;
>  
> -	will_return(__wrap_fgets, "MPATHb WWID1\n");
> -	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHb WWID1\n"
> +			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n", -1);
>  	mock_used_alias("MPATHa", "WWID2");
>  	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWID2");
>  	expect_condlog(0, NOMORE_STR);
> @@ -1014,7 +991,7 @@ static void rl_empty(void **state)
>  	char buf[WWID_SIZE];
>  
>  	buf[0] = '\0';
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	expect_condlog(3, NOMATCH_STR("MPATHa"));
>  	rc = rlookup_binding(NULL, buf, "MPATHa");
>  	assert_int_equal(rc, -1);
> @@ -1027,7 +1004,7 @@ static void rl_match_a(void **state)
>  	char buf[WWID_SIZE];
>  
>  	buf[0] = '\0';
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> +	mock_bindings_file("MPATHa WWID0\n", 0);
>  	expect_condlog(3, FOUND_ALIAS_STR("MPATHa", "WWID0"));
>  	rc = rlookup_binding(NULL, buf, "MPATHa");
>  	assert_int_equal(rc, 0);
> @@ -1040,8 +1017,7 @@ static void rl_nomatch_a(void **state)
>  	char buf[WWID_SIZE];
>  
>  	buf[0] = '\0';
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHa WWID0\n", -1);
>  	expect_condlog(3, NOMATCH_STR("MPATHb"));
>  	rc = rlookup_binding(NULL, buf, "MPATHb");
>  	assert_int_equal(rc, -1);
> @@ -1054,8 +1030,7 @@ static void rl_malformed_a(void **state)
>  	char buf[WWID_SIZE];
>  
>  	buf[0] = '\0';
> -	will_return(__wrap_fgets, "MPATHa     \n");
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("MPATHa     \n", -1);
>  	expect_condlog(3, "Ignoring malformed line 1 in bindings file\n");
>  	expect_condlog(3, NOMATCH_STR("MPATHa"));
>  	rc = rlookup_binding(NULL, buf, "MPATHa");
> @@ -1074,8 +1049,7 @@ static void rl_overlong_a(void **state)
>  	snprintf(line + sizeof(line) - 2, 2, "\n");
>  
>  	buf[0] = '\0';
> -	will_return(__wrap_fgets, line);
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file(line, -1);
>  	expect_condlog(3, "Ignoring too large wwid at 1 in bindings file\n");
>  	expect_condlog(3, NOMATCH_STR("MPATHa"));
>  	rc = rlookup_binding(NULL, buf, "MPATHa");
> @@ -1089,9 +1063,9 @@ static void rl_match_b(void **state)
>  	char buf[WWID_SIZE];
>  
>  	buf[0] = '\0';
> -	will_return(__wrap_fgets, "MPATHa WWID0\n");
> -	will_return(__wrap_fgets, "MPATHz WWID26\n");
> -	will_return(__wrap_fgets, "MPATHb WWID2\n");
> +	mock_bindings_file("MPATHa WWID0\n"
> +			   "MPATHz WWID26\n"
> +			   "MPATHb WWID2\n", 2);
>  	expect_condlog(3, FOUND_ALIAS_STR("MPATHb", "WWID2"));
>  	rc = rlookup_binding(NULL, buf, "MPATHb");
>  	assert_int_equal(rc, 0);
> @@ -1222,7 +1196,7 @@ static void gufa_empty_new_rw(void **state) {
>  
>  	will_return(__wrap_open_file, true);
>  
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	mock_unused_alias("MPATHa");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  
> @@ -1235,7 +1209,7 @@ static void gufa_empty_new_rw(void **state) {
>  static void gufa_empty_new_ro_1(void **state) {
>  	char *alias;
>  	will_return(__wrap_open_file, false);
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	mock_unused_alias("MPATHa");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  
> @@ -1248,7 +1222,7 @@ static void gufa_empty_new_ro_2(void **state) {
>  
>  	will_return(__wrap_open_file, true);
>  
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	mock_unused_alias("MPATHa");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  
> @@ -1261,7 +1235,7 @@ static void gufa_match_a_unused(void **state) {
>  
>  	will_return(__wrap_open_file, true);
>  
> -	will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
> +	mock_bindings_file("MPATHa WWID0", 0);
>  	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
>  	mock_unused_alias("MPATHa");
>  
> @@ -1275,7 +1249,7 @@ static void gufa_match_a_self(void **state) {
>  
>  	will_return(__wrap_open_file, true);
>  
> -	will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
> +	mock_bindings_file("MPATHa WWID0", 0);
>  	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
>  	mock_self_alias("MPATHa", "WWID0");
>  
> @@ -1289,7 +1263,7 @@ static void gufa_match_a_used(void **state) {
>  
>  	will_return(__wrap_open_file, true);
>  
> -	will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
> +	mock_bindings_file("MPATHa WWID0", 0);
>  	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
>  	mock_used_alias("MPATHa", "WWID0");
>  
> @@ -1389,11 +1363,11 @@ static void gufa_old_empty(void **state) {
>  	will_return(__wrap_open_file, true);
>  
>  	/* rlookup_binding for ALIAS */
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	expect_condlog(3, NOMATCH_STR("MPATHz"));
>  
>  	/* lookup_binding */
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  
>  	mock_unused_alias("MPATHz");
> @@ -1410,10 +1384,10 @@ static void gufa_old_empty_self(void **state) {
>  	char *alias;
>  	will_return(__wrap_open_file, true);
>  
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	expect_condlog(3, NOMATCH_STR("MPATHz"));
>  
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	mock_self_alias("MPATHz", "WWID0");
>  
> @@ -1429,10 +1403,10 @@ static void gufa_old_empty_used(void **state) {
>  	char *alias;
>  	will_return(__wrap_open_file, true);
>  
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	expect_condlog(3, NOMATCH_STR("MPATHz"));
>  
> -	will_return(__wrap_fgets, NULL);
> +	mock_bindings_file("", -1);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	mock_used_alias("MPATHz", "WWID0");
>  	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 09/21] multipath-tools tests: convert mock_{failed, used}_alias to macros
  2023-09-01 18:02 ` [dm-devel] [PATCH 09/21] multipath-tools tests: convert mock_{failed, used}_alias to macros mwilck
@ 2023-09-06 22:44   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:44 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:22PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> This way we can further improve readability of the individual test
> cases.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  tests/alias.c | 98 +++++++++++++++++++++++++--------------------------
>  1 file changed, 49 insertions(+), 49 deletions(-)
> 
> diff --git a/tests/alias.c b/tests/alias.c
> index 1c9903c..cb6695b 100644
> --- a/tests/alias.c
> +++ b/tests/alias.c
> @@ -445,26 +445,26 @@ static void mock_self_alias(const char *alias, const char *wwid)
>  #define REUSE_STR(alias_str, wwid_str) ("alias " alias_str " already bound to wwid " wwid_str ", cannot reuse\n")
>  #define NOMORE_STR "no more available user_friendly_names\n"
>  
> -static void mock_failed_alias(const char *alias, char *msg)
> -{
> -	expect_string(__wrap_dm_map_present, str, alias);
> -	will_return(__wrap_dm_map_present, 1);
> -	expect_string(__wrap_dm_get_uuid, name, alias);
> -	expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);
> -	will_return(__wrap_dm_get_uuid, 1);
> -	expect_condlog(3, msg);
> -}
> +#define mock_failed_alias(alias, wwid)					\
> +	do {								\
> +		expect_string(__wrap_dm_map_present, str, alias);	\
> +		will_return(__wrap_dm_map_present, 1);			\
> +		expect_string(__wrap_dm_get_uuid, name, alias);		\
> +		expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);	\
> +		will_return(__wrap_dm_get_uuid, 1);			\
> +		expect_condlog(3, USED_STR(alias, wwid));		\
> +	} while (0)
>  
> -static void mock_used_alias(const char *alias, char *msg)
> -{
> -	expect_string(__wrap_dm_map_present, str, alias);
> -	will_return(__wrap_dm_map_present, 1);
> -	expect_string(__wrap_dm_get_uuid, name, alias);
> -	expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);
> -	will_return(__wrap_dm_get_uuid, 0);
> -	will_return(__wrap_dm_get_uuid, "WWID_USED");
> -	expect_condlog(3, msg);
> -}
> +#define mock_used_alias(alias, wwid)					\
> +	do {								\
> +		expect_string(__wrap_dm_map_present, str, alias);	\
> +		will_return(__wrap_dm_map_present, 1);			\
> +		expect_string(__wrap_dm_get_uuid, name, alias);		\
> +		expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);	\
> +		will_return(__wrap_dm_get_uuid, 0);			\
> +		will_return(__wrap_dm_get_uuid, "WWID_USED");		\
> +		expect_condlog(3, USED_STR(alias, wwid));		\
> +	} while(0)
>  
>  static void mock_bindings_file(const char *content, int match_line)
>  {
> @@ -516,7 +516,7 @@ static void lb_empty_failed(void **state)
>  	char *alias;
>  
>  	will_return(__wrap_fgets, NULL);
> -	mock_failed_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
> +	mock_failed_alias("MPATHa", "WWID0");
>  	mock_unused_alias("MPATHb");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
> @@ -531,7 +531,7 @@ static void lb_empty_1_used(void **state)
>  	char *alias;
>  
>  	will_return(__wrap_fgets, NULL);
> -	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
> +	mock_used_alias("MPATHa", "WWID0");
>  	mock_unused_alias("MPATHb");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
> @@ -546,7 +546,7 @@ static void lb_empty_1_used_self(void **state)
>  	char *alias;
>  
>  	will_return(__wrap_fgets, NULL);
> -	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
> +	mock_used_alias("MPATHa", "WWID0");
>  	mock_self_alias("MPATHb", "WWID0");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
> @@ -616,10 +616,10 @@ static void lb_nomatch_a_3_used_failed_self(void **state)
>  
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, NULL);
> -	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID1"));
> -	mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID1"));
> -	mock_used_alias("MPATHd", USED_STR("MPATHd", "WWID1"));
> -	mock_failed_alias("MPATHe", USED_STR("MPATHe", "WWID1"));
> +	mock_used_alias("MPATHb", "WWID1");
> +	mock_used_alias("MPATHc", "WWID1");
> +	mock_used_alias("MPATHd", "WWID1");
> +	mock_failed_alias("MPATHe", "WWID1");
>  	mock_self_alias("MPATHf", "WWID1");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
>  	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
> @@ -689,7 +689,7 @@ static void lb_nomatch_a_d_1_used(void **state)
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, "MPATHd WWID1\n");
>  	will_return(__wrap_fgets, NULL);
> -	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
> +	mock_used_alias("MPATHb", "WWID2");
>  	mock_unused_alias("MPATHc");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> @@ -705,8 +705,8 @@ static void lb_nomatch_a_d_2_used(void **state)
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, "MPATHd WWID1\n");
>  	will_return(__wrap_fgets, NULL);
> -	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
> -	mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID2"));
> +	mock_used_alias("MPATHb", "WWID2");
> +	mock_used_alias("MPATHc", "WWID2");
>  	mock_unused_alias("MPATHe");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> @@ -722,9 +722,9 @@ static void lb_nomatch_a_d_3_used(void **state)
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, "MPATHd WWID1\n");
>  	will_return(__wrap_fgets, NULL);
> -	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
> -	mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID2"));
> -	mock_used_alias("MPATHe", USED_STR("MPATHe", "WWID2"));
> +	mock_used_alias("MPATHb", "WWID2");
> +	mock_used_alias("MPATHc", "WWID2");
> +	mock_used_alias("MPATHe", "WWID2");
>  	mock_unused_alias("MPATHf");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> @@ -771,7 +771,7 @@ static void lb_nomatch_d_a_1_used(void **state)
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, "MPATHd WWID0\n");
>  	will_return(__wrap_fgets, NULL);
> -	mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
> +	mock_used_alias("MPATHb", "WWID2");
>  	mock_unused_alias("MPATHe");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> @@ -851,9 +851,9 @@ static void lb_nomatch_b_a_3_used(void **state)
>  	will_return(__wrap_fgets, "MPATHz WWID26\n");
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, NULL);
> -	mock_used_alias("MPATHaa", USED_STR("MPATHaa", "WWID2"));
> -	mock_used_alias("MPATHab", USED_STR("MPATHab", "WWID2"));
> -	mock_used_alias("MPATHac", USED_STR("MPATHac", "WWID2"));
> +	mock_used_alias("MPATHaa", "WWID2");
> +	mock_used_alias("MPATHab", "WWID2");
> +	mock_used_alias("MPATHac", "WWID2");
>  	mock_unused_alias("MPATHad");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> @@ -895,7 +895,7 @@ static void lb_nomatch_int_max_used(void **state)
>  	will_return(__wrap_fgets, "MPATHb WWID1\n");
>  	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n");
>  	will_return(__wrap_fgets, NULL);
> -	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
> +	mock_used_alias("MPATHa", "WWID2");
>  	expect_condlog(0, NOMORE_STR);
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, -1);
> @@ -926,7 +926,7 @@ static void lb_nomatch_int_max_m1_used(void **state)
>  	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
>  	will_return(__wrap_fgets, "MPATHa WWID0\n");
>  	will_return(__wrap_fgets, NULL);
> -	mock_used_alias("MPATH" MPATH_ID_INT_MAX, USED_STR("MPATH" MPATH_ID_INT_MAX, "WWID2"));
> +	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWID2");
>  	expect_condlog(0, NOMORE_STR);
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, -1);
> @@ -941,7 +941,7 @@ static void lb_nomatch_int_max_m1_1_used(void **state)
>  	will_return(__wrap_fgets, "MPATHb WWID1\n");
>  	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
>  	will_return(__wrap_fgets, NULL);
> -	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
> +	mock_used_alias("MPATHa", "WWID2");
>  	mock_unused_alias("MPATH" MPATH_ID_INT_MAX);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> @@ -957,8 +957,8 @@ static void lb_nomatch_int_max_m1_2_used(void **state)
>  	will_return(__wrap_fgets, "MPATHb WWID1\n");
>  	will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
>  	will_return(__wrap_fgets, NULL);
> -	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
> -	mock_used_alias("MPATH" MPATH_ID_INT_MAX, USED_STR("MPATH" MPATH_ID_INT_MAX, "WWID2"));
> +	mock_used_alias("MPATHa", "WWID2");
> +	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWID2");
>  	expect_condlog(0, NOMORE_STR);
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, -1);
> @@ -1291,7 +1291,7 @@ static void gufa_match_a_used(void **state) {
>  
>  	will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
>  	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
> -	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
> +	mock_used_alias("MPATHa", "WWID0");
>  
>  	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
>  	assert_ptr_equal(alias, NULL);
> @@ -1355,7 +1355,7 @@ static void gufa_nomatch_c_b_used(void **state) {
>  	mock_bindings_file("MPATHc WWID2\n"
>  			   "MPATHb WWID1",
>  			   -1);
> -	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID4"));
> +	mock_used_alias("MPATHa", "WWID4");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID4"));
>  	mock_unused_alias("MPATHd");
>  
> @@ -1434,7 +1434,7 @@ static void gufa_old_empty_used(void **state) {
>  
>  	will_return(__wrap_fgets, NULL);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> -	mock_used_alias("MPATHz", USED_STR("MPATHz", "WWID0"));
> +	mock_used_alias("MPATHz", "WWID0");
>  	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
>  
>  	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> @@ -1475,7 +1475,7 @@ static void gufa_old_match_used(void **state) {
>  
>  	mock_bindings_file("MPATHz WWID0", 0);
>  	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
> -	mock_used_alias("MPATHz", USED_STR("MPATHz", "WWID0"));
> +	mock_used_alias("MPATHz", "WWID0");
>  	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
>  
>  	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> @@ -1514,7 +1514,7 @@ static void gufa_old_match_other_used(void **state) {
>  	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
>  
>  	mock_bindings_file(bindings, -1);
> -	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
> +	mock_used_alias("MPATHa", "WWID0");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	mock_unused_alias("MPATHb");
>  
> @@ -1557,7 +1557,7 @@ static void gufa_old_match_other_wwidmatch_used(void **state) {
>  
>  	mock_bindings_file(bindings, 1);
>  	expect_condlog(3, FOUND_STR("MPATHc", "WWID2"));
> -	mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID2"));
> +	mock_used_alias("MPATHc", "WWID2");
>  
>  	alias = get_user_friendly_alias("WWID2", "x", "MPATHz", "MPATH", false);
>  	assert_ptr_equal(alias, NULL);
> @@ -1592,7 +1592,7 @@ static void gufa_old_nomatch_wwidmatch_used(void **state) {
>  
>  	mock_bindings_file(bindings, 0);
>  	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
> -	mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
> +	mock_used_alias("MPATHa", "WWID0");
>  
>  	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
>  	assert_ptr_equal(alias, NULL);
> @@ -1632,7 +1632,7 @@ static void gufa_old_nomatch_nowwidmatch_used(void **state) {
>  	mock_bindings_file(bindings, -1);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  
> -	mock_used_alias("MPATHz", USED_STR("MPATHz", "WWID0"));
> +	mock_used_alias("MPATHz", "WWID0");
>  	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
>  
>  	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 11/21] libmultipath: add global variable for current bindings
  2023-09-01 18:02 ` [dm-devel] [PATCH 11/21] libmultipath: add global variable for current bindings mwilck
@ 2023-09-06 22:44   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:44 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:24PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Add a variable global_bindings that holds the currently active vector of
> bindings. This variable is freed at program exit.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/alias.c              | 11 +++++++++--
>  libmultipath/alias.h              |  1 +
>  libmultipath/libmultipath.version |  1 +
>  multipath/main.c                  |  2 ++
>  multipathd/main.c                 |  1 +
>  5 files changed, 14 insertions(+), 2 deletions(-)
> 
> diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> index e61eb91..0759643 100644
> --- a/libmultipath/alias.c
> +++ b/libmultipath/alias.c
> @@ -522,6 +522,7 @@ static void _free_binding(struct binding *bdg)
>   * an abstract type.
>   */
>  typedef struct _vector Bindings;
> +static Bindings global_bindings = { .allocated = 0 };
>  
>  static void free_bindings(Bindings *bindings)
>  {
> @@ -533,6 +534,11 @@ static void free_bindings(Bindings *bindings)
>  	vector_reset(bindings);
>  }
>  
> +void cleanup_bindings(void)
> +{
> +	free_bindings(&global_bindings);
> +}
> +
>  enum {
>  	BINDING_EXISTS,
>  	BINDING_CONFLICT,
> @@ -762,7 +768,6 @@ int check_alias_settings(const struct config *conf)
>  	pthread_cleanup_pop(1);
>  	pthread_cleanup_pop(1);
>  
> -	pthread_cleanup_push_cast(free_bindings, &bindings);
>  	fd = open_file(conf->bindings_file, &can_write, BINDINGS_FILE_HEADER);
>  	if (fd != -1) {
>  		FILE *file = fdopen(fd, "r");
> @@ -782,6 +787,8 @@ int check_alias_settings(const struct config *conf)
>  			close(fd);
>  		}
>  	}
> -	pthread_cleanup_pop(1);
> +
> +	cleanup_bindings();
> +	global_bindings = bindings;
>  	return rc;
>  }
> diff --git a/libmultipath/alias.h b/libmultipath/alias.h
> index fa33223..37b49d9 100644
> --- a/libmultipath/alias.h
> +++ b/libmultipath/alias.h
> @@ -9,5 +9,6 @@ char *get_user_friendly_alias(const char *wwid, const char *file,
>  
>  struct config;
>  int check_alias_settings(const struct config *);
> +void cleanup_bindings(void);
>  
>  #endif /* _ALIAS_H */
> diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
> index a7b8c33..ddd302f 100644
> --- a/libmultipath/libmultipath.version
> +++ b/libmultipath/libmultipath.version
> @@ -64,6 +64,7 @@ global:
>  	checker_name;
>  	checker_state_name;
>  	check_foreign;
> +	cleanup_bindings;
>  	cleanup_lock;
>  	coalesce_paths;
>  	count_active_paths;
> diff --git a/multipath/main.c b/multipath/main.c
> index b78f316..45e9745 100644
> --- a/multipath/main.c
> +++ b/multipath/main.c
> @@ -843,6 +843,8 @@ main (int argc, char *argv[])
>  	conf->force_sync = 1;
>  	if (atexit(cleanup_vecs))
>  		condlog(1, "failed to register cleanup handler for vecs: %m");
> +	if (atexit(cleanup_bindings))
> +		condlog(1, "failed to register cleanup handler for bindings: %m");
>  	while ((arg = getopt(argc, argv, ":adDcChl::eFfM:v:p:b:BrR:itTquUwW")) != EOF ) {
>  		switch(arg) {
>  		case 'v':
> diff --git a/multipathd/main.c b/multipathd/main.c
> index 2e02a54..214ed4a 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -3325,6 +3325,7 @@ static void cleanup_child(void)
>  {
>  	cleanup_threads();
>  	cleanup_vecs();
> +	cleanup_bindings();
>  	if (poll_dmevents)
>  		cleanup_dmevent_waiter();
>  
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 12/21] libmultipath: rename fix_bindings_file() to update_bindings_file()
  2023-09-01 18:02 ` [dm-devel] [PATCH 12/21] libmultipath: rename fix_bindings_file() to update_bindings_file() mwilck
@ 2023-09-06 22:44   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:44 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:25PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> We will use this function in a more generic way, give it a more
> generic name.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/alias.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> index 0759643..af2f647 100644
> --- a/libmultipath/alias.c
> +++ b/libmultipath/alias.c
> @@ -606,8 +606,8 @@ static int write_bindings_file(const Bindings *bindings, int fd)
>  	return 0;
>  }
>  
> -static int fix_bindings_file(const struct config *conf,
> -			     const Bindings *bindings)
> +static int update_bindings_file(const struct config *conf,
> +				const Bindings *bindings)
>  {
>  	int rc;
>  	int fd = -1;
> @@ -777,7 +777,7 @@ int check_alias_settings(const struct config *conf)
>  			rc = _check_bindings_file(conf, file, &bindings);
>  			pthread_cleanup_pop(1);
>  			if (rc == -1 && can_write && !conf->bindings_read_only)
> -				rc = fix_bindings_file(conf, &bindings);
> +				rc = update_bindings_file(conf, &bindings);
>  			else if (rc == -1)
>  				condlog(0, "ERROR: bad settings in read-only bindings file %s",
>  					conf->bindings_file);
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 13/21] libmultipath: alias.c: move bindings related code up
  2023-09-01 18:02 ` [dm-devel] [PATCH 13/21] libmultipath: alias.c: move bindings related code up mwilck
@ 2023-09-06 22:44   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:44 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:26PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> No code changes, just moving code.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/alias.c | 239 ++++++++++++++++++++++---------------------
>  1 file changed, 120 insertions(+), 119 deletions(-)
> 
> diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> index af2f647..5a6cdee 100644
> --- a/libmultipath/alias.c
> +++ b/libmultipath/alias.c
> @@ -9,6 +9,7 @@
>  #include <limits.h>
>  #include <stdio.h>
>  #include <stdbool.h>
> +#include <assert.h>
>  
>  #include "debug.h"
>  #include "util.h"
> @@ -51,6 +52,125 @@
>  
>  static const char bindings_file_header[] = BINDINGS_FILE_HEADER;
>  
> +struct binding {
> +	char *alias;
> +	char *wwid;
> +};
> +
> +/*
> + * Perhaps one day we'll implement this more efficiently, thus use
> + * an abstract type.
> + */
> +typedef struct _vector Bindings;
> +static Bindings global_bindings = { .allocated = 0 };
> +
> +enum {
> +	BINDING_EXISTS,
> +	BINDING_CONFLICT,
> +	BINDING_ADDED,
> +	BINDING_DELETED,
> +	BINDING_NOTFOUND,
> +	BINDING_ERROR,
> +};
> +
> +static void _free_binding(struct binding *bdg)
> +{
> +	free(bdg->wwid);
> +	free(bdg->alias);
> +	free(bdg);
> +}
> +
> +static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
> +{
> +	struct binding *bdg;
> +	int i, cmp = 0;
> +
> +	/*
> +	 * Keep the bindings array sorted by alias.
> +	 * Optimization: Search backwards, assuming that the bindings file is
> +	 * sorted already.
> +	 */
> +	vector_foreach_slot_backwards(bindings, bdg, i) {
> +		if ((cmp = strcmp(bdg->alias, alias)) <= 0)
> +			break;
> +	}
> +
> +	/* Check for exact match */
> +	if (i >= 0 && cmp == 0)
> +		return strcmp(bdg->wwid, wwid) ?
> +			BINDING_CONFLICT : BINDING_EXISTS;
> +
> +	i++;
> +	bdg = calloc(1, sizeof(*bdg));
> +	if (bdg) {
> +		bdg->wwid = strdup(wwid);
> +		bdg->alias = strdup(alias);
> +		if (bdg->wwid && bdg->alias &&
> +		    vector_insert_slot(bindings, i, bdg))
> +			return BINDING_ADDED;
> +		else
> +			_free_binding(bdg);
> +	}
> +
> +	return BINDING_ERROR;
> +}
> +
> +static int write_bindings_file(const Bindings *bindings, int fd)
> +{
> +	struct binding *bnd;
> +	STRBUF_ON_STACK(line);
> +	int i;
> +
> +	if (write(fd, BINDINGS_FILE_HEADER, sizeof(BINDINGS_FILE_HEADER) - 1)
> +	    != sizeof(BINDINGS_FILE_HEADER) - 1)
> +		return -1;
> +
> +	vector_foreach_slot(bindings, bnd, i) {
> +		int len;
> +
> +		if ((len = print_strbuf(&line, "%s %s\n",
> +					bnd->alias, bnd->wwid)) < 0)
> +			return -1;
> +		if (write(fd, get_strbuf_str(&line), len) != len)
> +			return -1;
> +		truncate_strbuf(&line, 0);
> +	}
> +	return 0;
> +}
> +
> +static int update_bindings_file(const struct config *conf,
> +				const Bindings *bindings)
> +{
> +	int rc;
> +	int fd = -1;
> +	char tempname[PATH_MAX];
> +	mode_t old_umask;
> +
> +	if (safe_sprintf(tempname, "%s.XXXXXX", conf->bindings_file))
> +		return -1;
> +	/* coverity: SECURE_TEMP */
> +	old_umask = umask(0077);
> +	if ((fd = mkstemp(tempname)) == -1) {
> +		condlog(1, "%s: mkstemp: %m", __func__);
> +		return -1;
> +	}
> +	umask(old_umask);
> +	pthread_cleanup_push(cleanup_fd_ptr, &fd);
> +	rc = write_bindings_file(bindings, fd);
> +	pthread_cleanup_pop(1);
> +	if (rc == -1) {
> +		condlog(1, "failed to write new bindings file %s",
> +			tempname);
> +		unlink(tempname);
> +		return rc;
> +	}
> +	if ((rc = rename(tempname, conf->bindings_file)) == -1)
> +		condlog(0, "%s: rename: %m", __func__);
> +	else
> +		condlog(1, "updated bindings file %s", conf->bindings_file);
> +	return rc;
> +}
> +
>  int
>  valid_alias(const char *alias)
>  {
> @@ -505,25 +625,6 @@ get_user_friendly_wwid(const char *alias, char *buff, const char *file)
>  	return 0;
>  }
>  
> -struct binding {
> -	char *alias;
> -	char *wwid;
> -};
> -
> -static void _free_binding(struct binding *bdg)
> -{
> -	free(bdg->wwid);
> -	free(bdg->alias);
> -	free(bdg);
> -}
> -
> -/*
> - * Perhaps one day we'll implement this more efficiently, thus use
> - * an abstract type.
> - */
> -typedef struct _vector Bindings;
> -static Bindings global_bindings = { .allocated = 0 };
> -
>  static void free_bindings(Bindings *bindings)
>  {
>  	struct binding *bdg;
> @@ -539,106 +640,6 @@ void cleanup_bindings(void)
>  	free_bindings(&global_bindings);
>  }
>  
> -enum {
> -	BINDING_EXISTS,
> -	BINDING_CONFLICT,
> -	BINDING_ADDED,
> -	BINDING_DELETED,
> -	BINDING_NOTFOUND,
> -	BINDING_ERROR,
> -};
> -
> -static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
> -{
> -	struct binding *bdg;
> -	int i, cmp = 0;
> -
> -	/*
> -	 * Keep the bindings array sorted by alias.
> -	 * Optimization: Search backwards, assuming that the bindings file is
> -	 * sorted already.
> -	 */
> -	vector_foreach_slot_backwards(bindings, bdg, i) {
> -		if ((cmp = strcmp(bdg->alias, alias)) <= 0)
> -			break;
> -	}
> -
> -	/* Check for exact match */
> -	if (i >= 0 && cmp == 0)
> -		return strcmp(bdg->wwid, wwid) ?
> -			BINDING_CONFLICT : BINDING_EXISTS;
> -
> -	i++;
> -	bdg = calloc(1, sizeof(*bdg));
> -	if (bdg) {
> -		bdg->wwid = strdup(wwid);
> -		bdg->alias = strdup(alias);
> -		if (bdg->wwid && bdg->alias &&
> -		    vector_insert_slot(bindings, i, bdg))
> -			return BINDING_ADDED;
> -		else
> -			_free_binding(bdg);
> -	}
> -
> -	return BINDING_ERROR;
> -}
> -
> -static int write_bindings_file(const Bindings *bindings, int fd)
> -{
> -	struct binding *bnd;
> -	STRBUF_ON_STACK(line);
> -	int i;
> -
> -	if (write(fd, BINDINGS_FILE_HEADER, sizeof(BINDINGS_FILE_HEADER) - 1)
> -	    != sizeof(BINDINGS_FILE_HEADER) - 1)
> -		return -1;
> -
> -	vector_foreach_slot(bindings, bnd, i) {
> -		int len;
> -
> -		if ((len = print_strbuf(&line, "%s %s\n",
> -					bnd->alias, bnd->wwid)) < 0)
> -			return -1;
> -		if (write(fd, get_strbuf_str(&line), len) != len)
> -			return -1;
> -		truncate_strbuf(&line, 0);
> -	}
> -	return 0;
> -}
> -
> -static int update_bindings_file(const struct config *conf,
> -				const Bindings *bindings)
> -{
> -	int rc;
> -	int fd = -1;
> -	char tempname[PATH_MAX];
> -	mode_t old_umask;
> -
> -	if (safe_sprintf(tempname, "%s.XXXXXX", conf->bindings_file))
> -		return -1;
> -	/* coverity: SECURE_TEMP */
> -	old_umask = umask(0077);
> -	if ((fd = mkstemp(tempname)) == -1) {
> -		condlog(1, "%s: mkstemp: %m", __func__);
> -		return -1;
> -	}
> -	umask(old_umask);
> -	pthread_cleanup_push(cleanup_fd_ptr, &fd);
> -	rc = write_bindings_file(bindings, fd);
> -	pthread_cleanup_pop(1);
> -	if (rc == -1) {
> -		condlog(1, "failed to write new bindings file %s",
> -			tempname);
> -		unlink(tempname);
> -		return rc;
> -	}
> -	if ((rc = rename(tempname, conf->bindings_file)) == -1)
> -		condlog(0, "%s: rename: %m", __func__);
> -	else
> -		condlog(1, "updated bindings file %s", conf->bindings_file);
> -	return rc;
> -}
> -
>  static int _check_bindings_file(const struct config *conf, FILE *file,
>  				 Bindings *bindings)
>  {
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 14/21] libmultipath: update_bindings_file: take filename argument
  2023-09-01 18:02 ` [dm-devel] [PATCH 14/21] libmultipath: update_bindings_file: take filename argument mwilck
@ 2023-09-06 22:45   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:45 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:27PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> This function just uses the file name, no other configuration
> parameters. Also, pass the Bindings argument first to use the
> same convention as the other functions in this file.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/alias.c | 12 ++++++------
>  1 file changed, 6 insertions(+), 6 deletions(-)
> 
> diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> index 5a6cdee..76d852f 100644
> --- a/libmultipath/alias.c
> +++ b/libmultipath/alias.c
> @@ -138,15 +138,15 @@ static int write_bindings_file(const Bindings *bindings, int fd)
>  	return 0;
>  }
>  
> -static int update_bindings_file(const struct config *conf,
> -				const Bindings *bindings)
> +static int update_bindings_file(const Bindings *bindings,
> +				const char *bindings_file)
>  {
>  	int rc;
>  	int fd = -1;
>  	char tempname[PATH_MAX];
>  	mode_t old_umask;
>  
> -	if (safe_sprintf(tempname, "%s.XXXXXX", conf->bindings_file))
> +	if (safe_sprintf(tempname, "%s.XXXXXX", bindings_file))
>  		return -1;
>  	/* coverity: SECURE_TEMP */
>  	old_umask = umask(0077);
> @@ -164,10 +164,10 @@ static int update_bindings_file(const struct config *conf,
>  		unlink(tempname);
>  		return rc;
>  	}
> -	if ((rc = rename(tempname, conf->bindings_file)) == -1)
> +	if ((rc = rename(tempname, bindings_file)) == -1)
>  		condlog(0, "%s: rename: %m", __func__);
>  	else
> -		condlog(1, "updated bindings file %s", conf->bindings_file);
> +		condlog(1, "updated bindings file %s", bindings_file);
>  	return rc;
>  }
>  
> @@ -778,7 +778,7 @@ int check_alias_settings(const struct config *conf)
>  			rc = _check_bindings_file(conf, file, &bindings);
>  			pthread_cleanup_pop(1);
>  			if (rc == -1 && can_write && !conf->bindings_read_only)
> -				rc = update_bindings_file(conf, &bindings);
> +				rc = update_bindings_file(&bindings, conf->bindings_file);
>  			else if (rc == -1)
>  				condlog(0, "ERROR: bad settings in read-only bindings file %s",
>  					conf->bindings_file);
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 15/21] libmultipath: update_bindings_file: use a single write()
  2023-09-01 18:02 ` [dm-devel] [PATCH 15/21] libmultipath: update_bindings_file: use a single write() mwilck
@ 2023-09-06 22:45   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:45 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:28PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Save code and syscalls by assembling the content in memory first.
> write() may return less bytes written than expected. Deal with it.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/alias.c | 26 +++++++++++++++++---------
>  1 file changed, 17 insertions(+), 9 deletions(-)
> 
> diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> index 76d852f..c26f37c 100644
> --- a/libmultipath/alias.c
> +++ b/libmultipath/alias.c
> @@ -118,22 +118,30 @@ static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
>  static int write_bindings_file(const Bindings *bindings, int fd)
>  {
>  	struct binding *bnd;
> -	STRBUF_ON_STACK(line);
> +	STRBUF_ON_STACK(content);
>  	int i;
> +	size_t len;
>  
> -	if (write(fd, BINDINGS_FILE_HEADER, sizeof(BINDINGS_FILE_HEADER) - 1)
> -	    != sizeof(BINDINGS_FILE_HEADER) - 1)
> +	if (__append_strbuf_str(&content, BINDINGS_FILE_HEADER,
> +				sizeof(BINDINGS_FILE_HEADER) - 1) == -1)
>  		return -1;
>  
>  	vector_foreach_slot(bindings, bnd, i) {
> -		int len;
> +		if (print_strbuf(&content, "%s %s\n",
> +					bnd->alias, bnd->wwid) < 0)
> +			return -1;
> +	}
> +	len = get_strbuf_len(&content);
> +	while (len > 0) {
> +		ssize_t n = write(fd, get_strbuf_str(&content), len);
>  
> -		if ((len = print_strbuf(&line, "%s %s\n",
> -					bnd->alias, bnd->wwid)) < 0)
> +		if (n < 0)
> +			return n;
> +		else if (n == 0) {
> +			condlog(2, "%s: short write", __func__);
>  			return -1;
> -		if (write(fd, get_strbuf_str(&line), len) != len)
> -			return -1;
> -		truncate_strbuf(&line, 0);
> +		}
> +		len -= n;
>  	}
>  	return 0;
>  }
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 16/21] libmultipath: update_bindings_file: don't log temp file name
  2023-09-01 18:02 ` [dm-devel] [PATCH 16/21] libmultipath: update_bindings_file: don't log temp file name mwilck
@ 2023-09-06 22:45   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:45 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:29PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> The name of the temp file is unlikely to be helpful for uses,
> and hard to predict in the unit test. Omit it.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/alias.c | 3 +--
>  1 file changed, 1 insertion(+), 2 deletions(-)
> 
> diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> index c26f37c..9ca5da8 100644
> --- a/libmultipath/alias.c
> +++ b/libmultipath/alias.c
> @@ -167,8 +167,7 @@ static int update_bindings_file(const Bindings *bindings,
>  	rc = write_bindings_file(bindings, fd);
>  	pthread_cleanup_pop(1);
>  	if (rc == -1) {
> -		condlog(1, "failed to write new bindings file %s",
> -			tempname);
> +		condlog(1, "failed to write new bindings file");
>  		unlink(tempname);
>  		return rc;
>  	}
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 17/21] libmultipath: alias.c: factor out read_binding()
  2023-09-01 18:02 ` [dm-devel] [PATCH 17/21] libmultipath: alias.c: factor out read_binding() mwilck
@ 2023-09-06 22:45   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:45 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:30PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> This way we can test the parsing of input lines from the bindings
> file more easily.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/alias.c | 58 ++++++++++++++++++++++++++++++--------------
>  1 file changed, 40 insertions(+), 18 deletions(-)
> 
> diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> index 9ca5da8..2f9bc82 100644
> --- a/libmultipath/alias.c
> +++ b/libmultipath/alias.c
> @@ -647,6 +647,43 @@ void cleanup_bindings(void)
>  	free_bindings(&global_bindings);
>  }
>  
> +enum {
> +	READ_BINDING_OK,
> +	READ_BINDING_SKIP,
> +};
> +
> +static int read_binding(char *line, unsigned int linenr, char **alias,
> +			char **wwid) {
> +	char *c, *saveptr;
> +
> +	c = strpbrk(line, "#\n\r");
> +	if (c)
> +		*c = '\0';
> +
> +	*alias = strtok_r(line, " \t", &saveptr);
> +	if (!*alias) /* blank line */
> +		return READ_BINDING_SKIP;
> +
> +	*wwid = strtok_r(NULL, " \t", &saveptr);
> +	if (!*wwid) {
> +		condlog(1, "invalid line %u in bindings file, missing WWID",
> +			linenr);
> +		return READ_BINDING_SKIP;
> +	}
> +	if (strlen(*wwid) > WWID_SIZE - 1) {
> +		condlog(3,
> +			"Ignoring too large wwid at %u in bindings file",
> +			linenr);
> +		return READ_BINDING_SKIP;
> +	}
> +	c = strtok_r(NULL, " \t", &saveptr);
> +	if (c)
> +		/* This is non-fatal */
> +		condlog(1, "invalid line %d in bindings file, extra args \"%s\"",
> +			linenr, c);
> +	return READ_BINDING_OK;
> +}
> +
>  static int _check_bindings_file(const struct config *conf, FILE *file,
>  				 Bindings *bindings)
>  {
> @@ -658,27 +695,12 @@ static int _check_bindings_file(const struct config *conf, FILE *file,
>  
>  	pthread_cleanup_push(cleanup_free_ptr, &line);
>  	while ((n = getline(&line, &line_len, file)) >= 0) {
> -		char *c, *alias, *wwid, *saveptr;
> +		char *alias, *wwid;
>  		const char *mpe_wwid;
>  
> -		linenr++;
> -		c = strpbrk(line, "#\n\r");
> -		if (c)
> -			*c = '\0';
> -		alias = strtok_r(line, " \t", &saveptr);
> -		if (!alias) /* blank line */
> +		if (read_binding(line, ++linenr, &alias, &wwid)
> +		    == READ_BINDING_SKIP)
>  			continue;
> -		wwid = strtok_r(NULL, " \t", &saveptr);
> -		if (!wwid) {
> -			condlog(1, "invalid line %d in bindings file, missing WWID",
> -				linenr);
> -			continue;
> -		}
> -		c = strtok_r(NULL, " \t", &saveptr);
> -		if (c)
> -			/* This is non-fatal */
> -			condlog(1, "invalid line %d in bindings file, extra args \"%s\"",
> -				linenr, c);
>  
>  		mpe_wwid = get_mpe_wwid(conf->mptable, alias);
>  		if (mpe_wwid && strcmp(mpe_wwid, wwid)) {
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 18/21] libmultipath: keep bindings in memory
  2023-09-01 18:02 ` [dm-devel] [PATCH 18/21] libmultipath: keep bindings in memory mwilck
@ 2023-09-06 22:47   ` Benjamin Marzinski
  2023-09-07 10:30     ` Martin Wilck
  0 siblings, 1 reply; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:47 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:31PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Rather than opening the bindings file every time we must retrieve
> a binding, keep the contents in memory and write the file only
> if additions have been made. This simplifies the code, and should speed up
> alias lookups significantly. As a side effect, the aliases will be stored
> sorted by alias, which changes the way aliases are allocated if there are
> unused "holes" in the sequence of aliases. For example, if the bindings file
> contains mpathb, mpathy, and mpatha, in this order, the next new alias used to
> be mpathz and is now mpathc.
> 
> Another side effect is that multipathd will not automatically pick up changes
> to the bindings file at runtime without a reconfigure operation. It is
> questionable whether these on-the-fly changes were a good idea in the first
> place, as inconsistent configurations may easily come to pass. It desired,
> it would be feasible to implement automatic update of the bindings using the
> existing inotify approach.

I'm not so much worried about someone manually changing the bindings
file outside of multipath. But it is possible for multipathd to miss
changes made by the multipath command.  For instance, say that someone
has find_multipaths set to "yes" and adds a new device, but only a
single path comes up.  They know there will be more paths later, so they
run

# multipath <new_path_dev>

to create a multipath device for this.  Multipathd won't pick up this
new binding. If, for some reason the path goes away, and comes back
later, it will now be in the bindings file, but multipathd will still
have no record of the correct binding for it, which already exists in
the bindings file. I don't know if this needs something as complex as
inotify to handle.  We could just record the mtime of the bindings file
when we read it.  If it has changed when we call
get_user_friendly_alias() or get_user_friendly_wwid() we would call
check_alias_settings().

additional comments below. 
> The new implementation of get_user_friendly_alias() is slightly different
> than before. The logic is summarized in a comment in the code. Unit tests
> will be provided that illustrate the changes.
> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/alias.c     | 369 ++++++++++++++++-----------------------
>  libmultipath/alias.h     |   2 +-
>  libmultipath/configure.c |   3 +-
>  3 files changed, 157 insertions(+), 217 deletions(-)
> 
> diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> index 2f9bc82..6003df0 100644
> --- a/libmultipath/alias.c
> +++ b/libmultipath/alias.c
> @@ -50,8 +50,6 @@
>  "# alias wwid\n" \
>  "#\n"
>  
> -static const char bindings_file_header[] = BINDINGS_FILE_HEADER;
> -
>  struct binding {
>  	char *alias;
>  	char *wwid;
> @@ -80,6 +78,45 @@ static void _free_binding(struct binding *bdg)
>  	free(bdg);
>  }
>  
> +static const struct binding *get_binding_for_alias(const Bindings *bindings,
> +						   const char *alias)
> +{
> +	const struct binding *bdg;
> +	int i;
> +
> +	if (!alias)
> +		return NULL;
> +	vector_foreach_slot(bindings, bdg, i) {
> +		if (!strncmp(bdg->alias, alias, WWID_SIZE)) {
> +			condlog(3, "Found matching alias [%s] in bindings file."
> +				" Setting wwid to %s", alias, bdg->wwid);
> +			return bdg;
> +		}
> +	}
> +
> +	condlog(3, "No matching alias [%s] in bindings file.", alias);
> +	return NULL;
> +}
> +
> +static const struct binding *get_binding_for_wwid(const Bindings *bindings,
> +						  const char *wwid)
> +{
> +	const struct binding *bdg;
> +	int i;
> +
> +	if (!wwid)
> +		return NULL;
> +	vector_foreach_slot(bindings, bdg, i) {
> +		if (!strncmp(bdg->wwid, wwid, WWID_SIZE)) {
> +			condlog(3, "Found matching wwid [%s] in bindings file."
> +				" Setting alias to %s", wwid, bdg->alias);
> +			return bdg;
> +		}
> +	}
> +	condlog(3, "No matching wwid [%s] in bindings file.", wwid);
> +	return NULL;
> +}
> +
>  static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
>  {
>  	struct binding *bdg;
> @@ -115,6 +152,24 @@ static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
>  	return BINDING_ERROR;
>  }
>  
> +static int delete_binding(Bindings *bindings, const char *wwid)
> +{
> +	struct binding *bdg;
> +	int i;
> +
> +	vector_foreach_slot(bindings, bdg, i) {
> +		if (!strncmp(bdg->wwid, wwid, WWID_SIZE)) {
> +			_free_binding(bdg);
> +			break;
> +		}
> +	}
> +	if (i >= VECTOR_SIZE(bindings))
> +		return BINDING_NOTFOUND;
> +
> +	vector_del_slot(bindings, i);
> +	return BINDING_DELETED;
> +}
> +
>  static int write_bindings_file(const Bindings *bindings, int fd)
>  {
>  	struct binding *bnd;
> @@ -267,38 +322,15 @@ static bool id_already_taken(int id, const char *prefix, const char *map_wwid)
>  	return alias_already_taken(alias, map_wwid);
>  }
>  
> -/*
> - * Returns: 0   if matching entry in WWIDs file found
> - *         -1   if an error occurs
> - *         >0   a free ID that could be used for the WWID at hand
> - * *map_alias is set to a freshly allocated string with the matching alias if
> - * the function returns 0, or to NULL otherwise.
> - */
> -static int
> -lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
> -	       const char *prefix, int check_if_taken)
> +int get_free_id(const Bindings *bindings, const char *prefix, const char *map_wwid)
>  {
> -	char buf[LINE_MAX];
> -	unsigned int line_nr = 0;
> -	int id = 1;
> +	const struct binding *bdg;
> +	int i, id = 1;
>  	int biggest_id = 1;
>  	int smallest_bigger_id = INT_MAX;
>  
> -	*map_alias = NULL;
> -
> -	rewind(f);
> -	while (fgets(buf, LINE_MAX, f)) {
> -		const char *alias, *wwid;
> -		char *c, *saveptr;
> -		int curr_id;
> -
> -		line_nr++;
> -		c = strpbrk(buf, "#\n\r");
> -		if (c)
> -			*c = '\0';
> -		alias = strtok_r(buf, " \t", &saveptr);
> -		if (!alias) /* blank line */
> -			continue;
> +	vector_foreach_slot(bindings, bdg, i) {
> +		int curr_id = scan_devname(bdg->alias, prefix);

If we know that the bindings will be sorted by alias order, we don't
need to do all this. Something like this should work:

		if (curr_id <= 0)
			continue;

		while (id < curr_id) {
			if (!id_already_taken(id, prefix, map_wwid))
				return id;
			id++;
		}
		if (id == INT_MAX)
			break;
		id++;
	}
	if (id == INT_MAX)
		condlog(0, "no more available user_friendly_names");
	return id < INT_MAX ? id : -1;
}
>  
>  		/*
>  		 * Find an unused index - explanation of the algorithm
> @@ -333,8 +365,6 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
>  		 * biggest_id is always > smallest_bigger_id, except in the
>  		 * "perfectly ordered" case.
>  		 */
> -
> -		curr_id = scan_devname(alias, prefix);
>  		if (curr_id == id) {
>  			if (id < INT_MAX)
>  				id++;
> @@ -345,36 +375,15 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
>  		}
>  		if (curr_id > biggest_id)
>  			biggest_id = curr_id;
> +
>  		if (curr_id > id && curr_id < smallest_bigger_id)
>  			smallest_bigger_id = curr_id;
> -		wwid = strtok_r(NULL, " \t", &saveptr);
> -		if (!wwid){
> -			condlog(3,
> -				"Ignoring malformed line %u in bindings file",
> -				line_nr);
> -			continue;
> -		}
> -		if (strcmp(wwid, map_wwid) == 0){
> -			condlog(3, "Found matching wwid [%s] in bindings file."
> -				" Setting alias to %s", wwid, alias);
> -			*map_alias = strdup(alias);
> -			if (*map_alias == NULL) {
> -				condlog(0, "Cannot copy alias from bindings "
> -					"file: out of memory");
> -				return -1;
> -			}
> -			return 0;
> -		}
>  	}
> -	if (!prefix && check_if_taken)
> -		id = -1;
> -	if (id >= smallest_bigger_id) {
> -		if (biggest_id < INT_MAX)
> -			id = biggest_id + 1;
> -		else
> -			id = -1;
> -	}
> -	if (id > 0 && check_if_taken) {
> +
> +	if (id >= smallest_bigger_id)
> +		id = biggest_id < INT_MAX ? biggest_id + 1 : -1;
> +
> +	if (id > 0) {
>  		while(id_already_taken(id, prefix, map_wwid)) {
>  			if (id == INT_MAX) {
>  				id = -1;
> @@ -391,63 +400,16 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
>  			}
>  		}
>  	}
> -	if (id < 0) {
> +
> +	if (id < 0)
>  		condlog(0, "no more available user_friendly_names");
> -		return -1;
> -	} else
> -		condlog(3, "No matching wwid [%s] in bindings file.", map_wwid);
>  	return id;
>  }
>  
> -static int
> -rlookup_binding(FILE *f, char *buff, const char *map_alias)
> -{
> -	char line[LINE_MAX];
> -	unsigned int line_nr = 0;
> -
> -	buff[0] = '\0';
> -
> -	while (fgets(line, LINE_MAX, f)) {
> -		char *c, *saveptr;
> -		const char *alias, *wwid;
> -
> -		line_nr++;
> -		c = strpbrk(line, "#\n\r");
> -		if (c)
> -			*c = '\0';
> -		alias = strtok_r(line, " \t", &saveptr);
> -		if (!alias) /* blank line */
> -			continue;
> -		wwid = strtok_r(NULL, " \t", &saveptr);
> -		if (!wwid){
> -			condlog(3,
> -				"Ignoring malformed line %u in bindings file",
> -				line_nr);
> -			continue;
> -		}
> -		if (strlen(wwid) > WWID_SIZE - 1) {
> -			condlog(3,
> -				"Ignoring too large wwid at %u in bindings file", line_nr);
> -			continue;
> -		}
> -		if (strcmp(alias, map_alias) == 0){
> -			condlog(3, "Found matching alias [%s] in bindings file."
> -				" Setting wwid to %s", alias, wwid);
> -			strlcpy(buff, wwid, WWID_SIZE);
> -			return 0;
> -		}
> -	}
> -	condlog(3, "No matching alias [%s] in bindings file.", map_alias);
> -
> -	return -1;
> -}
> -
>  static char *
> -allocate_binding(int fd, const char *wwid, int id, const char *prefix)
> +allocate_binding(const char *filename, const char *wwid, int id, const char *prefix)
>  {
>  	STRBUF_ON_STACK(buf);
> -	off_t offset;
> -	ssize_t len;
>  	char *alias, *c;
>  
>  	if (id <= 0) {
> @@ -460,29 +422,22 @@ allocate_binding(int fd, const char *wwid, int id, const char *prefix)
>  	    format_devname(&buf, id) == -1)
>  		return NULL;
>  
> -	if (print_strbuf(&buf, " %s\n", wwid) < 0)
> -		return NULL;
> -
> -	offset = lseek(fd, 0, SEEK_END);
> -	if (offset < 0){
> -		condlog(0, "Cannot seek to end of bindings file : %s",
> -			strerror(errno));
> -		return NULL;
> -	}
> -
> -	len = get_strbuf_len(&buf);
>  	alias = steal_strbuf_str(&buf);
>  
> -	if (write(fd, alias, len) != len) {
> -		condlog(0, "Cannot write binding to bindings file : %s",
> -			strerror(errno));
> -		/* clear partial write */
> -		if (ftruncate(fd, offset))
> -			condlog(0, "Cannot truncate the header : %s",
> -				strerror(errno));
> +	if (add_binding(&global_bindings, alias, wwid) != BINDING_ADDED) {
> +		condlog(0, "%s: cannot allocate new binding %s for %s",
> +			__func__, alias, wwid);
>  		free(alias);
>  		return NULL;
>  	}
> +
> +	if (update_bindings_file(&global_bindings, filename) == -1) {
> +		condlog(1, "%s: deleting binding %s for %s", __func__, alias, wwid);
> +		delete_binding(&global_bindings, wwid);
> +		free(alias);
> +		return NULL;
> +	}
> +

We no longer need to end the alias at the space, since we're not
printing the wwid in the buffer.

>  	c = strchr(alias, ' ');
>  	if (c)
>  		*c = '\0';
> @@ -491,144 +446,130 @@ allocate_binding(int fd, const char *wwid, int id, const char *prefix)
>  	return alias;
>  }
>  
> +/*
> + * get_user_friendly_alias() action table
> + *
> + * The table shows the various cases, the actions taken, and the CI
> + * functions from tests/alias.c that represent them.
> + *
> + *  - O: old alias given
> + *  - A: old alias in table (y: yes, correct WWID; X: yes, wrong WWID)
> + *  - W: wwid in table
> + *  - U: old alias is used
> + *
> + *  | No | O | A | W | U | action                                     | function gufa_X              |
> + *  |----+---+---+---+---+--------------------------------------------+------------------------------|
> + *  |  1 | n | - | n | - | get new alias                              | nomatch_Y                    |
> + *  |  2 | n | - | y | - | use alias from bindings                    | match_a_Y                    |
> + *  |  3 | y | n | n | n | add binding for old alias                  | old_nomatch_nowwidmatch      |
> + *  |  4 | y | n | n | y | error, no alias (can't add entry)          | old_nomatch_nowwidmatch_used |
> + *  |  5 | y | n | y | - | use alias from bindings (avoid duplicates) | old_nomatch_wwidmatch        |
> + *  |  6 | y | y | n | - | [ impossible ]                             | -                            |
> + *  |  7 | y | y | y | n | use old alias == alias from bindings       | old_match                    |
> + *  |  8 | y | y | y | y | error, no alias (would be duplicate)       | old_match_used               |
> + *  |  9 | y | X | n | - | get new alias                              | old_match_other              |
> + *  | 10 | y | X | y | - | use alias from bindings                    | old_match_other_wwidmatch    |
> + *
> + * Notes:
> + *  - "use alias from bindings" means that the alias from the bindings file will
> + *     be tried; if it is in use, the alias selection will fail. No other
> + *     bindings will be attempted.
> + *  - "get new alias" fails if all aliases are used up, or if writing the
> + *     bindings file fails.
> + */
> +
>  char *get_user_friendly_alias(const char *wwid, const char *file, const char *alias_old,
>  			      const char *prefix, bool bindings_read_only)
>  {
>  	char *alias = NULL;
>  	int id = 0;
> -	int fd, can_write;
>  	bool new_binding = false;
> -	char buff[WWID_SIZE];
> -	FILE *f;
> +	bool old_alias_taken = false;
> +	const struct binding *bdg;
>  
> -	fd = open_file(file, &can_write, bindings_file_header);
> -	if (fd < 0)
> -		return NULL;
> -
> -	f = fdopen(fd, "r");
> -	if (!f) {
> -		condlog(0, "cannot fdopen on bindings file descriptor");
> -		close(fd);
> -		return NULL;
> -	}
> -
> -	if (!strlen(alias_old))
> +	if (!*alias_old)
>  		goto new_alias;
>  
> -	/* lookup the binding. if it exists, the wwid will be in buff
> -	 * either way, id contains the id for the alias
> -	 */
> -	rlookup_binding(f, buff, alias_old);
> -
> -	if (strlen(buff) > 0) {
> -		/* if buff is our wwid, it's already
> -		 * allocated correctly
> -		 */
> -		if (strcmp(buff, wwid) == 0) {
> +	/* See if there's a binding matching both alias_old and wwid */
> +	bdg = get_binding_for_alias(&global_bindings, alias_old);
> +	if (bdg) {
> +		if (!strcmp(bdg->wwid, wwid)) {

I'm still not convinced that it is possible for mpp->alias_old to be set
when there isn't a multipath device with the alias_old and the desired
wwid. Unless I'm missing something we should be able to skip the
alias_already_taken() check.

>  			if (!alias_already_taken(alias_old, wwid))
>  				alias = strdup(alias_old);
>  			else
>  				condlog(0, "ERROR: old alias [%s] for wwid [%s] is used by other map",
>  					alias_old, wwid);
>  			goto out;
> -
>  		} else {
>  			condlog(0, "alias %s already bound to wwid %s, cannot reuse",
> -				alias_old, buff);
> -			goto new_alias;		     ;
> +				alias_old, bdg->wwid);
> +			goto new_alias;
>  		}
>  	}
>  
> -	/*
> -	 * Look for an existing alias in the bindings file.
> -	 * Pass prefix = NULL, so lookup_binding() won't try to allocate a new id.
> -	 */
> -	id = lookup_binding(f, wwid, &alias, NULL, 0);
> -	if (alias) {
> -		if (alias_already_taken(alias, wwid)) {
> -			free(alias);
> -			alias = NULL;
> -		} else
> -			condlog(3, "Use existing binding [%s] for WWID [%s]",
> -				alias, wwid);
> -		goto out;
> -	}
> -
>  	/* allocate the existing alias in the bindings file */
> -	id = scan_devname(alias_old, prefix);
> -	if (id > 0 && id_already_taken(id, prefix, wwid)) {
again, I think it's safe to skip this check, since we're checking
alias_old.
> +	if (alias_already_taken(alias_old, wwid)) {
>  		condlog(0, "ERROR: old alias [%s] for wwid [%s] is used by other map",
>  			alias_old, wwid);
> +		old_alias_taken = true;
> +	} else
> +		id = scan_devname(alias_old, prefix);
> +
> +new_alias:
> +	/* Check for existing binding of WWID */
> +	bdg = get_binding_for_wwid(&global_bindings, wwid);
> +	if (bdg) {
> +		if (!alias_already_taken(bdg->alias, wwid)) {
> +			condlog(3, "Use existing binding [%s] for WWID [%s]",
> +				bdg->alias, wwid);
> +			alias = strdup(bdg->alias);
> +		}
>  		goto out;
>  	}
>  
> -new_alias:
> -	if (id <= 0) {
> +	/*
> +	 * old_alias_taken means that the WWID is not in the bindings file, but
> +	 * alias_old is currently taken by a different WWID. We shouldn't added
> +	 * a new binding in this case.
> +	 */
> +	if (id <= 0 && !old_alias_taken) {
>  		/*
>  		 * no existing alias was provided, or allocating it
>  		 * failed. Try a new one.
>  		 */
> -		id = lookup_binding(f, wwid, &alias, prefix, 1);
> -		if (id == 0 && alias_already_taken(alias, wwid)) {
> -			free(alias);
> -			alias = NULL;
> -		}
> +		id = get_free_id(&global_bindings, prefix, wwid);
>  		if (id <= 0)
>  			goto out;
>  		else
>  			new_binding = true;
>  	}
>  
> -	if (fflush(f) != 0) {
> -		condlog(0, "cannot fflush bindings file stream : %s",
> -			strerror(errno));
> -		goto out;
> -	}
> +	if (!bindings_read_only && id > 0)
> +		alias = allocate_binding(file, wwid, id, prefix);
>  
> -	if (can_write && !bindings_read_only) {
> -		alias = allocate_binding(fd, wwid, id, prefix);
> -		if (alias && !new_binding)
> -			condlog(2, "Allocated existing binding [%s] for WWID [%s]",
> -				alias, wwid);
> -	}
> +	if (alias && !new_binding)
> +		condlog(2, "Allocated existing binding [%s] for WWID [%s]",
> +			alias, wwid);
>  
>  out:
> -	pthread_cleanup_push(free, alias);
> -	fclose(f);
> -	pthread_cleanup_pop(0);
>  	return alias;
>  }
>  
> -int
> -get_user_friendly_wwid(const char *alias, char *buff, const char *file)
> +int get_user_friendly_wwid(const char *alias, char *buff)
>  {
> -	int fd, unused;
> -	FILE *f;
> +	const struct binding *bdg;
>  
>  	if (!alias || *alias == '\0') {
>  		condlog(3, "Cannot find binding for empty alias");
>  		return -1;
>  	}
>  
> -	fd = open_file(file, &unused, bindings_file_header);
> -	if (fd < 0)
> -		return -1;
> -
> -	f = fdopen(fd, "r");
> -	if (!f) {
> -		condlog(0, "cannot fdopen on bindings file descriptor : %s",
> -			strerror(errno));
> -		close(fd);
> +	bdg = get_binding_for_alias(&global_bindings, alias);
> +	if (!bdg) {
> +		*buff = '\0';
>  		return -1;
>  	}
> -
> -	rlookup_binding(f, buff, alias);
> -	if (!strlen(buff)) {
> -		fclose(f);
> -		return -1;
> -	}
> -
> -	fclose(f);
I think you mean bdg->wwid here.

-Ben

> +	strlcpy(buff, bdg->alias, WWID_SIZE);
>  	return 0;
>  }
>  
> diff --git a/libmultipath/alias.h b/libmultipath/alias.h
> index 37b49d9..5ef6720 100644
> --- a/libmultipath/alias.h
> +++ b/libmultipath/alias.h
> @@ -2,7 +2,7 @@
>  #define _ALIAS_H
>  
>  int valid_alias(const char *alias);
> -int get_user_friendly_wwid(const char *alias, char *buff, const char *file);
> +int get_user_friendly_wwid(const char *alias, char *buff);
>  char *get_user_friendly_alias(const char *wwid, const char *file,
>  			      const char *alias_old,
>  			      const char *prefix, bool bindings_read_only);
> diff --git a/libmultipath/configure.c b/libmultipath/configure.c
> index 029fbbd..d809490 100644
> --- a/libmultipath/configure.c
> +++ b/libmultipath/configure.c
> @@ -1378,8 +1378,7 @@ static int _get_refwwid(enum mpath_cmds cmd, const char *dev,
>  			refwwid = tmpwwid;
>  
>  		/* or may be a binding */
> -		else if (get_user_friendly_wwid(dev, tmpwwid,
> -						conf->bindings_file) == 0)
> +		else if (get_user_friendly_wwid(dev, tmpwwid) == 0)
>  			refwwid = tmpwwid;
>  
>  		/* or may be an alias */
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 19/21] multipath-tools tests: fix alias tests
  2023-09-01 18:02 ` [dm-devel] [PATCH 19/21] multipath-tools tests: fix alias tests mwilck
@ 2023-09-06 22:47   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:47 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:32PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> The different implementation of get_user_friendly_alias() and its helpers
> necessitates changes in the unit tests. It would be nice if it didn't, but the
> unit tests are too closely bound to the implementation to make this possible.
> 
> - The bindings table is held in memory in alphabetically sorted order, which
>   may change the result of looking for free alias IDs if the entries in the
>   bindings file were unordered initially. In particular, if only a small
>   number of bindings exists, "holes" in the file will be detected more easily.
>   But because the sort order of the aliases differs from simple alphabetic
>   sorting ("mpathz" precedes "mpathaa"), a bindings file that contains all
>   bindings from "a" to "aa" (or more) will appear unsorted.
>   As an extra check, some of the unit tests deliberately use a different
>   implementation of add_binding() that does not order the bindings
>   table.
> 
> - Broken lines in the bindings file never make it to the in-memory
>   representation. An alias that appeard "used" because it occurred in a broken
>   line will not appear used any more. Warnings about malformed lines will only
>   be printed while the bindings file is read, not from get_user_friendly_alias().
> 
> - The match_line argument of mock_bindings_file() is obsolete.
> 
> - lookup_binding() and rlookup_binding() have been removed from
>   libmultipath. They are now emulated in the unit test code.
> 
> - lookup_binding() didn't check for used alias in all cases previously, but it does now.
> 
> - prefix != NULL and check_if_taken == false is not supported any more
>   in lookup_binding().
> 
> - allocate_binding() uses a very different sequence of systems calls now, as
>   it's implemented using update_bindings_file(). In particular, it's now more
>   difficult to predict the content of the write() call that creates the
>   bindings file. See comments for __wrap_write().
> 
> - some unit tests for get_user_friendly_alias() had to call
>   mock_bindings_file() twice, because the old implementation would read the
>   file twice (first rlookup_binding() and then lookup_binding()). This is not
>   necessary any more.
> 
> - The unit tests need a teardown function to clear the bindings table in memory.
> 
> - Minor changes are necessary because of changed ordering of the log messages.
>   Previously, lookup_binding() combined check for an existing entry and the
>   search for a new ID. The new algorithm does this in two separate steps and
>   tests for used aliases in between, which causes a change in the order in which
>   log messages are emitted.
> 

I'm still not sure that gufa_old_empty_used, gufa_old_match_used, and
gufa_old_nomatch_nowwidmatch_used are valid tests.

-Ben

> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  tests/alias.c | 1013 +++++++++++++++++++++++++++++++------------------
>  1 file changed, 638 insertions(+), 375 deletions(-)
> 
> diff --git a/tests/alias.c b/tests/alias.c
> index a1415c6..4ac6425 100644
> --- a/tests/alias.c
> +++ b/tests/alias.c
> @@ -3,10 +3,12 @@
>  #include <setjmp.h>
>  #include <stdio.h>
>  #include <cmocka.h>
> +#include "strbuf.h"
>  #include "util.h"
>  #include "alias.h"
>  #include "test-log.h"
>  #include <errno.h>
> +#include <string.h>
>  
>  #include "globals.c"
>  #include "../libmultipath/alias.c"
> @@ -20,18 +22,6 @@
>  #define MPATH_ID_INT_MAX_p1 "fxshrxx"
>  #endif
>  
> -void __wrap_rewind(FILE *stream)
> -{}
> -
> -char *__wrap_fgets(char *buf, int n, FILE *stream)
> -{
> -	char *val = mock_ptr_type(char *);
> -	if (!val)
> -		return NULL;
> -	strlcpy(buf, val, n);
> -	return buf;
> -}
> -
>  static int __set_errno(int err)
>  {
>  	if (err >= 0) {
> @@ -43,25 +33,46 @@ static int __set_errno(int err)
>  	}
>  }
>  
> -off_t __wrap_lseek(int fd, off_t offset, int whence)
> -{
> -	return __set_errno(mock_type(int));
> -
> -}
> -
> +/*
> + * allocate_binding -> write_bindings_file() writes the entire file, i.e. the
> + * header, any pre-existing bindings, and the new binding. The complete content
> + * depends on history and is different to predict here. Therefore we check only
> + * the newly added binding. Because add_binding() sorts entries, this new
> + * binding isn't necessarily the last one; receive it from will_return() and
> + * search for it with strstr().
> + * If the string to be written doesn't start with the bindings file
> + * header, it's a test of a partial write.
> + */
>  ssize_t __wrap_write(int fd, const void *buf, size_t count)
>  {
> +	const char *binding, *start;
> +
> +#if DEBUG_WRITE
> +	fprintf(stderr, "%s: %zx exp %zx\n===\n%s\n===\n", __func__, strlen(buf),
> +		count, (const char *)buf);
> +#endif
> +	if (!strncmp((const char *)buf, BINDINGS_FILE_HEADER,
> +		     sizeof(BINDINGS_FILE_HEADER) - 1))
> +		start = (const char *)buf + sizeof(BINDINGS_FILE_HEADER) - 1;
> +	else
> +		start = buf;
> +	binding = mock_ptr_type(char *);
> +	start = strstr(start, binding);
>  	check_expected(count);
> -	check_expected(buf);
> +	assert_ptr_not_equal(start, NULL);
>  	return __set_errno(mock_type(int));
>  }
>  
> -int __wrap_ftruncate(int fd, off_t length)
> +int __wrap_rename(const char *old, const char *new)
>  {
> -	check_expected(length);
>  	return __set_errno(mock_type(int));
>  }
>  
> +int __wrap_mkstemp(char *template)
> +{
> +	return 10;
> +}
> +
>  int __wrap_dm_map_present(const char * str)
>  {
>  	check_expected(str);
> @@ -84,32 +95,6 @@ int __wrap_dm_get_uuid(const char *name, char *uuid, int uuid_len)
>  #define TEST_FDNO 1234
>  #define TEST_FPTR ((FILE *) 0xaffe)
>  
> -int __wrap_open_file(const char *file, int *can_write, const char *header)
> -{
> -	int cw = mock_type(int);
> -
> -        *can_write = cw;
> -	return TEST_FDNO;
> -}
> -
> -FILE *__wrap_fdopen(int fd, const char *mode)
> -{
> -	assert_int_equal(fd, TEST_FDNO);
> -	return TEST_FPTR;
> -}
> -
> -int __wrap_fflush(FILE *f)
> -{
> -	assert_ptr_equal(f, TEST_FPTR);
> -	return 0;
> -}
> -
> -int __wrap_fclose(FILE *f)
> -{
> -	assert_ptr_equal(f, TEST_FPTR);
> -	return 0;
> -}
> -
>  /* strbuf wrapper for the old format_devname() */
>  static int __format_devname(char *name, int id, size_t len, const char *prefix)
>  {
> @@ -466,22 +451,85 @@ static void mock_self_alias(const char *alias, const char *wwid)
>  		expect_condlog(3, USED_STR(alias, wwid));		\
>  	} while(0)
>  
> -static void mock_bindings_file(const char *content, int match_line)
> +static int add_binding_unsorted(Bindings *bindings,
> +				const char *alias, const char *wwid)
>  {
> -	static char cnt[1024];
> -	char *token;
> +	struct binding *bdg = calloc(1, sizeof(*bdg));
> +
> +	if (!bdg)
> +		return -1;
> +	bdg->wwid = strdup(wwid);
> +	bdg->alias = strdup(alias);
> +	if (!bdg->wwid || !bdg->alias || !vector_alloc_slot(bindings)) {
> +		free(bdg->alias);
> +		free(bdg->wwid);
> +		free(bdg);
> +		return BINDING_ERROR;
> +	}
> +	vector_set_slot(bindings, bdg);
> +	return BINDING_ADDED;
> +}
> +
> +static void __mock_bindings_file(const char *content,
> +				 int (*add)(Bindings *, const char *, const char *))
> +{
> +	char *cnt __attribute__((cleanup(cleanup_charp))) = NULL;
> +	char *token, *savep = NULL;
>  	int i;
>  
> -	assert_in_range(strlcpy(cnt, content, sizeof(cnt)), 0, sizeof(cnt) - 1);
> +	cnt = strdup(content);
> +	assert_ptr_not_equal(cnt, NULL);
>  
> -	for (token = strtok(cnt, "\n"), i = 0;
> +	for (token = strtok_r(cnt, "\n", &savep), i = 0;
>  	     token && *token;
> -	     token = strtok(NULL, "\n"), i++) {
> -		will_return(__wrap_fgets, token);
> -		if (match_line == i)
> -			return;
> +	     token = strtok_r(NULL, "\n", &savep), i++) {
> +		char *alias, *wwid;
> +		int rc;
> +
> +		if (read_binding(token, i + 1, &alias, &wwid)
> +		    == READ_BINDING_SKIP)
> +			continue;
> +
> +		rc = add(&global_bindings, alias, wwid);
> +		assert_int_equal(rc, BINDING_ADDED);
>  	}
> -	will_return(__wrap_fgets, NULL);
> +}
> +
> +static void mock_bindings_file(const char *content) {
> +	return __mock_bindings_file(content, add_binding);
> +}
> +
> +static void mock_bindings_file_unsorted(const char *content) {
> +	return __mock_bindings_file(content, add_binding_unsorted);
> +}
> +
> +static int teardown_bindings(void **state)
> +{
> +	cleanup_bindings();
> +	return 0;
> +}
> +
> +static int lookup_binding(FILE *dummy, const char *wwid, char **alias,
> +			  const char *prefix, int check_if_taken)
> +{
> +	const struct binding *bdg;
> +	int id;
> +
> +	/*
> +	 * get_free_id() always checks if aliases are taken.
> +	 * Therefore if prefix is non-null, check_if_taken must be true.
> +	 */
> +	assert_true(!prefix || check_if_taken);
> +	*alias = NULL;
> +	bdg = get_binding_for_wwid(&global_bindings, wwid);
> +	if (bdg) {
> +		*alias = strdup(bdg->alias);
> +		return 0;
> +	} else if (!prefix && check_if_taken)
> +		return -1;
> +
> +	id = get_free_id(&global_bindings, prefix, wwid);
> +	return id;
>  }
>  
>  static void lb_empty(void **state)
> @@ -489,7 +537,7 @@ static void lb_empty(void **state)
>  	int rc;
>  	char *alias;
>  
> -	mock_bindings_file("", -1);
> +	mock_bindings_file("");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, NULL, 0);
>  	assert_int_equal(rc, 1);
> @@ -501,7 +549,7 @@ static void lb_empty_unused(void **state)
>  	int rc;
>  	char *alias;
>  
> -	mock_bindings_file("", -1);
> +	mock_bindings_file("");
>  	mock_unused_alias("MPATHa");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
> @@ -515,10 +563,10 @@ static void lb_empty_failed(void **state)
>  	int rc;
>  	char *alias;
>  
> -	mock_bindings_file("", -1);
> +	mock_bindings_file("");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	mock_failed_alias("MPATHa", "WWID0");
>  	mock_unused_alias("MPATHb");
> -	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
> @@ -530,10 +578,10 @@ static void lb_empty_1_used(void **state)
>  	int rc;
>  	char *alias;
>  
> -	mock_bindings_file("", -1);
> +	mock_bindings_file("");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	mock_used_alias("MPATHa", "WWID0");
>  	mock_unused_alias("MPATHb");
> -	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
> @@ -545,10 +593,10 @@ static void lb_empty_1_used_self(void **state)
>  	int rc;
>  	char *alias;
>  
> -	mock_bindings_file("", -1);
> +	mock_bindings_file("");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	mock_used_alias("MPATHa", "WWID0");
>  	mock_self_alias("MPATHb", "WWID0");
> -	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
> @@ -560,9 +608,9 @@ static void lb_match_a(void **state)
>  	int rc;
>  	char *alias;
>  
> -	mock_bindings_file("MPATHa WWID0\n", 0);
> +	mock_bindings_file("MPATHa WWID0\n");
>  	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
> -	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 0);
> +	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 0);
>  	assert_ptr_not_equal(alias, NULL);
>  	assert_string_equal(alias, "MPATHa");
> @@ -574,9 +622,10 @@ static void lb_nomatch_a(void **state)
>  	int rc;
>  	char *alias;
>  
> -	mock_bindings_file("MPATHa WWID0\n", -1);
> +	mock_bindings_file("MPATHa WWID0\n");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
> -	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 0);
> +	mock_unused_alias("MPATHb");
> +	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
>  }
> @@ -586,8 +635,8 @@ static void lb_nomatch_a_bad_check(void **state)
>  	int rc;
>  	char *alias;
>  
> -	mock_bindings_file("MPATHa WWID0\n", -1);
> -	expect_condlog(0, NOMORE_STR);
> +	mock_bindings_file("MPATHa WWID0\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
>  	rc = lookup_binding(NULL, "WWID1", &alias, NULL, 1);
>  	assert_int_equal(rc, -1);
>  	assert_ptr_equal(alias, NULL);
> @@ -598,7 +647,7 @@ static void lb_nomatch_a_unused(void **state)
>  	int rc;
>  	char *alias;
>  
> -	mock_bindings_file("MPATHa WWID0\n", -1);
> +	mock_bindings_file("MPATHa WWID0\n");
>  	mock_unused_alias("MPATHb");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
>  	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
> @@ -611,27 +660,27 @@ static void lb_nomatch_a_3_used_failed_self(void **state)
>  	int rc;
>  	char *alias;
>  
> -	mock_bindings_file("MPATHa WWID0\n", -1);
> +	mock_bindings_file("MPATHa WWID0\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
>  	mock_used_alias("MPATHb", "WWID1");
>  	mock_used_alias("MPATHc", "WWID1");
>  	mock_used_alias("MPATHd", "WWID1");
>  	mock_failed_alias("MPATHe", "WWID1");
>  	mock_self_alias("MPATHf", "WWID1");
> -	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
>  	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 6);
>  	assert_ptr_equal(alias, NULL);
>  }
>  
> -static void do_lb_match_c(void **state, int check_if_taken)
> +static void do_lb_match_c(void **state)
>  {
>  	int rc;
>  	char *alias;
>  
>  	mock_bindings_file("MPATHa WWID0\n"
> -			   "MPATHc WWID1", 1);
> +			   "MPATHc WWID1");
>  	expect_condlog(3, FOUND_STR("MPATHc", "WWID1"));
> -	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", check_if_taken);
> +	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 0);
>  	assert_ptr_not_equal(alias, NULL);
>  	assert_string_equal(alias, "MPATHc");
> @@ -640,12 +689,12 @@ static void do_lb_match_c(void **state, int check_if_taken)
>  
>  static void lb_match_c(void **state)
>  {
> -	do_lb_match_c(state, 0);
> +	do_lb_match_c(state);
>  }
>  
>  static void lb_match_c_check(void **state)
>  {
> -	do_lb_match_c(state, 1);
> +	do_lb_match_c(state);
>  }
>  
>  static void lb_nomatch_a_c(void **state)
> @@ -654,9 +703,10 @@ static void lb_nomatch_a_c(void **state)
>  	char *alias;
>  
>  	mock_bindings_file("MPATHa WWID0\n"
> -			   "MPATHc WWID1", -1);
> +			   "MPATHc WWID1");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> -	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
> +	mock_unused_alias("MPATHb");
> +	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
>  }
> @@ -667,7 +717,7 @@ static void lb_nomatch_a_d_unused(void **state)
>  	char *alias;
>  
>  	mock_bindings_file("MPATHa WWID0\n"
> -			   "MPATHd WWID1", -1);
> +			   "MPATHd WWID1");
>  	mock_unused_alias("MPATHb");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> @@ -681,10 +731,10 @@ static void lb_nomatch_a_d_1_used(void **state)
>  	char *alias;
>  
>  	mock_bindings_file("MPATHa WWID0\n"
> -			   "MPATHd WWID1", -1);
> +			   "MPATHd WWID1");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	mock_used_alias("MPATHb", "WWID2");
>  	mock_unused_alias("MPATHc");
> -	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 3);
>  	assert_ptr_equal(alias, NULL);
> @@ -696,11 +746,11 @@ static void lb_nomatch_a_d_2_used(void **state)
>  	char *alias;
>  
>  	mock_bindings_file("MPATHa WWID0\n"
> -			   "MPATHd WWID1", -1);
> +			   "MPATHd WWID1");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	mock_used_alias("MPATHb", "WWID2");
>  	mock_used_alias("MPATHc", "WWID2");
>  	mock_unused_alias("MPATHe");
> -	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 5);
>  	assert_ptr_equal(alias, NULL);
> @@ -712,12 +762,12 @@ static void lb_nomatch_a_d_3_used(void **state)
>  	char *alias;
>  
>  	mock_bindings_file("MPATHa WWID0\n"
> -			   "MPATHd WWID1", -1);
> +			   "MPATHd WWID1");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	mock_used_alias("MPATHb", "WWID2");
>  	mock_used_alias("MPATHc", "WWID2");
>  	mock_used_alias("MPATHe", "WWID2");
>  	mock_unused_alias("MPATHf");
> -	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 6);
>  	assert_ptr_equal(alias, NULL);
> @@ -729,9 +779,10 @@ static void lb_nomatch_c_a(void **state)
>  	char *alias;
>  
>  	mock_bindings_file("MPATHc WWID1\n"
> -			   "MPATHa WWID0\n", -1);
> +			   "MPATHa WWID0\n");
> +	mock_unused_alias("MPATHb");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> -	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
> +	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
>  }
> @@ -743,7 +794,7 @@ static void lb_nomatch_d_a_unused(void **state)
>  
>  	mock_bindings_file("MPATHc WWID1\n"
>  			   "MPATHa WWID0\n"
> -			   "MPATHd WWID0\n", -1);
> +			   "MPATHd WWID0\n");
>  	mock_unused_alias("MPATHb");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> @@ -758,10 +809,10 @@ static void lb_nomatch_d_a_1_used(void **state)
>  
>  	mock_bindings_file("MPATHc WWID1\n"
>  			   "MPATHa WWID0\n"
> -			   "MPATHd WWID0\n", -1);
> +			   "MPATHd WWID0\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	mock_used_alias("MPATHb", "WWID2");
>  	mock_unused_alias("MPATHe");
> -	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 5);
>  	assert_ptr_equal(alias, NULL);
> @@ -774,9 +825,10 @@ static void lb_nomatch_a_b(void **state)
>  
>  	mock_bindings_file("MPATHa WWID0\n"
>  			   "MPATHz WWID26\n"
> -			   "MPATHb WWID1\n", -1);
> +			   "MPATHb WWID1\n");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> -	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
> +	mock_unused_alias("MPATHc");
> +	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 3);
>  	assert_ptr_equal(alias, NULL);
>  }
> @@ -786,13 +838,19 @@ static void lb_nomatch_a_b_bad(void **state)
>  	int rc;
>  	char *alias;
>  
> +	expect_condlog(1, "invalid line 3 in bindings file, missing WWID\n");
> +	/*
> +	 * The broken line will be ignored when constructing the bindings vector.
> +	 * Thus in lookup_binding() MPATHb is never encountered,
> +	 * and MPATHb appears usable.
> +	 */
>  	mock_bindings_file("MPATHa WWID0\n"
>  			   "MPATHz WWID26\n"
> -			   "MPATHb\n", -1);
> -	expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
> +			   "MPATHb\n");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> -	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
> -	assert_int_equal(rc, 3);
> +	mock_unused_alias("MPATHb");
> +	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> +	assert_int_equal(rc, 2);
>  	assert_ptr_equal(alias, NULL);
>  }
>  
> @@ -801,28 +859,140 @@ static void lb_nomatch_a_b_bad_self(void **state)
>  	int rc;
>  	char *alias;
>  
> +	expect_condlog(1, "invalid line 3 in bindings file, missing WWID\n");
>  	mock_bindings_file("MPATHa WWID0\n"
>  			   "MPATHz WWID26\n"
> -			   "MPATHb\n", -1);
> -	expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
> -	mock_self_alias("MPATHc", "WWID2");
> +			   "MPATHb\n");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> +	mock_self_alias("MPATHb", "WWID2");
> +	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> +	assert_int_equal(rc, 2);
> +	assert_ptr_equal(alias, NULL);
> +}
> +
> +static void lb_nomatch_b_z_a(void **state)
> +{
> +	int rc;
> +	char *alias;
> +
> +	/*
> +	 * add_bindings() sorts alphabetically. Therefore get_free_id()
> +	 * finds MPATHc as a free entry.
> +	 */
> +	mock_bindings_file("MPATHb WWID1\n"
> +			   "MPATHz WWID26\n"
> +			   "MPATHa WWID0\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> +	mock_unused_alias("MPATHc");
>  	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
>  	assert_int_equal(rc, 3);
>  	assert_ptr_equal(alias, NULL);
>  }
>  
> +static void lb_nomatch_b_aa_a(void **state)
> +{
> +	int rc;
> +	char *alias;
> +
> +	/*
> +	 * add_bindings() sorts alphabetically. ("a", "aa", b").
> +	 * The get_free_id() algorithm finds the "hole" after "b".
> +	 */
> +	mock_bindings_file("MPATHb WWID1\n"
> +			   "MPATHz WWID26\n"
> +			   "MPATHa WWID0\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> +	mock_unused_alias("MPATHc");
> +	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> +	assert_int_equal(rc, 3);
> +	assert_ptr_equal(alias, NULL);
> +}
> +
> +static void fill_bindings(struct strbuf *buf, int start, int end)
> +{
> +	int i;
> +
> +	for (i = start; i <= end; i++) {
> +		print_strbuf(buf,  "MPATH");
> +		format_devname(buf, i + 1);
> +		print_strbuf(buf,  " WWID%d\n", i);
> +	}
> +}
> +
> +static void lb_nomatch_b_a_aa(void **state)
> +{
> +	int rc;
> +	char *alias;
> +	STRBUF_ON_STACK(buf);
> +
> +	/*
> +	 * add_bindings() sorts alphabetically. ("a", "aa", "ab", "b", "c", ...)
> +	 * lookup_binding finds MPATHac as next free entry.
> +	 */
> +	fill_bindings(&buf, 0, 26);
> +	mock_bindings_file(get_strbuf_str(&buf));
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID28"));
> +	mock_unused_alias("MPATHab");
> +	rc = lookup_binding(NULL, "WWID28", &alias, "MPATH", 1);
> +	assert_int_equal(rc, 28);
> +	assert_ptr_equal(alias, NULL);
> +}
> +
> +static void lb_nomatch_b_a_aa_zz(void **state)
> +{
> +	int rc;
> +	char *alias;
> +	STRBUF_ON_STACK(buf);
> +
> +	/*
> +	 * add_bindings() sorts alphabetically. ("a", "aa", "ab", "b", "c", ...)
> +	 * lookup_binding finds MPATHaaa as next free entry, because MPATHaa is
> +	 * found before MPATHb, and MPATHzz was in the bindings, too.
> +	 */
> +	for (i = 0; i <= 26; i++) {
> +		print_strbuf(&buf,  "MPATH");
> +		format_devname(&buf, i + 1);
> +		print_strbuf(&buf,  " WWID%d\n", i);
> +	}
> +	print_strbuf(&buf, "MPATHzz WWID676\n");
> +	mock_bindings_file(get_strbuf_str(&buf));
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID703"));
> +	mock_unused_alias("MPATHaaa");
> +	rc = lookup_binding(NULL, "WWID703", &alias, "MPATH", 1);
> +	assert_int_equal(rc, 703);
> +	assert_ptr_equal(alias, NULL);
> +}
> +
> +static void lb_nomatch_b_z_a_unsorted(void **state)
> +{
> +	int rc;
> +	char *alias;
> +
> +	/*
> +	 * With unsorted bindings (shouldn't happen normally), get_free_id()
> +	 * plays safe and returns MPATHaa as first free entry.
> +	 */
> +	mock_bindings_file_unsorted("MPATHb WWID1\n"
> +				    "MPATHz WWID26\n"
> +				    "MPATHa WWID0\n");
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> +	mock_unused_alias("MPATHaa");
> +	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> +	assert_int_equal(rc, 27);
> +	assert_ptr_equal(alias, NULL);
> +}
> +
>  static void lb_nomatch_b_a(void **state)
>  {
>  	int rc;
>  	char *alias;
>  
>  	mock_bindings_file("MPATHb WWID1\n"
> -			   "MPATHz WWID26\n"
> -			   "MPATHa WWID0\n", -1);
> +			   "MPATHa WWID0\n");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> -	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
> -	assert_int_equal(rc, 27);
> +	mock_unused_alias("MPATHc");
> +	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> +	assert_int_equal(rc, 3);
>  	assert_ptr_equal(alias, NULL);
>  }
>  
> @@ -830,55 +1000,59 @@ static void lb_nomatch_b_a_3_used(void **state)
>  {
>  	int rc;
>  	char *alias;
> +	STRBUF_ON_STACK(buf);
>  
> -	mock_bindings_file("MPATHb WWID1\n"
> -			   "MPATHz WWID26\n"
> -			   "MPATHa WWID0\n", -1);
> -	mock_used_alias("MPATHaa", "WWID2");
> -	mock_used_alias("MPATHab", "WWID2");
> -	mock_used_alias("MPATHac", "WWID2");
> -	mock_unused_alias("MPATHad");
> -	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> -	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> -	assert_int_equal(rc, 30);
> +	fill_bindings(&buf, 0, 26);
> +	mock_bindings_file(get_strbuf_str(&buf));
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID31"));
> +	mock_used_alias("MPATHab", "WWID31");
> +	mock_used_alias("MPATHac", "WWID31");
> +	mock_used_alias("MPATHad", "WWID31");
> +	mock_unused_alias("MPATHae");
> +	rc = lookup_binding(NULL, "WWID31", &alias, "MPATH", 1);
> +	assert_int_equal(rc, 31);
>  	assert_ptr_equal(alias, NULL);
>  }
>  
>  #ifdef MPATH_ID_INT_MAX
> -static void do_lb_nomatch_int_max(void **state, int check_if_taken)
> +/*
> + * The bindings will be sorted by alias, alphabetically, which is not
> + * the same as the "numeric" sort order for user-friendly aliases.
> + * get_free_id() selects the highest used ID + 1 if an unsorted entry
> + * is encountered in the bindings table and it's id is equal to the
> + * next "expected" id. This happens if all IDs from "a" to "aa" are
> + * in the table. If the INT_MAX entry is in the table, too, it will
> + * overflow.
> + */
> +static void lb_nomatch_int_max(void **state)
>  {
>  	int rc;
>  	char *alias;
> +	STRBUF_ON_STACK(buf);
>  
> -	mock_bindings_file("MPATHb WWID1\n"
> -			   "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n"
> -			   "MPATHa WWID0\n", -1);
> +	fill_bindings(&buf, 0, 26);
> +	print_strbuf(&buf, "MPATH%s WWIDMAX\n", MPATH_ID_INT_MAX);
> +	mock_bindings_file(get_strbuf_str(&buf));
> +	expect_condlog(3, NOMATCH_WWID_STR("WWIDNOMORE"));
>  	expect_condlog(0, NOMORE_STR);
> -	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", check_if_taken);
> +	rc = lookup_binding(NULL, "WWIDNOMORE", &alias, "MPATH", 1);
>  	assert_int_equal(rc, -1);
>  	assert_ptr_equal(alias, NULL);
>  }
>  
> -static void lb_nomatch_int_max(void **state)
> -{
> -	do_lb_nomatch_int_max(state, 0);
> -}
> -
> -static void lb_nomatch_int_max_check(void **state)
> -{
> -	do_lb_nomatch_int_max(state, 1);
> -}
> -
>  static void lb_nomatch_int_max_used(void **state)
>  {
>  	int rc;
>  	char *alias;
> +	STRBUF_ON_STACK(buf);
>  
> -	mock_bindings_file("MPATHb WWID1\n"
> -			   "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n", -1);
> -	mock_used_alias("MPATHa", "WWID2");
> +	fill_bindings(&buf, 1, 26);
> +	print_strbuf(&buf, "MPATH%s WWIDMAX\n", MPATH_ID_INT_MAX);
> +	mock_bindings_file(get_strbuf_str(&buf));
> +	expect_condlog(3, NOMATCH_WWID_STR("WWIDNOMORE"));
> +	mock_used_alias("MPATHa", "WWIDNOMORE");
>  	expect_condlog(0, NOMORE_STR);
> -	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> +	rc = lookup_binding(NULL, "WWIDNOMORE", &alias, "MPATH", 1);
>  	assert_int_equal(rc, -1);
>  	assert_ptr_equal(alias, NULL);
>  }
> @@ -887,12 +1061,14 @@ static void lb_nomatch_int_max_m1(void **state)
>  {
>  	int rc;
>  	char *alias;
> +	STRBUF_ON_STACK(buf);
>  
> -	mock_bindings_file("MPATHb WWID1\n"
> -			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n"
> -			   "MPATHa WWID0\n", -1);
> -	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> -	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
> +	fill_bindings(&buf, 0, 26);
> +	print_strbuf(&buf, "MPATH%s WWIDMAXM1\n", MPATH_ID_INT_MAX_m1);
> +	mock_bindings_file(get_strbuf_str(&buf));
> +	expect_condlog(3, NOMATCH_WWID_STR("WWIDMAX"));
> +	mock_unused_alias("MPATH" MPATH_ID_INT_MAX);
> +	rc = lookup_binding(NULL, "WWIDMAX", &alias, "MPATH", 1);
>  	assert_int_equal(rc, INT_MAX);
>  	assert_ptr_equal(alias, NULL);
>  }
> @@ -901,13 +1077,15 @@ static void lb_nomatch_int_max_m1_used(void **state)
>  {
>  	int rc;
>  	char *alias;
> +	STRBUF_ON_STACK(buf);
>  
> -	mock_bindings_file("MPATHb WWID1\n"
> -			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n"
> -			   "MPATHa WWID0\n", -1);
> -	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWID2");
> +	fill_bindings(&buf, 0, 26);
> +	print_strbuf(&buf, "MPATH%s WWIDMAXM1\n", MPATH_ID_INT_MAX_m1);
> +	mock_bindings_file(get_strbuf_str(&buf));
> +	expect_condlog(3, NOMATCH_WWID_STR("WWIDMAX"));
> +	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWIDMAX");
>  	expect_condlog(0, NOMORE_STR);
> -	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> +	rc = lookup_binding(NULL, "WWIDMAX", &alias, "MPATH", 1);
>  	assert_int_equal(rc, -1);
>  	assert_ptr_equal(alias, NULL);
>  }
> @@ -916,13 +1094,15 @@ static void lb_nomatch_int_max_m1_1_used(void **state)
>  {
>  	int rc;
>  	char *alias;
> +	STRBUF_ON_STACK(buf);
>  
> -	mock_bindings_file("MPATHb WWID1\n"
> -			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n", -1);
> -	mock_used_alias("MPATHa", "WWID2");
> +	fill_bindings(&buf, 1, 26);
> +	print_strbuf(&buf, "MPATH%s WWIDMAXM1\n", MPATH_ID_INT_MAX_m1);
> +	mock_bindings_file(get_strbuf_str(&buf));
> +	expect_condlog(3, NOMATCH_WWID_STR("WWIDMAX"));
> +	mock_used_alias("MPATHa", "WWIDMAX");
>  	mock_unused_alias("MPATH" MPATH_ID_INT_MAX);
> -	expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
> -	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> +	rc = lookup_binding(NULL, "WWIDMAX", &alias, "MPATH", 1);
>  	assert_int_equal(rc, INT_MAX);
>  	assert_ptr_equal(alias, NULL);
>  }
> @@ -931,13 +1111,17 @@ static void lb_nomatch_int_max_m1_2_used(void **state)
>  {
>  	int rc;
>  	char *alias;
> +	STRBUF_ON_STACK(buf);
>  
> -	mock_bindings_file("MPATHb WWID1\n"
> -			   "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n", -1);
> -	mock_used_alias("MPATHa", "WWID2");
> -	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWID2");
> +	fill_bindings(&buf, 1, 26);
> +	print_strbuf(&buf, "MPATH%s WWIDMAXM1\n", MPATH_ID_INT_MAX_m1);
> +	mock_bindings_file(get_strbuf_str(&buf));
> +
> +	expect_condlog(3, NOMATCH_WWID_STR("WWIDMAX"));
> +	mock_used_alias("MPATHa", "WWIDMAX");
> +	mock_used_alias("MPATH" MPATH_ID_INT_MAX, "WWIDMAX");
>  	expect_condlog(0, NOMORE_STR);
> -	rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
> +	rc = lookup_binding(NULL, "WWIDMAX", &alias, "MPATH", 1);
>  	assert_int_equal(rc, -1);
>  	assert_ptr_equal(alias, NULL);
>  }
> @@ -946,52 +1130,68 @@ static void lb_nomatch_int_max_m1_2_used(void **state)
>  static int test_lookup_binding(void)
>  {
>  	const struct CMUnitTest tests[] = {
> -		cmocka_unit_test(lb_empty),
> -		cmocka_unit_test(lb_empty_unused),
> -		cmocka_unit_test(lb_empty_failed),
> -		cmocka_unit_test(lb_empty_1_used),
> -		cmocka_unit_test(lb_empty_1_used_self),
> -		cmocka_unit_test(lb_match_a),
> -		cmocka_unit_test(lb_nomatch_a),
> -		cmocka_unit_test(lb_nomatch_a_bad_check),
> -		cmocka_unit_test(lb_nomatch_a_unused),
> -		cmocka_unit_test(lb_nomatch_a_3_used_failed_self),
> -		cmocka_unit_test(lb_match_c),
> -		cmocka_unit_test(lb_match_c_check),
> -		cmocka_unit_test(lb_nomatch_a_c),
> -		cmocka_unit_test(lb_nomatch_a_d_unused),
> -		cmocka_unit_test(lb_nomatch_a_d_1_used),
> -		cmocka_unit_test(lb_nomatch_a_d_2_used),
> -		cmocka_unit_test(lb_nomatch_a_d_3_used),
> -		cmocka_unit_test(lb_nomatch_c_a),
> -		cmocka_unit_test(lb_nomatch_d_a_unused),
> -		cmocka_unit_test(lb_nomatch_d_a_1_used),
> -		cmocka_unit_test(lb_nomatch_a_b),
> -		cmocka_unit_test(lb_nomatch_a_b_bad),
> -		cmocka_unit_test(lb_nomatch_a_b_bad_self),
> -		cmocka_unit_test(lb_nomatch_b_a),
> -		cmocka_unit_test(lb_nomatch_b_a_3_used),
> +		cmocka_unit_test_teardown(lb_empty, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_empty_unused, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_empty_failed, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_empty_1_used, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_empty_1_used_self, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_match_a, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_a, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_a_bad_check, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_a_unused, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_a_3_used_failed_self, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_match_c, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_match_c_check, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_a_c, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_a_d_unused, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_a_d_1_used, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_a_d_2_used, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_a_d_3_used, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_c_a, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_d_a_unused, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_d_a_1_used, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_a_b, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_a_b_bad, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_a_b_bad_self, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_b_z_a, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_b_aa_a, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_b_a_aa, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_b_a_aa_zz, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_b_z_a_unsorted, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_b_a, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_b_a_3_used, teardown_bindings),
>  #ifdef MPATH_ID_INT_MAX
> -		cmocka_unit_test(lb_nomatch_int_max),
> -		cmocka_unit_test(lb_nomatch_int_max_check),
> -		cmocka_unit_test(lb_nomatch_int_max_used),
> -		cmocka_unit_test(lb_nomatch_int_max_m1),
> -		cmocka_unit_test(lb_nomatch_int_max_m1_used),
> -		cmocka_unit_test(lb_nomatch_int_max_m1_1_used),
> -		cmocka_unit_test(lb_nomatch_int_max_m1_2_used),
> +		cmocka_unit_test_teardown(lb_nomatch_int_max, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_int_max_used, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_int_max_m1, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_int_max_m1_used, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_int_max_m1_1_used, teardown_bindings),
> +		cmocka_unit_test_teardown(lb_nomatch_int_max_m1_2_used, teardown_bindings),
>  #endif
>  	};
>  
>  	return cmocka_run_group_tests(tests, NULL, NULL);
>  }
>  
> +static int rlookup_binding(FILE *dummy, char *buf, const char *alias) {
> +
> +	const struct binding *bdg;
> +
> +	bdg = get_binding_for_alias(&global_bindings, alias);
> +	if (!bdg) {
> +		return -1;
> +	}
> +	strlcpy(buf, bdg->wwid, WWID_SIZE);
> +	return 0;
> +}
> +
>  static void rl_empty(void **state)
>  {
>  	int rc;
>  	char buf[WWID_SIZE];
>  
>  	buf[0] = '\0';
> -	mock_bindings_file("", -1);
> +	mock_bindings_file("");
>  	expect_condlog(3, NOMATCH_STR("MPATHa"));
>  	rc = rlookup_binding(NULL, buf, "MPATHa");
>  	assert_int_equal(rc, -1);
> @@ -1004,7 +1204,7 @@ static void rl_match_a(void **state)
>  	char buf[WWID_SIZE];
>  
>  	buf[0] = '\0';
> -	mock_bindings_file("MPATHa WWID0\n", 0);
> +	mock_bindings_file("MPATHa WWID0\n");
>  	expect_condlog(3, FOUND_ALIAS_STR("MPATHa", "WWID0"));
>  	rc = rlookup_binding(NULL, buf, "MPATHa");
>  	assert_int_equal(rc, 0);
> @@ -1017,7 +1217,7 @@ static void rl_nomatch_a(void **state)
>  	char buf[WWID_SIZE];
>  
>  	buf[0] = '\0';
> -	mock_bindings_file("MPATHa WWID0\n", -1);
> +	mock_bindings_file("MPATHa WWID0\n");
>  	expect_condlog(3, NOMATCH_STR("MPATHb"));
>  	rc = rlookup_binding(NULL, buf, "MPATHb");
>  	assert_int_equal(rc, -1);
> @@ -1030,8 +1230,8 @@ static void rl_malformed_a(void **state)
>  	char buf[WWID_SIZE];
>  
>  	buf[0] = '\0';
> -	mock_bindings_file("MPATHa     \n", -1);
> -	expect_condlog(3, "Ignoring malformed line 1 in bindings file\n");
> +	expect_condlog(1, "invalid line 1 in bindings file, missing WWID\n");
> +	mock_bindings_file("MPATHa     \n");
>  	expect_condlog(3, NOMATCH_STR("MPATHa"));
>  	rc = rlookup_binding(NULL, buf, "MPATHa");
>  	assert_int_equal(rc, -1);
> @@ -1049,8 +1249,8 @@ static void rl_overlong_a(void **state)
>  	snprintf(line + sizeof(line) - 2, 2, "\n");
>  
>  	buf[0] = '\0';
> -	mock_bindings_file(line, -1);
>  	expect_condlog(3, "Ignoring too large wwid at 1 in bindings file\n");
> +	mock_bindings_file(line);
>  	expect_condlog(3, NOMATCH_STR("MPATHa"));
>  	rc = rlookup_binding(NULL, buf, "MPATHa");
>  	assert_int_equal(rc, -1);
> @@ -1065,7 +1265,7 @@ static void rl_match_b(void **state)
>  	buf[0] = '\0';
>  	mock_bindings_file("MPATHa WWID0\n"
>  			   "MPATHz WWID26\n"
> -			   "MPATHb WWID2\n", 2);
> +			   "MPATHb WWID2\n");
>  	expect_condlog(3, FOUND_ALIAS_STR("MPATHb", "WWID2"));
>  	rc = rlookup_binding(NULL, buf, "MPATHb");
>  	assert_int_equal(rc, 0);
> @@ -1075,31 +1275,41 @@ static void rl_match_b(void **state)
>  static int test_rlookup_binding(void)
>  {
>  	const struct CMUnitTest tests[] = {
> -		cmocka_unit_test(rl_empty),
> -		cmocka_unit_test(rl_match_a),
> -		cmocka_unit_test(rl_nomatch_a),
> -		cmocka_unit_test(rl_malformed_a),
> -		cmocka_unit_test(rl_overlong_a),
> -		cmocka_unit_test(rl_match_b),
> +		cmocka_unit_test_teardown(rl_empty, teardown_bindings),
> +		cmocka_unit_test_teardown(rl_match_a, teardown_bindings),
> +		cmocka_unit_test_teardown(rl_nomatch_a, teardown_bindings),
> +		cmocka_unit_test_teardown(rl_malformed_a, teardown_bindings),
> +		cmocka_unit_test_teardown(rl_overlong_a, teardown_bindings),
> +		cmocka_unit_test_teardown(rl_match_b, teardown_bindings),
>  	};
>  
>  	return cmocka_run_group_tests(tests, NULL, NULL);
>  }
>  
> +void check_bindings_size(int n)
> +{
> +	/* avoid -Waddress problem */
> +	Bindings *bindings = &global_bindings;
> +
> +	assert_int_equal(VECTOR_SIZE(bindings), n);
> +}
> +
>  static void al_a(void **state)
>  {
>  	static const char ln[] = "MPATHa WWIDa\n";
>  	char *alias;
>  
> -	will_return(__wrap_lseek, 0);
> -	expect_value(__wrap_write, count, strlen(ln));
> -	expect_string(__wrap_write, buf, ln);
> -	will_return(__wrap_write, strlen(ln));
> +	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
> +	will_return(__wrap_write, ln);
> +	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
> +	will_return(__wrap_rename, 0);
> +	expect_condlog(1, "updated bindings file foo");
>  	expect_condlog(3, NEW_STR("MPATHa", "WWIDa"));
>  
> -	alias = allocate_binding(0, "WWIDa", 1, "MPATH");
> +	alias = allocate_binding("foo", "WWIDa", 1, "MPATH");
>  	assert_ptr_not_equal(alias, NULL);
>  	assert_string_equal(alias, "MPATHa");
> +	check_bindings_size(1);
>  	free(alias);
>  }
>  
> @@ -1108,15 +1318,17 @@ static void al_zz(void **state)
>  	static const char ln[] = "MPATHzz WWIDzz\n";
>  	char *alias;
>  
> -	will_return(__wrap_lseek, 0);
> -	expect_value(__wrap_write, count, strlen(ln));
> -	expect_string(__wrap_write, buf, ln);
> -	will_return(__wrap_write, strlen(ln));
> +	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
> +	will_return(__wrap_write, ln);
> +	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
> +	will_return(__wrap_rename, 0);
> +	expect_condlog(1, "updated bindings file foo");
>  	expect_condlog(3, NEW_STR("MPATHzz", "WWIDzz"));
>  
> -	alias = allocate_binding(0, "WWIDzz", 26*26 + 26, "MPATH");
> +	alias = allocate_binding("foo", "WWIDzz", 26*26 + 26, "MPATH");
>  	assert_ptr_not_equal(alias, NULL);
>  	assert_string_equal(alias, "MPATHzz");
> +	check_bindings_size(1);
>  	free(alias);
>  }
>  
> @@ -1127,6 +1339,7 @@ static void al_0(void **state)
>  	expect_condlog(0, "allocate_binding: cannot allocate new binding for id 0\n");
>  	alias = allocate_binding(0, "WWIDa", 0, "MPATH");
>  	assert_ptr_equal(alias, NULL);
> +	check_bindings_size(0);
>  }
>  
>  static void al_m2(void **state)
> @@ -1136,71 +1349,137 @@ static void al_m2(void **state)
>  	expect_condlog(0, "allocate_binding: cannot allocate new binding for id -2\n");
>  	alias = allocate_binding(0, "WWIDa", -2, "MPATH");
>  	assert_ptr_equal(alias, NULL);
> +	check_bindings_size(0);
>  }
>  
> -static void al_lseek_err(void **state)
> +static void al_write_partial(void **state)
>  {
> +	static const char ln[] = "MPATHa WWIDa\n";
>  	char *alias;
>  
> -	will_return(__wrap_lseek, -ENODEV);
> -	expect_condlog(0, "Cannot seek to end of bindings file : No such device\n");
> -	alias = allocate_binding(0, "WWIDa", 1, "MPATH");
> +	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
> +	will_return(__wrap_write, ln);
> +	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln) - 1);
> +	expect_value(__wrap_write, count, 1);
> +	will_return(__wrap_write, ln + sizeof(ln) - 2);
> +	will_return(__wrap_write, 1);
> +	will_return(__wrap_rename, 0);
> +	expect_condlog(1, "updated bindings file foo");
> +	expect_condlog(3, "Created new binding [MPATHa] for WWID [WWIDa]\n");
> +
> +	alias = allocate_binding("foo", "WWIDa", 1, "MPATH");
> +	assert_ptr_not_equal(alias, NULL);
> +	assert_string_equal(alias, "MPATHa");
> +	check_bindings_size(1);
> +	free(alias);
> +}
> +
> +static void al_write_short(void **state)
> +{
> +	static const char ln[] = "MPATHa WWIDa\n";
> +	char *alias;
> +
> +	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
> +	will_return(__wrap_write, ln);
> +	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln) - 1);
> +	expect_value(__wrap_write, count, 1);
> +	will_return(__wrap_write, ln + sizeof(ln) - 2);
> +	will_return(__wrap_write, 0);
> +	expect_condlog(2, "write_bindings_file: short write");
> +	expect_condlog(1, "failed to write new bindings file");
> +	expect_condlog(1, "allocate_binding: deleting binding MPATHa for WWIDa");
> +
> +	alias = allocate_binding("foo", "WWIDa", 1, "MPATH");
>  	assert_ptr_equal(alias, NULL);
> +	check_bindings_size(0);
>  }
>  
>  static void al_write_err(void **state)
>  {
>  	static const char ln[] = "MPATHa WWIDa\n";
> -	const int offset = 20;
>  	char *alias;
>  
> -	will_return(__wrap_lseek, offset);
> -	expect_value(__wrap_write, count, strlen(ln));
> -	expect_string(__wrap_write, buf, ln);
> -	will_return(__wrap_write, strlen(ln) - 1);
> -	expect_value(__wrap_ftruncate, length, offset);
> -	will_return(__wrap_ftruncate, 0);
> -	expect_condlog(0, "Cannot write binding to bindings file :");
> +	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
> +	will_return(__wrap_write, ln);
> +	will_return(__wrap_write, -EPERM);
> +	expect_condlog(1, "failed to write new bindings file");
> +	expect_condlog(1, "allocate_binding: deleting binding MPATHa for WWIDa");
>  
> -	alias = allocate_binding(0, "WWIDa", 1, "MPATH");
> +	alias = allocate_binding("foo", "WWIDa", 1, "MPATH");
>  	assert_ptr_equal(alias, NULL);
> +	check_bindings_size(0);
> +}
> +
> +static void al_rename_err(void **state)
> +{
> +	static const char ln[] = "MPATHa WWIDa\n";
> +	char *alias;
> +
> +	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
> +	will_return(__wrap_write, ln);
> +	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
> +	will_return(__wrap_rename, -EROFS);
> +
> +	expect_condlog(0, "update_bindings_file: rename: Read-only file system");
> +	expect_condlog(1, "allocate_binding: deleting binding MPATHa for WWIDa");
> +	alias = allocate_binding("foo", "WWIDa", 1, "MPATH");
> +	assert_ptr_equal(alias, NULL);
> +	check_bindings_size(0);
>  }
>  
>  static int test_allocate_binding(void)
>  {
>  	const struct CMUnitTest tests[] = {
> -		cmocka_unit_test(al_a),
> -		cmocka_unit_test(al_zz),
> -		cmocka_unit_test(al_0),
> -		cmocka_unit_test(al_m2),
> -		cmocka_unit_test(al_lseek_err),
> -		cmocka_unit_test(al_write_err),
> +		cmocka_unit_test_teardown(al_a, teardown_bindings),
> +		cmocka_unit_test_teardown(al_zz, teardown_bindings),
> +		cmocka_unit_test_teardown(al_0, teardown_bindings),
> +		cmocka_unit_test_teardown(al_m2, teardown_bindings),
> +		cmocka_unit_test_teardown(al_write_partial, teardown_bindings),
> +		cmocka_unit_test_teardown(al_write_short, teardown_bindings),
> +		cmocka_unit_test_teardown(al_write_err, teardown_bindings),
> +		cmocka_unit_test_teardown(al_rename_err, teardown_bindings),
>  	};
>  
>  	return cmocka_run_group_tests(tests, NULL, NULL);
>  }
>  
> -#define mock_allocate_binding(alias, wwid)				\
> +#define mock_allocate_binding_err_len(alias, wwid, len, err, msg)	\
>  	do {								\
>  		static const char ln[] = BINDING_STR(alias, wwid);	\
>  									\
> -		will_return(__wrap_lseek, 0);				\
> -		expect_value(__wrap_write, count, strlen(ln));		\
> -		expect_string(__wrap_write, buf, ln);			\
> -		will_return(__wrap_write, strlen(ln));			\
> -		expect_condlog(3, NEW_STR(alias, wwid));		\
> +		expect_value(__wrap_write, count,			\
> +			     strlen(BINDINGS_FILE_HEADER) + (len) + strlen(ln)); \
> +		will_return(__wrap_write, ln);				\
> +		will_return(__wrap_write,				\
> +			    strlen(BINDINGS_FILE_HEADER) + (len) + strlen(ln)); \
> +		will_return(__wrap_rename, err);			\
> +		if (err == 0) {						\
> +			expect_condlog(1, "updated bindings file x\n");	\
> +			expect_condlog(3, NEW_STR(alias, wwid));	\
> +		} else {						\
> +			expect_condlog(0, "update_bindings_file: rename: " msg "\n"); \
> +			expect_condlog(1, "allocate_binding: deleting binding "	\
> +				       alias " for " wwid "\n");	\
> +		}							\
>  	} while (0)
>  
> +#define mock_allocate_binding_err(alias, wwid, err, msg)	\
> +	mock_allocate_binding_err_len(alias, wwid, 0, err, msg)
> +
> +#define mock_allocate_binding(alias, wwid)			\
> +	mock_allocate_binding_err(alias, wwid, 0, "")
> +
> +#define mock_allocate_binding_len(alias, wwid, len)			\
> +	mock_allocate_binding_err_len(alias, wwid, len, 0, "")
> +
>  static void gufa_empty_new_rw(void **state) {
>  	char *alias;
>  
> -	will_return(__wrap_open_file, true);
> -
> -	mock_bindings_file("", -1);
> +	mock_bindings_file("");
>  	mock_unused_alias("MPATHa");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> -
>  	mock_allocate_binding("MPATHa", "WWID0");
> +
>  	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", false);
>  	assert_string_equal(alias, "MPATHa");
>  	free(alias);
> @@ -1208,10 +1487,11 @@ static void gufa_empty_new_rw(void **state) {
>  
>  static void gufa_empty_new_ro_1(void **state) {
>  	char *alias;
> -	will_return(__wrap_open_file, false);
> -	mock_bindings_file("", -1);
> +
> +	mock_bindings_file("");
>  	mock_unused_alias("MPATHa");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +	mock_allocate_binding_err("MPATHa", "WWID0", -EROFS, "Read-only file system");
>  
>  	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", false);
>  	assert_ptr_equal(alias, NULL);
> @@ -1220,11 +1500,9 @@ static void gufa_empty_new_ro_1(void **state) {
>  static void gufa_empty_new_ro_2(void **state) {
>  	char *alias;
>  
> -	will_return(__wrap_open_file, true);
> -
> -	mock_bindings_file("", -1);
> -	mock_unused_alias("MPATHa");
> +	mock_bindings_file("");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +	mock_unused_alias("MPATHa");
>  
>  	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
>  	assert_ptr_equal(alias, NULL);
> @@ -1233,11 +1511,10 @@ static void gufa_empty_new_ro_2(void **state) {
>  static void gufa_match_a_unused(void **state) {
>  	char *alias;
>  
> -	will_return(__wrap_open_file, true);
> -
> -	mock_bindings_file("MPATHa WWID0", 0);
> +	mock_bindings_file("MPATHa WWID0");
>  	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
>  	mock_unused_alias("MPATHa");
> +	expect_condlog(3, EXISTING_STR("MPATHa", "WWID0"));
>  
>  	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
>  	assert_string_equal(alias, "MPATHa");
> @@ -1247,11 +1524,10 @@ static void gufa_match_a_unused(void **state) {
>  static void gufa_match_a_self(void **state) {
>  	char *alias;
>  
> -	will_return(__wrap_open_file, true);
> -
> -	mock_bindings_file("MPATHa WWID0", 0);
> +	mock_bindings_file("MPATHa WWID0");
>  	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
>  	mock_self_alias("MPATHa", "WWID0");
> +	expect_condlog(3, EXISTING_STR("MPATHa", "WWID0"));
>  
>  	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", true);
>  	assert_string_equal(alias, "MPATHa");
> @@ -1261,9 +1537,8 @@ static void gufa_match_a_self(void **state) {
>  static void gufa_match_a_used(void **state) {
>  	char *alias;
>  
> -	will_return(__wrap_open_file, true);
>  
> -	mock_bindings_file("MPATHa WWID0", 0);
> +	mock_bindings_file("MPATHa WWID0");
>  	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
>  	mock_used_alias("MPATHa", "WWID0");
>  
> @@ -1273,15 +1548,14 @@ static void gufa_match_a_used(void **state) {
>  
>  static void gufa_nomatch_a_c(void **state) {
>  	char *alias;
> -	will_return(__wrap_open_file, true);
> +	static const char bindings[] = ("MPATHa WWID0\n"
> +					"MPATHc WWID2\n");
>  
> -	mock_bindings_file("MPATHa WWID0\n"
> -			   "MPATHc WWID2",
> -			   -1);
> +	mock_bindings_file(bindings);
>  	mock_unused_alias("MPATHb");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
>  
> -	mock_allocate_binding("MPATHb", "WWID1");
> +	mock_allocate_binding_len("MPATHb", "WWID1", strlen(bindings));
>  
>  	alias = get_user_friendly_alias("WWID1", "x", "", "MPATH", false);
>  	assert_string_equal(alias, "MPATHb");
> @@ -1290,15 +1564,14 @@ static void gufa_nomatch_a_c(void **state) {
>  
>  static void gufa_nomatch_c_a(void **state) {
>  	char *alias;
> -	will_return(__wrap_open_file, true);
> +	const char bindings[] = ("MPATHc WWID2\n"
> +				 "MPATHa WWID0\n");
>  
> -	mock_bindings_file("MPATHc WWID2\n"
> -			   "MPATHa WWID0",
> -			   -1);
> +	mock_bindings_file(bindings);
>  	mock_unused_alias("MPATHb");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
>  
> -	mock_allocate_binding("MPATHb", "WWID1");
> +	mock_allocate_binding_len("MPATHb", "WWID1", sizeof(bindings) - 1);
>  
>  	alias = get_user_friendly_alias("WWID1", "x", "", "MPATH", false);
>  	assert_string_equal(alias, "MPATHb");
> @@ -1307,15 +1580,14 @@ static void gufa_nomatch_c_a(void **state) {
>  
>  static void gufa_nomatch_c_b(void **state) {
>  	char *alias;
> -	will_return(__wrap_open_file, true);
> +	const char bindings[] = ("MPATHc WWID2\n"
> +				 "MPATHb WWID1\n");
>  
> -	mock_bindings_file("MPATHc WWID2\n"
> -			   "MPATHb WWID1\n",
> -			   -1);
> -	mock_unused_alias("MPATHa");
> +	mock_bindings_file(bindings);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +	mock_unused_alias("MPATHa");
>  
> -	mock_allocate_binding("MPATHa", "WWID0");
> +	mock_allocate_binding_len("MPATHa", "WWID0", sizeof(bindings) - 1);
>  
>  	alias = get_user_friendly_alias("WWID0", "x", "", "MPATH", false);
>  	assert_string_equal(alias, "MPATHa");
> @@ -1324,16 +1596,15 @@ static void gufa_nomatch_c_b(void **state) {
>  
>  static void gufa_nomatch_c_b_used(void **state) {
>  	char *alias;
> -	will_return(__wrap_open_file, true);
> +	const char bindings[] = ("MPATHc WWID2\n"
> +				 "MPATHb WWID1\n");
>  
> -	mock_bindings_file("MPATHc WWID2\n"
> -			   "MPATHb WWID1",
> -			   -1);
> -	mock_used_alias("MPATHa", "WWID4");
> +	mock_bindings_file(bindings);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID4"));
> +	mock_used_alias("MPATHa", "WWID4");
>  	mock_unused_alias("MPATHd");
>  
> -	mock_allocate_binding("MPATHd", "WWID4");
> +	mock_allocate_binding_len("MPATHd", "WWID4", sizeof(bindings) - 1);
>  
>  	alias = get_user_friendly_alias("WWID4", "x", "", "MPATH", false);
>  	assert_string_equal(alias, "MPATHd");
> @@ -1342,34 +1613,60 @@ static void gufa_nomatch_c_b_used(void **state) {
>  
>  static void gufa_nomatch_b_f_a(void **state) {
>  	char *alias;
> -	will_return(__wrap_open_file, true);
> +	const char bindings[] = ("MPATHb WWID1\n"
> +				 "MPATHf WWID6\n"
> +				 "MPATHa WWID0\n");
>  
> -	mock_bindings_file("MPATHb WWID1\n"
> -			   "MPATHf WWID6\n"
> -			   "MPATHa WWID0\n",
> -			   -1);
> +	mock_bindings_file_unsorted(bindings);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID7"));
>  	mock_unused_alias("MPATHg");
>  
> -	mock_allocate_binding("MPATHg", "WWID7");
> +	mock_allocate_binding_len("MPATHg", "WWID7", sizeof(bindings) - 1);
>  
>  	alias = get_user_friendly_alias("WWID7", "x", "", "MPATH", false);
>  	assert_string_equal(alias, "MPATHg");
>  	free(alias);
>  }
>  
> +static void gufa_nomatch_b_aa_a(void **state) {
> +	char *alias;
> +	STRBUF_ON_STACK(buf);
> +
> +	fill_bindings(&buf, 0, 26);
> +	mock_bindings_file(get_strbuf_str(&buf));
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID28"));
> +	mock_unused_alias("MPATHab");
> +	mock_allocate_binding_len("MPATHab", "WWID28", get_strbuf_len(&buf));
> +
> +	alias = get_user_friendly_alias("WWID28", "x", "", "MPATH", false);
> +	assert_string_equal(alias, "MPATHab");
> +	free(alias);
> +}
> +
> +static void gufa_nomatch_b_f_a_sorted(void **state) {
> +	char *alias;
> +	const char bindings[] = ("MPATHb WWID1\n"
> +				 "MPATHf WWID6\n"
> +				 "MPATHa WWID0\n");
> +
> +	mock_bindings_file(bindings);
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID7"));
> +	mock_unused_alias("MPATHc");
> +
> +	mock_allocate_binding_len("MPATHc", "WWID7", sizeof(bindings) - 1);
> +
> +	alias = get_user_friendly_alias("WWID7", "x", "", "MPATH", false);
> +	assert_string_equal(alias, "MPATHc");
> +	free(alias);
> +}
> +
>  static void gufa_old_empty(void **state) {
>  	char *alias;
> -	will_return(__wrap_open_file, true);
>  
>  	/* rlookup_binding for ALIAS */
> -	mock_bindings_file("", -1);
> +	mock_bindings_file("");
>  	expect_condlog(3, NOMATCH_STR("MPATHz"));
> -
> -	/* lookup_binding */
> -	mock_bindings_file("", -1);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> -
>  	mock_unused_alias("MPATHz");
>  
>  	mock_allocate_binding("MPATHz", "WWID0");
> @@ -1382,12 +1679,11 @@ static void gufa_old_empty(void **state) {
>  
>  static void gufa_old_empty_self(void **state) {
>  	char *alias;
> -	will_return(__wrap_open_file, true);
>  
> -	mock_bindings_file("", -1);
> +	mock_bindings_file("");
>  	expect_condlog(3, NOMATCH_STR("MPATHz"));
>  
> -	mock_bindings_file("", -1);
> +	mock_bindings_file("");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	mock_self_alias("MPATHz", "WWID0");
>  
> @@ -1401,15 +1697,14 @@ static void gufa_old_empty_self(void **state) {
>  
>  static void gufa_old_empty_used(void **state) {
>  	char *alias;
> -	will_return(__wrap_open_file, true);
>  
> -	mock_bindings_file("", -1);
> +	mock_bindings_file("");
>  	expect_condlog(3, NOMATCH_STR("MPATHz"));
>  
> -	mock_bindings_file("", -1);
> -	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +	mock_bindings_file("");
>  	mock_used_alias("MPATHz", "WWID0");
>  	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  
>  	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
>  	assert_ptr_equal(alias, NULL);
> @@ -1417,11 +1712,9 @@ static void gufa_old_empty_used(void **state) {
>  
>  static void gufa_old_match(void **state) {
>  	char *alias;
> -	will_return(__wrap_open_file, true);
>  
>  	mock_bindings_file("MPATHb WWID1\n"
> -			   "MPATHz WWID0",
> -			   1);
> +			   "MPATHz WWID0");
>  	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
>  	mock_unused_alias("MPATHz");
>  
> @@ -1432,9 +1725,8 @@ static void gufa_old_match(void **state) {
>  
>  static void gufa_old_match_self(void **state) {
>  	char *alias;
> -	will_return(__wrap_open_file, true);
>  
> -	mock_bindings_file("MPATHz WWID0", 0);
> +	mock_bindings_file("MPATHz WWID0");
>  	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
>  	mock_self_alias("MPATHz", "WWID0");
>  
> @@ -1445,9 +1737,8 @@ static void gufa_old_match_self(void **state) {
>  
>  static void gufa_old_match_used(void **state) {
>  	char *alias;
> -	will_return(__wrap_open_file, true);
>  
> -	mock_bindings_file("MPATHz WWID0", 0);
> +	mock_bindings_file("MPATHz WWID0");
>  	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
>  	mock_used_alias("MPATHz", "WWID0");
>  	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
> @@ -1458,19 +1749,15 @@ static void gufa_old_match_used(void **state) {
>  
>  static void gufa_old_match_other(void **state) {
>  	char *alias;
> -	static const char bindings[] = "MPATHz WWID9";
> +	static const char bindings[] = "MPATHz WWID9\n";
>  
> -	will_return(__wrap_open_file, true);
> -
> -	mock_bindings_file(bindings, 0);
> +	mock_bindings_file(bindings);
>  	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
>  	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
> -
> -	mock_bindings_file(bindings, -1);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	mock_unused_alias("MPATHa");
>  
> -	mock_allocate_binding("MPATHa", "WWID0");
> +	mock_allocate_binding_len("MPATHa", "WWID0", sizeof(bindings) - 1);
>  
>  	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
>  	assert_string_equal(alias, "MPATHa");
> @@ -1479,21 +1766,16 @@ static void gufa_old_match_other(void **state) {
>  
>  static void gufa_old_match_other_used(void **state) {
>  	char *alias;
> -	static const char bindings[] = "MPATHz WWID9";
> +	static const char bindings[] = "MPATHz WWID9\n";
>  
> -	will_return(__wrap_open_file, true);
> -
> -	mock_bindings_file(bindings, 0);
> +	mock_bindings_file(bindings);
>  	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
>  	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
> -
> -	mock_bindings_file(bindings, -1);
> -	mock_used_alias("MPATHa", "WWID0");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> +	mock_used_alias("MPATHa", "WWID0");
>  	mock_unused_alias("MPATHb");
>  
> -	mock_allocate_binding("MPATHb", "WWID0");
> -
> +	mock_allocate_binding_len("MPATHb", "WWID0", sizeof(bindings) - 1);
>  	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
>  	assert_string_equal(alias, "MPATHb");
>  	free(alias);
> @@ -1503,15 +1785,13 @@ static void gufa_old_match_other_wwidmatch(void **state) {
>  	char *alias;
>  	static const char bindings[] = ("MPATHz WWID9\n"
>  					"MPATHc WWID2");
> -	will_return(__wrap_open_file, true);
>  
> -	mock_bindings_file(bindings, 0);
> +	mock_bindings_file(bindings);
>  	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
>  	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
> -
> -	mock_bindings_file(bindings, 1);
>  	expect_condlog(3, FOUND_STR("MPATHc", "WWID2"));
>  	mock_unused_alias("MPATHc");
> +	expect_condlog(3, EXISTING_STR("MPATHc", "WWID2"));
>  
>  	alias = get_user_friendly_alias("WWID2", "x", "MPATHz", "MPATH", false);
>  	assert_string_equal(alias, "MPATHc");
> @@ -1523,13 +1803,9 @@ static void gufa_old_match_other_wwidmatch_used(void **state) {
>  	static const char bindings[] = ("MPATHz WWID9\n"
>  					"MPATHc WWID2");
>  
> -	will_return(__wrap_open_file, true);
> -
> -	mock_bindings_file(bindings, 0);
> +	mock_bindings_file(bindings);
>  	expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
>  	expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
> -
> -	mock_bindings_file(bindings, 1);
>  	expect_condlog(3, FOUND_STR("MPATHc", "WWID2"));
>  	mock_used_alias("MPATHc", "WWID2");
>  
> @@ -1541,12 +1817,9 @@ static void gufa_old_nomatch_wwidmatch(void **state) {
>  	char *alias;
>  	static const char bindings[] = "MPATHa WWID0";
>  
> -	will_return(__wrap_open_file, true);
> -
> -	mock_bindings_file(bindings, -1);
> +	mock_bindings_file(bindings);
> +	mock_unused_alias("MPATHz");
>  	expect_condlog(3, NOMATCH_STR("MPATHz"));
> -
> -	mock_bindings_file(bindings, 0);
>  	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
>  	mock_unused_alias("MPATHa");
>  	expect_condlog(3, EXISTING_STR("MPATHa", "WWID0"));
> @@ -1559,12 +1832,10 @@ static void gufa_old_nomatch_wwidmatch(void **state) {
>  static void gufa_old_nomatch_wwidmatch_used(void **state) {
>  	char *alias;
>  	static const char bindings[] = "MPATHa WWID0";
> -	will_return(__wrap_open_file, true);
>  
> -	mock_bindings_file(bindings, -1);
> +	mock_bindings_file(bindings);
> +	mock_unused_alias("MPATHz");
>  	expect_condlog(3, NOMATCH_STR("MPATHz"));
> -
> -	mock_bindings_file(bindings, 0);
>  	expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
>  	mock_used_alias("MPATHa", "WWID0");
>  
> @@ -1574,19 +1845,14 @@ static void gufa_old_nomatch_wwidmatch_used(void **state) {
>  
>  static void gufa_old_nomatch_nowwidmatch(void **state) {
>  	char *alias;
> -	static const char bindings[] = "MPATHb WWID1";
> +	static const char bindings[] = "MPATHb WWID1\n";
>  
> -	will_return(__wrap_open_file, true);
> -
> -	mock_bindings_file(bindings, -1);
> +	mock_bindings_file(bindings);
>  	expect_condlog(3, NOMATCH_STR("MPATHz"));
> -
> -	mock_bindings_file(bindings, -1);
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> -
>  	mock_unused_alias("MPATHz");
>  
> -	mock_allocate_binding("MPATHz", "WWID0");
> +	mock_allocate_binding_len("MPATHz", "WWID0", sizeof(bindings) - 1);
>  	expect_condlog(2, ALLOC_STR("MPATHz", "WWID0"));
>  
>  	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
> @@ -1598,16 +1864,12 @@ static void gufa_old_nomatch_nowwidmatch_used(void **state) {
>  	char *alias;
>  	static const char bindings[] = "MPATHb WWID1";
>  
> -	will_return(__wrap_open_file, true);
> -
> -	mock_bindings_file(bindings, -1);
> +	mock_bindings_file(bindings);
>  	expect_condlog(3, NOMATCH_STR("MPATHz"));
>  
> -	mock_bindings_file(bindings, -1);
> -	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> -
>  	mock_used_alias("MPATHz", "WWID0");
>  	expect_condlog(0, ERR_STR("MPATHz", "WWID0"));
> +	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  
>  	alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false);
>  	assert_ptr_equal(alias, NULL);
> @@ -1616,31 +1878,33 @@ static void gufa_old_nomatch_nowwidmatch_used(void **state) {
>  static int test_get_user_friendly_alias()
>  {
>  	const struct CMUnitTest tests[] = {
> -		cmocka_unit_test(gufa_empty_new_rw),
> -		cmocka_unit_test(gufa_empty_new_ro_1),
> -		cmocka_unit_test(gufa_empty_new_ro_2),
> -		cmocka_unit_test(gufa_match_a_unused),
> -		cmocka_unit_test(gufa_match_a_self),
> -		cmocka_unit_test(gufa_match_a_used),
> -		cmocka_unit_test(gufa_nomatch_a_c),
> -		cmocka_unit_test(gufa_nomatch_c_a),
> -		cmocka_unit_test(gufa_nomatch_c_b),
> -		cmocka_unit_test(gufa_nomatch_c_b_used),
> -		cmocka_unit_test(gufa_nomatch_b_f_a),
> -		cmocka_unit_test(gufa_old_empty),
> -		cmocka_unit_test(gufa_old_empty_self),
> -		cmocka_unit_test(gufa_old_empty_used),
> -		cmocka_unit_test(gufa_old_match),
> -		cmocka_unit_test(gufa_old_match_self),
> -		cmocka_unit_test(gufa_old_match_used),
> -		cmocka_unit_test(gufa_old_match_other),
> -		cmocka_unit_test(gufa_old_match_other_used),
> -		cmocka_unit_test(gufa_old_match_other_wwidmatch),
> -		cmocka_unit_test(gufa_old_match_other_wwidmatch_used),
> -		cmocka_unit_test(gufa_old_nomatch_wwidmatch),
> -		cmocka_unit_test(gufa_old_nomatch_wwidmatch_used),
> -		cmocka_unit_test(gufa_old_nomatch_nowwidmatch),
> -		cmocka_unit_test(gufa_old_nomatch_nowwidmatch_used),
> +		cmocka_unit_test_teardown(gufa_empty_new_rw, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_empty_new_ro_1, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_empty_new_ro_2, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_match_a_unused, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_match_a_self, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_match_a_used, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_nomatch_a_c, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_nomatch_c_a, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_nomatch_c_b, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_nomatch_c_b_used, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_nomatch_b_f_a, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_nomatch_b_aa_a, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_nomatch_b_f_a_sorted, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_old_empty, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_old_empty_self, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_old_empty_used, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_old_match, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_old_match_self, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_old_match_used, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_old_match_other, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_old_match_other_used, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_old_match_other_wwidmatch, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_old_match_other_wwidmatch_used, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_old_nomatch_wwidmatch, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_old_nomatch_wwidmatch_used, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_old_nomatch_nowwidmatch, teardown_bindings),
> +		cmocka_unit_test_teardown(gufa_old_nomatch_nowwidmatch_used, teardown_bindings),
>  	};
>  
>  	return cmocka_run_group_tests(tests, NULL, NULL);
> @@ -1656,7 +1920,6 @@ int main(void)
>  	ret += test_lookup_binding();
>  	ret += test_rlookup_binding();
>  	ret += test_allocate_binding();
> -	ret += test_allocate_binding();
>  	ret += test_get_user_friendly_alias();
>  
>  	return ret;
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 20/21] libmultipath: dm_get_uuid(): return emtpy UUID for non-existing maps
  2023-09-01 18:02 ` [dm-devel] [PATCH 20/21] libmultipath: dm_get_uuid(): return emtpy UUID for non-existing maps mwilck
@ 2023-09-06 22:47   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:47 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:33PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> libdevmapper will most probably not return a UUID for non-existing
> maps anyway. But it's cheap to double-check here.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/devmapper.c | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
> index 248c373..9be82f4 100644
> --- a/libmultipath/devmapper.c
> +++ b/libmultipath/devmapper.c
> @@ -706,12 +706,16 @@ dm_get_prefixed_uuid(const char *name, char *uuid, int uuid_len)
>  {
>  	struct dm_task *dmt;
>  	const char *uuidtmp;
> +	struct dm_info info;
>  	int r = 1;
>  
>  	dmt = libmp_dm_task_create(DM_DEVICE_INFO);
>  	if (!dmt)
>  		return 1;
>  
> +	if (uuid_len > 0)
> +		uuid[0] = '\0';
> +
>  	if (!dm_task_set_name (dmt, name))
>  		goto uuidout;
>  
> @@ -720,11 +724,13 @@ dm_get_prefixed_uuid(const char *name, char *uuid, int uuid_len)
>  		goto uuidout;
>  	}
>  
> +	if (!dm_task_get_info(dmt, &info) ||
> +	    !info.exists)
> +		goto uuidout;
> +
>  	uuidtmp = dm_task_get_uuid(dmt);
>  	if (uuidtmp)
>  		strlcpy(uuid, uuidtmp, uuid_len);
> -	else
> -		uuid[0] = '\0';
>  
>  	r = 0;
>  uuidout:
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 21/21] libmultipath: adapt to new semantics of dm_get_uuid()
  2023-09-01 18:02 ` [dm-devel] [PATCH 21/21] libmultipath: adapt to new semantics of dm_get_uuid() mwilck
@ 2023-09-06 22:47   ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-06 22:47 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Fri, Sep 01, 2023 at 08:02:34PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> dm_get_uuid() will return 1 for non-existing maps. Thus we don't need
> to call dm_map_present() any more in alias_already_taken(). This changes
> our semantics: previously we'd avoid using an alias for which dm_get_uuid()
> had failed. Now we treat failure in dm_get_uuid() as indication that the
> map doesn't exist. This is not dangerous because dm_task_get_uuid() cannot
> fail, and thus the modified dm_get_uuid() will fail if and only if
> dm_map_present() would return false.
> 
> This makes the "failed alias" test mostly obsolete, as "failed" is now
> treated as "unused".
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/alias.c | 23 ++++++++++++-----------
>  tests/alias.c        | 30 ++++++------------------------
>  2 files changed, 18 insertions(+), 35 deletions(-)
> 
> diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> index 6003df0..0f5af17 100644
> --- a/libmultipath/alias.c
> +++ b/libmultipath/alias.c
> @@ -295,18 +295,19 @@ scan_devname(const char *alias, const char *prefix)
>  static bool alias_already_taken(const char *alias, const char *map_wwid)
>  {
>  
> -	if (dm_map_present(alias)) {
> -		char wwid[WWID_SIZE];
> +	char wwid[WWID_SIZE];
>  
> -		/* If both the name and the wwid match, then it's fine.*/
> -		if (dm_get_uuid(alias, wwid, sizeof(wwid)) == 0 &&
> -		    strncmp(map_wwid, wwid, sizeof(wwid)) == 0)
> -			return false;
> -		condlog(3, "%s: alias '%s' already taken, reselecting alias",
> -			map_wwid, alias);
> -		return true;
> -	}
> -	return false;
> +	/* If the map doesn't exist, it's fine */
> +	if (dm_get_uuid(alias, wwid, sizeof(wwid)) != 0)
> +		return false;
> +
> +	/* If both the name and the wwid match, it's fine.*/
> +	if (strncmp(map_wwid, wwid, sizeof(wwid)) == 0)
> +		return false;
> +
> +	condlog(3, "%s: alias '%s' already taken, reselecting alias",
> +		map_wwid, alias);
> +	return true;
>  }
>  
>  static bool id_already_taken(int id, const char *prefix, const char *map_wwid)
> diff --git a/tests/alias.c b/tests/alias.c
> index 4ac6425..4f54031 100644
> --- a/tests/alias.c
> +++ b/tests/alias.c
> @@ -73,12 +73,6 @@ int __wrap_mkstemp(char *template)
>  	return 10;
>  }
>  
> -int __wrap_dm_map_present(const char * str)
> -{
> -	check_expected(str);
> -	return mock_type(int);
> -}
> -
>  int __wrap_dm_get_uuid(const char *name, char *uuid, int uuid_len)
>  {
>  	int ret;
> @@ -398,14 +392,13 @@ static int test_scan_devname(void)
>  
>  static void mock_unused_alias(const char *alias)
>  {
> -	expect_string(__wrap_dm_map_present, str, alias);
> -	will_return(__wrap_dm_map_present, 0);
> +	expect_string(__wrap_dm_get_uuid, name, alias);
> +	expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);
> +	will_return(__wrap_dm_get_uuid, 1);
>  }
>  
>  static void mock_self_alias(const char *alias, const char *wwid)
>  {
> -	expect_string(__wrap_dm_map_present, str, alias);
> -	will_return(__wrap_dm_map_present, 1);
>  	expect_string(__wrap_dm_get_uuid, name, alias);
>  	expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);
>  	will_return(__wrap_dm_get_uuid, 0);
> @@ -432,18 +425,13 @@ static void mock_self_alias(const char *alias, const char *wwid)
>  
>  #define mock_failed_alias(alias, wwid)					\
>  	do {								\
> -		expect_string(__wrap_dm_map_present, str, alias);	\
> -		will_return(__wrap_dm_map_present, 1);			\
>  		expect_string(__wrap_dm_get_uuid, name, alias);		\
>  		expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);	\
>  		will_return(__wrap_dm_get_uuid, 1);			\
> -		expect_condlog(3, USED_STR(alias, wwid));		\
>  	} while (0)
>  
>  #define mock_used_alias(alias, wwid)					\
>  	do {								\
> -		expect_string(__wrap_dm_map_present, str, alias);	\
> -		will_return(__wrap_dm_map_present, 1);			\
>  		expect_string(__wrap_dm_get_uuid, name, alias);		\
>  		expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);	\
>  		will_return(__wrap_dm_get_uuid, 0);			\
> @@ -566,9 +554,8 @@ static void lb_empty_failed(void **state)
>  	mock_bindings_file("");
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
>  	mock_failed_alias("MPATHa", "WWID0");
> -	mock_unused_alias("MPATHb");
>  	rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
> -	assert_int_equal(rc, 2);
> +	assert_int_equal(rc, 1);
>  	assert_ptr_equal(alias, NULL);
>  	free(alias);
>  }
> @@ -666,9 +653,8 @@ static void lb_nomatch_a_3_used_failed_self(void **state)
>  	mock_used_alias("MPATHc", "WWID1");
>  	mock_used_alias("MPATHd", "WWID1");
>  	mock_failed_alias("MPATHe", "WWID1");
> -	mock_self_alias("MPATHf", "WWID1");
>  	rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
> -	assert_int_equal(rc, 6);
> +	assert_int_equal(rc, 5);
>  	assert_ptr_equal(alias, NULL);
>  }
>  
> @@ -949,11 +935,7 @@ static void lb_nomatch_b_a_aa_zz(void **state)
>  	 * lookup_binding finds MPATHaaa as next free entry, because MPATHaa is
>  	 * found before MPATHb, and MPATHzz was in the bindings, too.
>  	 */
> -	for (i = 0; i <= 26; i++) {
> -		print_strbuf(&buf,  "MPATH");
> -		format_devname(&buf, i + 1);
> -		print_strbuf(&buf,  " WWID%d\n", i);
> -	}
> +	fill_bindings(&buf, 0, 26);
>  	print_strbuf(&buf, "MPATHzz WWID676\n");
>  	mock_bindings_file(get_strbuf_str(&buf));
>  	expect_condlog(3, NOMATCH_WWID_STR("WWID703"));
> -- 
> 2.41.0
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 04/21] libmultipath: never allocate an alias that's already taken
  2023-09-06 22:42   ` Benjamin Marzinski
@ 2023-09-07  7:24     ` Martin Wilck
  2023-09-07 13:33       ` Martin Wilck
  0 siblings, 1 reply; 55+ messages in thread
From: Martin Wilck @ 2023-09-07  7:24 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: dm-devel, David Bond

On Wed, 2023-09-06 at 17:42 -0500, Benjamin Marzinski wrote:
> On Fri, Sep 01, 2023 at 08:02:17PM +0200, mwilck@suse.com wrote:
> > From: Martin Wilck <mwilck@suse.com>
> > 
> > If the bindings file is changed in a way that multipathd can't
> > handle
> > (e.g. by swapping the aliases of two maps), multipathd must not try
> > to re-use an alias that is already used by another map. Creating
> > or renaming a map with such an alias will fail. We already avoid
> > this for some cases, but not for all. Fix it.
> > 
> > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > Cc: David Bond <dbond@suse.com>
> > ---
> >  libmultipath/alias.c | 36 +++++++++++++++++++++++++++++++-----
> >  tests/alias.c        |  2 +-
> >  2 files changed, 32 insertions(+), 6 deletions(-)
> > 
> > diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> > index 9b9b789..f7834d1 100644
> > --- a/libmultipath/alias.c
> > +++ b/libmultipath/alias.c
> > @@ -120,7 +120,7 @@ static bool alias_already_taken(const char
> > *alias, const char *map_wwid)
> >                 if (dm_get_uuid(alias, wwid, sizeof(wwid)) == 0 &&
> >                     strncmp(map_wwid, wwid, sizeof(wwid)) == 0)
> >                         return false;
> > -               condlog(3, "%s: alias '%s' already taken, but not
> > in bindings file. reselecting alias",
> > +               condlog(3, "%s: alias '%s' already taken,
> > reselecting alias",
> >                         map_wwid, alias);
> >                 return true;
> >         }
> > @@ -363,28 +363,54 @@ char *get_user_friendly_alias(const char
> > *wwid, const char *file, const char *al
> >                  * allocated correctly
> >                  */
> >                 if (strcmp(buff, wwid) == 0) {
> 
> I'm confused about this. We should only have alias_old set if there
> already exists a device that matches this WWID, right? That's what I
> though alias_old means. Am I missing some way that alias_old could
> get
> set to something other than the alias of an already existing device
> with
> a matching WWID? Otherwise, once we verify that that this mapping
> also
> exists in the bindings file, we should be fine to use it?
> 

This is mainly meant as an additional consistency check, to make sure
that the logic in get_user_friendly_alias() is correct, no matter how
"alias_old" was set by the caller. Note that alias_old is ab^H^Hreused
by the ACT_RENAME action; it is not immediately obvious that alias_old
can never be set to an invalid value in get_user_friendly_alias().

condlog() prints "ERROR" here because it's a condition that shouldn't
occur.

> > -                       alias = strdup(alias_old);
> > +                       if (!alias_already_taken(alias_old, wwid))
> > +                               alias = strdup(alias_old);
> > +                       else
> > +                               condlog(0, "ERROR: old alias [%s]
> > for wwid [%s] is used by other map",
> > +                                       alias_old, wwid);
> >                         goto out;
> > +
> >                 } else {
> >                         condlog(0, "alias %s already bound to wwid
> > %s, cannot reuse",
> >                                 alias_old, buff);
> > -                       goto new_alias;
> 
> extra semicolon.
> 
> > +                       goto new_alias;              ;
> >                 }
> >         }
> >  
> > +       /*
> > +        * Look for an existing alias in the bindings file.
> > +        * Pass prefix = NULL, so lookup_binding() won't try to
> > allocate a new id.
> > +        */
> 
> There's no point in saving the return value here. We don't use if for
> anything.
> 
> >         id = lookup_binding(f, wwid, &alias, NULL, 0);
> >         if (alias) {
> > -               condlog(3, "Use existing binding [%s] for WWID
> > [%s]",
> > -                       alias, wwid);
> > +               if (alias_already_taken(alias, wwid)) {
> > +                       free(alias);
> > +                       alias = NULL;
> > +               } else
> > +                       condlog(3, "Use existing binding [%s] for
> > WWID [%s]",
> > +                               alias, wwid);
> >                 goto out;
> >         }
> >  
> >         /* allocate the existing alias in the bindings file */
> >         id = scan_devname(alias_old, prefix);
> 
> Again, unless I'm overlooking something, I don't think we need to
> check if the alias is already taken here. Since we know that a device
> already exists with alias_old and the correct WWID, as long as
> alias_old
> is a valid user_friendly_name we can just use it.

Similar reasoning as above. We could perhaps remove these checks, but
we'd need to replace them by comments explaining why this condition
can't occur.

We could (and maybe should) move the call to find_existing_alias() from
add_map_with_path() to get_user_friendly_alias(), so that we have the
entire alias logic in a single place. The mpp->alias_old field would
then only be used for ACT_RENAME.

Martin

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 07/21] multipath-tools tests: add tests for get_user_friendly_alias()
  2023-09-06 22:43   ` Benjamin Marzinski
@ 2023-09-07  7:55     ` Martin Wilck
  0 siblings, 0 replies; 55+ messages in thread
From: Martin Wilck @ 2023-09-07  7:55 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: dm-devel

On Wed, 2023-09-06 at 17:43 -0500, Benjamin Marzinski wrote:
> On Fri, Sep 01, 2023 at 08:02:20PM +0200, mwilck@suse.com wrote:
> > From: Martin Wilck <mwilck@suse.com>
> > 
> > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > ---
> >  tests/alias.c | 531
> > ++++++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 531 insertions(+)
> > 
> > diff --git a/tests/alias.c b/tests/alias.c
> > index 11f209e..e2372d1 100644
> > --- a/tests/alias.c
> > +++ b/tests/alias.c
> > @@ -81,6 +81,35 @@ int __wrap_dm_get_uuid(const char *name, char
> > *uuid, int uuid_len)
> >         return ret;
> >  }
> >  
> > +#define TEST_FDNO 1234
> > +#define TEST_FPTR ((FILE *) 0xaffe)
> > +
> > +int __wrap_open_file(const char *file, int *can_write, const char
> > *header)
> > +{
> > +       int cw = mock_type(int);
> > +
> > +        *can_write = cw;
> > +       return TEST_FDNO;
> > +}
> > +
> > +FILE *__wrap_fdopen(int fd, const char *mode)
> > +{
> > +       assert_int_equal(fd, TEST_FDNO);
> > +       return TEST_FPTR;
> > +}
> > +
> > +int __wrap_fflush(FILE *f)
> > +{
> > +       assert_ptr_equal(f, TEST_FPTR);
> > +       return 0;
> > +}
> > +
> > +int __wrap_fclose(FILE *f)
> > +{
> > +       assert_ptr_equal(f, TEST_FPTR);
> > +       return 0;
> > +}
> > +
> >  /* strbuf wrapper for the old format_devname() */
> >  static int __format_devname(char *name, int id, size_t len, const
> > char *prefix)
> >  {
> > @@ -399,6 +428,22 @@ static void mock_self_alias(const char *alias,
> > const char *wwid)
> >  }
> >  
> >  #define USED_STR(alias_str, wwid_str) wwid_str ": alias '"
> > alias_str "' already taken, reselecting alias\n"
> > +#define NOMATCH_STR(alias_str) ("No matching alias [" alias_str "]
> > in bindings file.\n")
> > +#define FOUND_STR(alias_str, wwid_str)                         \
> > +       "Found matching wwid [" wwid_str "] in bindings file."  \
> > +       " Setting alias to " alias_str "\n"
> > +#define FOUND_ALIAS_STR(alias_str,
> > wwid_str)                           \
> > +       "Found matching alias [" alias_str "] in bindings
> > file."        \
> > +       " Setting wwid to " wwid_str "\n"
> > +#define NOMATCH_WWID_STR(wwid_str) ("No matching wwid [" wwid_str
> > "] in bindings file.\n")
> > +#define NEW_STR(alias_str, wwid_str) ("Created new binding ["
> > alias_str "] for WWID [" wwid_str "]\n")
> > +#define EXISTING_STR(alias_str, wwid_str) ("Use existing binding
> > [" alias_str "] for WWID [" wwid_str "]\n")
> > +#define ALLOC_STR(alias_str, wwid_str) ("Allocated existing
> > binding [" alias_str "] for WWID [" wwid_str "]\n")
> > +#define BINDING_STR(alias_str, wwid_str) (alias_str " " wwid_str
> > "\n")
> > +#define BOUND_STR(alias_str, wwid_str) ("alias "alias_str "
> > already bound to wwid " wwid_str ", cannot reuse")
> > +#define ERR_STR(alias_str, wwid_str) ("ERROR: old alias ["
> > alias_str "] for wwid [" wwid_str "] is used by other map\n")
> > +#define REUSE_STR(alias_str, wwid_str) ("alias " alias_str "
> > already bound to wwid " wwid_str ", cannot reuse\n")
> > +#define NOMORE_STR "no more available user_friendly_names\n"
> >  
> >  static void mock_failed_alias(const char *alias, char *msg)
> >  {
> > @@ -421,6 +466,24 @@ static void mock_used_alias(const char *alias,
> > char *msg)
> >         expect_condlog(3, msg);
> >  }
> >  
> > +static void mock_bindings_file(const char *content, int
> > match_line)
> > +{
> > +       static char cnt[1024];
> > +       char *token;
> > +       int i;
> > +
> > +       assert_in_range(strlcpy(cnt, content, sizeof(cnt)), 0,
> > sizeof(cnt) - 1);
> > +
> > +       for (token = strtok(cnt, "\n"), i = 0;
> > +            token && *token;
> > +            token = strtok(NULL, "\n"), i++) {
> > +               will_return(__wrap_fgets, token);
> > +               if (match_line == i)
> > +                       return;
> > +       }
> > +       will_return(__wrap_fgets, NULL);
> > +}
> > +
> >  static void lb_empty(void **state)
> >  {
> >         int rc;
> > @@ -1147,6 +1210,472 @@ static int test_allocate_binding(void)
> >         return cmocka_run_group_tests(tests, NULL, NULL);
> >  }
> >  
> > +#define mock_allocate_binding(alias,
> > wwid)                             \
> > +       do
> > {                                                            \
> > +               static const char ln[] = BINDING_STR(alias,
> > wwid);      \
> > +                                                                  
> >      \
> > +               will_return(__wrap_lseek,
> > 0);                           \
> > +               expect_value(__wrap_write, count,
> > strlen(ln));          \
> > +               expect_string(__wrap_write, buf,
> > ln);                   \
> > +               will_return(__wrap_write,
> > strlen(ln));                  \
> > +               expect_condlog(3, NEW_STR(alias,
> > wwid));                \
> > +       } while (0)
> > +
> > +static void gufa_empty_new_rw(void **state) {
> > +       char *alias;
> > +
> > +       will_return(__wrap_open_file, true);
> > +
> > +       will_return(__wrap_fgets, NULL);
> > +       mock_unused_alias("MPATHa");
> > +       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> > +
> > +       mock_allocate_binding("MPATHa", "WWID0");
> > +       alias = get_user_friendly_alias("WWID0", "x", "", "MPATH",
> > false);
> > +       assert_string_equal(alias, "MPATHa");
> > +       free(alias);
> > +}
> > +
> > +static void gufa_empty_new_ro_1(void **state) {
> > +       char *alias;
> > +       will_return(__wrap_open_file, false);
> > +       will_return(__wrap_fgets, NULL);
> > +       mock_unused_alias("MPATHa");
> > +       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> > +
> > +       alias = get_user_friendly_alias("WWID0", "x", "", "MPATH",
> > false);
> > +       assert_ptr_equal(alias, NULL);
> > +}
> > +
> > +static void gufa_empty_new_ro_2(void **state) {
> > +       char *alias;
> > +
> > +       will_return(__wrap_open_file, true);
> > +
> > +       will_return(__wrap_fgets, NULL);
> > +       mock_unused_alias("MPATHa");
> > +       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> > +
> > +       alias = get_user_friendly_alias("WWID0", "x", "", "MPATH",
> > true);
> > +       assert_ptr_equal(alias, NULL);
> > +}
> > +
> > +static void gufa_match_a_unused(void **state) {
> > +       char *alias;
> > +
> > +       will_return(__wrap_open_file, true);
> > +
> > +       will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
> > +       expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
> > +       mock_unused_alias("MPATHa");
> > +
> > +       alias = get_user_friendly_alias("WWID0", "x", "", "MPATH",
> > true);
> > +       assert_string_equal(alias, "MPATHa");
> > +       free(alias);
> > +}
> > +
> > +static void gufa_match_a_self(void **state) {
> > +       char *alias;
> > +
> > +       will_return(__wrap_open_file, true);
> > +
> > +       will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
> > +       expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
> > +       mock_self_alias("MPATHa", "WWID0");
> > +
> > +       alias = get_user_friendly_alias("WWID0", "x", "", "MPATH",
> > true);
> > +       assert_string_equal(alias, "MPATHa");
> > +       free(alias);
> > +}
> > +
> > +static void gufa_match_a_used(void **state) {
> > +       char *alias;
> > +
> > +       will_return(__wrap_open_file, true);
> > +
> > +       will_return(__wrap_fgets, BINDING_STR("MPATHa", "WWID0"));
> > +       expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
> > +       mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
> > +
> > +       alias = get_user_friendly_alias("WWID0", "x", "", "MPATH",
> > true);
> > +       assert_ptr_equal(alias, NULL);
> > +}
> > +
> > +static void gufa_nomatch_a_c(void **state) {
> > +       char *alias;
> > +       will_return(__wrap_open_file, true);
> > +
> > +       mock_bindings_file("MPATHa WWID0\n"
> > +                          "MPATHc WWID2",
> > +                          -1);
> > +       mock_unused_alias("MPATHb");
> > +       expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
> > +
> > +       mock_allocate_binding("MPATHb", "WWID1");
> > +
> > +       alias = get_user_friendly_alias("WWID1", "x", "", "MPATH",
> > false);
> > +       assert_string_equal(alias, "MPATHb");
> > +       free(alias);
> > +}
> > +
> > +static void gufa_nomatch_c_a(void **state) {
> > +       char *alias;
> > +       will_return(__wrap_open_file, true);
> > +
> > +       mock_bindings_file("MPATHc WWID2\n"
> > +                          "MPATHa WWID0",
> > +                          -1);
> > +       mock_unused_alias("MPATHb");
> > +       expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
> > +
> > +       mock_allocate_binding("MPATHb", "WWID1");
> > +
> > +       alias = get_user_friendly_alias("WWID1", "x", "", "MPATH",
> > false);
> > +       assert_string_equal(alias, "MPATHb");
> > +       free(alias);
> > +}
> > +
> > +static void gufa_nomatch_c_b(void **state) {
> > +       char *alias;
> > +       will_return(__wrap_open_file, true);
> > +
> > +       mock_bindings_file("MPATHc WWID2\n"
> > +                          "MPATHb WWID1\n",
> > +                          -1);
> > +       mock_unused_alias("MPATHa");
> > +       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> > +
> > +       mock_allocate_binding("MPATHa", "WWID0");
> > +
> > +       alias = get_user_friendly_alias("WWID0", "x", "", "MPATH",
> > false);
> > +       assert_string_equal(alias, "MPATHa");
> > +       free(alias);
> > +}
> > +
> > +static void gufa_nomatch_c_b_used(void **state) {
> > +       char *alias;
> > +       will_return(__wrap_open_file, true);
> > +
> > +       mock_bindings_file("MPATHc WWID2\n"
> > +                          "MPATHb WWID1",
> > +                          -1);
> > +       mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID4"));
> > +       expect_condlog(3, NOMATCH_WWID_STR("WWID4"));
> > +       mock_unused_alias("MPATHd");
> > +
> > +       mock_allocate_binding("MPATHd", "WWID4");
> > +
> > +       alias = get_user_friendly_alias("WWID4", "x", "", "MPATH",
> > false);
> > +       assert_string_equal(alias, "MPATHd");
> > +       free(alias);
> > +}
> > +
> > +static void gufa_nomatch_b_f_a(void **state) {
> > +       char *alias;
> > +       will_return(__wrap_open_file, true);
> > +
> > +       mock_bindings_file("MPATHb WWID1\n"
> > +                          "MPATHf WWID6\n"
> > +                          "MPATHa WWID0\n",
> > +                          -1);
> > +       expect_condlog(3, NOMATCH_WWID_STR("WWID7"));
> > +       mock_unused_alias("MPATHg");
> > +
> > +       mock_allocate_binding("MPATHg", "WWID7");
> > +
> > +       alias = get_user_friendly_alias("WWID7", "x", "", "MPATH",
> > false);
> > +       assert_string_equal(alias, "MPATHg");
> > +       free(alias);
> > +}
> > +
> > +static void gufa_old_empty(void **state) {
> > +       char *alias;
> > +       will_return(__wrap_open_file, true);
> > +
> > +       /* rlookup_binding for ALIAS */
> > +       will_return(__wrap_fgets, NULL);
> > +       expect_condlog(3, NOMATCH_STR("MPATHz"));
> > +
> > +       /* lookup_binding */
> > +       will_return(__wrap_fgets, NULL);
> > +       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> > +
> > +       mock_unused_alias("MPATHz");
> > +
> > +       mock_allocate_binding("MPATHz", "WWID0");
> > +       expect_condlog(2, ALLOC_STR("MPATHz", "WWID0"));
> > +
> > +       alias = get_user_friendly_alias("WWID0", "x", "MPATHz",
> > "MPATH", false);
> > +       assert_string_equal(alias, "MPATHz");
> > +       free(alias);
> > +}
> > +
> > +static void gufa_old_empty_self(void **state) {
> > +       char *alias;
> > +       will_return(__wrap_open_file, true);
> > +
> > +       will_return(__wrap_fgets, NULL);
> > +       expect_condlog(3, NOMATCH_STR("MPATHz"));
> > +
> > +       will_return(__wrap_fgets, NULL);
> > +       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
> > +       mock_self_alias("MPATHz", "WWID0");
> > +
> > +       mock_allocate_binding("MPATHz", "WWID0");
> > +       expect_condlog(2, ALLOC_STR("MPATHz", "WWID0"));
> > +
> > +       alias = get_user_friendly_alias("WWID0", "x", "MPATHz",
> > "MPATH", false);
> > +       assert_string_equal(alias, "MPATHz");
> > +       free(alias);
> > +}
> > +
> 
> I'm not sure this is a valid test. Neither are gufa_old_match_used()
> and
> gufa_old_nomatch_nowwidmatch_used(). Like I said, If
> get_user_friendly_alias() is called with a non-NULL alias_old, I
> think
> that means that there is current a device named alias_old with the
> expected wwid.  So I don't think mock_used_alias() returning a
> non-matching WWID is possible in real life. Again, if I'm wrong and
> it
> is possible to have alias_old set without there being a dm device,
> then
> this is a reasonable test.

Let's clarify this in the discussion about patch 4/21. I think the test
itself is valid. It tests a combination of arguments / internal state
variables that shouldn't occur, but that's true for many of our unit
tests.

Martin

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 05/21] libmultipath: lookup_binding: add comment about the algorithm
  2023-09-06 22:43   ` Benjamin Marzinski
@ 2023-09-07  8:22     ` Martin Wilck
  0 siblings, 0 replies; 55+ messages in thread
From: Martin Wilck @ 2023-09-07  8:22 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: dm-devel

On Wed, 2023-09-06 at 17:43 -0500, Benjamin Marzinski wrote:
> On Fri, Sep 01, 2023 at 08:02:18PM +0200, mwilck@suse.com wrote:
> > From: Martin Wilck <mwilck@suse.com>
> > 
> > When I read this code, I always get confused. Adding comments to
> > explain the algorithm.
> > 
> > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > ---
> >  libmultipath/alias.c | 35 +++++++++++++++++++++++++++++++++++
> >  1 file changed, 35 insertions(+)
> > 
> > diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> > index f7834d1..e61eb91 100644
> > --- a/libmultipath/alias.c
> > +++ b/libmultipath/alias.c
> > @@ -172,6 +172,41 @@ lookup_binding(FILE *f, const char *map_wwid,
> > char **map_alias,
> >                 alias = strtok_r(buf, " \t", &saveptr);
> >                 if (!alias) /* blank line */
> >                         continue;
> > +
> > +               /*
> > +                * Find an unused index - explanation of the
> > algorithm
> > +                *
> > +                * ID: 1 = mpatha, 2 = mpathb, ...
> > +                *
> > +                * We assume the bindings are unsorted. The only
> > constraint
> > +                * is that no ID occurs more than once. IDs that
> > occur in the
> > +                * bindings are called "used".
> > +                *
> > +                * We call the list 1,2,3,..., exactly in this
> > order, the list
> > +                * of "expected" IDs. The variable "id" always
> > holds the next
> > +                * "expected" ID, IOW the last "expected" ID
> > encountered plus 1.
> > +                * Thus all IDs below "id" are known to be used.
> > However, at the
> > +                * end of the loop, the value of "id" isn't
> > necessarily unused.
> > +                *
> > +                * "smallest_bigger_id" is the smallest used ID
> > that was
> > +                * encountered while it was larger than the next
> > "expected" ID
> > +                * at that iteration. Let X be some used ID. If all
> > IDs below X
> > +                * are used and encountered in the right sequence
> > before X, "id"
> > +                * will be > X when the loop ends. Otherwise, X was
> > encountered
> > +                * "out of order", the condition (X > id) holds
> > when X is
> > +                * encountered, and "smallest_bigger_id" will be
> > set to X; i.e.
> > +                * it will be less or equal than X when the loop
> > ends.
> > +                *
> > +                * At the end of the loop, (id <
> > smallest_bigger_id) means that
> > +                * the value of "id" had been encountered neither
> > in order nor
> > +                * out of order, and is thus unused. (id >=
> > smallest_bigger_id)
> 
> I know the check is (id >= smallest_bigger_id), but as long as no ID
> occurs more than once, id can never actually be bigger than
> smallest_bigger_id since id only gets incremented when (curr_id ==
> id)
> and if smallest_bigger_id is not INT_MAX, then smallest_bigger_id
> already occured once in the file before id was incremented to equal
> it.
> This means it can't occur again, so id can never get incremented past
> it. Not this this really matters, so

Right. Like you, I thought this didn't matter for the algorithm as
such, and that explaining it would make the lengthy explanation even
lengthier.

Btw, if you have a simpler explanation of this algorithm, please
provide it ;-)

Martin

> 
> Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> 
> > +                * means that "id"'s value is in use. In this case,
> > we play safe
> > +                * and use "biggest_id + 1" as the next value to
> > try.
> > +                *
> > +                * biggest_id is always > smallest_bigger_id,
> > except in the
> > +                * "perfectly ordered" case.
> > +                */
> > +
> >                 curr_id = scan_devname(alias, prefix);
> >                 if (curr_id == id) {
> >                         if (id < INT_MAX)
> > -- 
> > 2.41.0
> 

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 18/21] libmultipath: keep bindings in memory
  2023-09-06 22:47   ` Benjamin Marzinski
@ 2023-09-07 10:30     ` Martin Wilck
  2023-09-07 19:14       ` Benjamin Marzinski
  0 siblings, 1 reply; 55+ messages in thread
From: Martin Wilck @ 2023-09-07 10:30 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: dm-devel

On Wed, 2023-09-06 at 17:47 -0500, Benjamin Marzinski wrote:
> On Fri, Sep 01, 2023 at 08:02:31PM +0200, mwilck@suse.com wrote:
> > From: Martin Wilck <mwilck@suse.com>
> > 
> > Rather than opening the bindings file every time we must retrieve
> > a binding, keep the contents in memory and write the file only
> > if additions have been made. This simplifies the code, and should
> > speed up
> > alias lookups significantly. As a side effect, the aliases will be
> > stored
> > sorted by alias, which changes the way aliases are allocated if
> > there are
> > unused "holes" in the sequence of aliases. For example, if the
> > bindings file
> > contains mpathb, mpathy, and mpatha, in this order, the next new
> > alias used to
> > be mpathz and is now mpathc.
> > 
> > Another side effect is that multipathd will not automatically pick
> > up changes
> > to the bindings file at runtime without a reconfigure operation. It
> > is
> > questionable whether these on-the-fly changes were a good idea in
> > the first
> > place, as inconsistent configurations may easily come to pass. It
> > desired,
> > it would be feasible to implement automatic update of the bindings
> > using the
> > existing inotify approach.
> 
> I'm not so much worried about someone manually changing the bindings
> file outside of multipath. But it is possible for multipathd to miss
> changes made by the multipath command.  For instance, say that
> someone
> has find_multipaths set to "yes" and adds a new device, but only a
> single path comes up.  They know there will be more paths later, so
> they
> run
> 
> # multipath <new_path_dev>
> 
> to create a multipath device for this.  Multipathd won't pick up this
> new binding. If, for some reason the path goes away, and comes back
> later, it will now be in the bindings file, but multipathd will still
> have no record of the correct binding for it, which already exists in
> the bindings file. I don't know if this needs something as complex as
> inotify to handle.  We could just record the mtime of the bindings
> file
> when we read it.  If it has changed when we call
> get_user_friendly_alias() or get_user_friendly_wwid() we would call
> check_alias_settings().
> 
> additional comments below. 
> > The new implementation of get_user_friendly_alias() is slightly
> > different
> > than before. The logic is summarized in a comment in the code. Unit
> > tests
> > will be provided that illustrate the changes.
> > 
> > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > ---
> >  libmultipath/alias.c     | 369 ++++++++++++++++-------------------
> > ----
> >  libmultipath/alias.h     |   2 +-
> >  libmultipath/configure.c |   3 +-
> >  3 files changed, 157 insertions(+), 217 deletions(-)
> > 
> > diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> > index 2f9bc82..6003df0 100644
> > --- a/libmultipath/alias.c
> > +++ b/libmultipath/alias.c
> > @@ -50,8 +50,6 @@
> >  "# alias wwid\n" \
> >  "#\n"
> >  
> > -static const char bindings_file_header[] = BINDINGS_FILE_HEADER;
> > -
> >  struct binding {
> >         char *alias;
> >         char *wwid;
> > @@ -80,6 +78,45 @@ static void _free_binding(struct binding *bdg)
> >         free(bdg);
> >  }
> >  
> > +static const struct binding *get_binding_for_alias(const Bindings
> > *bindings,
> > +                                                  const char
> > *alias)
> > +{
> > +       const struct binding *bdg;
> > +       int i;
> > +
> > +       if (!alias)
> > +               return NULL;
> > +       vector_foreach_slot(bindings, bdg, i) {
> > +               if (!strncmp(bdg->alias, alias, WWID_SIZE)) {
> > +                       condlog(3, "Found matching alias [%s] in
> > bindings file."
> > +                               " Setting wwid to %s", alias, bdg-
> > >wwid);
> > +                       return bdg;
> > +               }
> > +       }
> > +
> > +       condlog(3, "No matching alias [%s] in bindings file.",
> > alias);
> > +       return NULL;
> > +}
> > +
> > +static const struct binding *get_binding_for_wwid(const Bindings
> > *bindings,
> > +                                                 const char *wwid)
> > +{
> > +       const struct binding *bdg;
> > +       int i;
> > +
> > +       if (!wwid)
> > +               return NULL;
> > +       vector_foreach_slot(bindings, bdg, i) {
> > +               if (!strncmp(bdg->wwid, wwid, WWID_SIZE)) {
> > +                       condlog(3, "Found matching wwid [%s] in
> > bindings file."
> > +                               " Setting alias to %s", wwid, bdg-
> > >alias);
> > +                       return bdg;
> > +               }
> > +       }
> > +       condlog(3, "No matching wwid [%s] in bindings file.",
> > wwid);
> > +       return NULL;
> > +}
> > +
> >  static int add_binding(Bindings *bindings, const char *alias,
> > const char *wwid)
> >  {
> >         struct binding *bdg;
> > @@ -115,6 +152,24 @@ static int add_binding(Bindings *bindings,
> > const char *alias, const char *wwid)
> >         return BINDING_ERROR;
> >  }
> >  
> > +static int delete_binding(Bindings *bindings, const char *wwid)
> > +{
> > +       struct binding *bdg;
> > +       int i;
> > +
> > +       vector_foreach_slot(bindings, bdg, i) {
> > +               if (!strncmp(bdg->wwid, wwid, WWID_SIZE)) {
> > +                       _free_binding(bdg);
> > +                       break;
> > +               }
> > +       }
> > +       if (i >= VECTOR_SIZE(bindings))
> > +               return BINDING_NOTFOUND;
> > +
> > +       vector_del_slot(bindings, i);
> > +       return BINDING_DELETED;
> > +}
> > +
> >  static int write_bindings_file(const Bindings *bindings, int fd)
> >  {
> >         struct binding *bnd;
> > @@ -267,38 +322,15 @@ static bool id_already_taken(int id, const
> > char *prefix, const char *map_wwid)
> >         return alias_already_taken(alias, map_wwid);
> >  }
> >  
> > -/*
> > - * Returns: 0   if matching entry in WWIDs file found
> > - *         -1   if an error occurs
> > - *         >0   a free ID that could be used for the WWID at hand
> > - * *map_alias is set to a freshly allocated string with the
> > matching alias if
> > - * the function returns 0, or to NULL otherwise.
> > - */
> > -static int
> > -lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
> > -              const char *prefix, int check_if_taken)
> > +int get_free_id(const Bindings *bindings, const char *prefix,
> > const char *map_wwid)
> >  {
> > -       char buf[LINE_MAX];
> > -       unsigned int line_nr = 0;
> > -       int id = 1;
> > +       const struct binding *bdg;
> > +       int i, id = 1;
> >         int biggest_id = 1;
> >         int smallest_bigger_id = INT_MAX;
> >  
> > -       *map_alias = NULL;
> > -
> > -       rewind(f);
> > -       while (fgets(buf, LINE_MAX, f)) {
> > -               const char *alias, *wwid;
> > -               char *c, *saveptr;
> > -               int curr_id;
> > -
> > -               line_nr++;
> > -               c = strpbrk(buf, "#\n\r");
> > -               if (c)
> > -                       *c = '\0';
> > -               alias = strtok_r(buf, " \t", &saveptr);
> > -               if (!alias) /* blank line */
> > -                       continue;
> > +       vector_foreach_slot(bindings, bdg, i) {
> > +               int curr_id = scan_devname(bdg->alias, prefix);
> 
> If we know that the bindings will be sorted by alias order, we don't
> need to do all this. Something like this should work:

That's true. Unfortunately, we can't ensure ordering "by alias order".
The reason is that our configuration allows using different alias
prefixes for different devices. The current sorting is simply
alphabetical. It detects duplicates, but it ensures "alias order" only
between "mpatha" and "mpathz".

I've thought of just removing the "different aliases for different
devices" feature. I don't know if any users out there actually set
device-specific alias_prefix values in the devices section of
multipath.conf. I don't recall having seen such a configuration so far;
almost every config I have seen simply uses "mpath" everywhere. But I
recognize that it may feel tempting in some cases. One could use the
"NETAPP" prefix for some arrays and the "EMC" prefix for others, for
example, making it easier to see which is what. And we simply don't
know if we'd break existing user setups with such a change. So if at
all, we can't do it in a minor release without deprecating it first.

When we add a binding in add_binding(), we don't know which
alias_prefix is configured for a given WWID, and we have no easy way to
figure it out. We know the alias, but we don't know which portion of it
is the prefix and which is the ID (it's not forbidden to use "aaaa" as
alias_prefix). I wouldn't want to start guessing.

If you can think of a way to keep the bindings cleanly sorted, please
let me know.

> 
>                 if (curr_id <= 0)
>                         continue;
> 
>                 while (id < curr_id) {
>                         if (!id_already_taken(id, prefix, map_wwid))
>                                 return id;
>                         id++;
>                 }
>                 if (id == INT_MAX)
>                         break;
>                 id++;
>         }
>         if (id == INT_MAX)
>                 condlog(0, "no more available user_friendly_names");
>         return id < INT_MAX ? id : -1;
> }
> >  
> >                 /*
> >                  * Find an unused index - explanation of the
> > algorithm
> > @@ -333,8 +365,6 @@ lookup_binding(FILE *f, const char *map_wwid,
> > char **map_alias,
> >                  * biggest_id is always > smallest_bigger_id,
> > except in the
> >                  * "perfectly ordered" case.
> >                  */
> > -
> > -               curr_id = scan_devname(alias, prefix);
> >                 if (curr_id == id) {
> >                         if (id < INT_MAX)
> >                                 id++;
> > @@ -345,36 +375,15 @@ lookup_binding(FILE *f, const char *map_wwid,
> > char **map_alias,
> >                 }
> >                 if (curr_id > biggest_id)
> >                         biggest_id = curr_id;
> > +
> >                 if (curr_id > id && curr_id < smallest_bigger_id)
> >                         smallest_bigger_id = curr_id;
> > -               wwid = strtok_r(NULL, " \t", &saveptr);
> > -               if (!wwid){
> > -                       condlog(3,
> > -                               "Ignoring malformed line %u in
> > bindings file",
> > -                               line_nr);
> > -                       continue;
> > -               }
> > -               if (strcmp(wwid, map_wwid) == 0){
> > -                       condlog(3, "Found matching wwid [%s] in
> > bindings file."
> > -                               " Setting alias to %s", wwid,
> > alias);
> > -                       *map_alias = strdup(alias);
> > -                       if (*map_alias == NULL) {
> > -                               condlog(0, "Cannot copy alias from
> > bindings "
> > -                                       "file: out of memory");
> > -                               return -1;
> > -                       }
> > -                       return 0;
> > -               }
> >         }
> > -       if (!prefix && check_if_taken)
> > -               id = -1;
> > -       if (id >= smallest_bigger_id) {
> > -               if (biggest_id < INT_MAX)
> > -                       id = biggest_id + 1;
> > -               else
> > -                       id = -1;
> > -       }
> > -       if (id > 0 && check_if_taken) {
> > +
> > +       if (id >= smallest_bigger_id)
> > +               id = biggest_id < INT_MAX ? biggest_id + 1 : -1;
> > +
> > +       if (id > 0) {
> >                 while(id_already_taken(id, prefix, map_wwid)) {
> >                         if (id == INT_MAX) {
> >                                 id = -1;
> > @@ -391,63 +400,16 @@ lookup_binding(FILE *f, const char *map_wwid,
> > char **map_alias,
> >                         }
> >                 }
> >         }
> > -       if (id < 0) {
> > +
> > +       if (id < 0)
> >                 condlog(0, "no more available
> > user_friendly_names");
> > -               return -1;
> > -       } else
> > -               condlog(3, "No matching wwid [%s] in bindings
> > file.", map_wwid);
> >         return id;
> >  }
> >  
> > -static int
> > -rlookup_binding(FILE *f, char *buff, const char *map_alias)
> > -{
> > -       char line[LINE_MAX];
> > -       unsigned int line_nr = 0;
> > -
> > -       buff[0] = '\0';
> > -
> > -       while (fgets(line, LINE_MAX, f)) {
> > -               char *c, *saveptr;
> > -               const char *alias, *wwid;
> > -
> > -               line_nr++;
> > -               c = strpbrk(line, "#\n\r");
> > -               if (c)
> > -                       *c = '\0';
> > -               alias = strtok_r(line, " \t", &saveptr);
> > -               if (!alias) /* blank line */
> > -                       continue;
> > -               wwid = strtok_r(NULL, " \t", &saveptr);
> > -               if (!wwid){
> > -                       condlog(3,
> > -                               "Ignoring malformed line %u in
> > bindings file",
> > -                               line_nr);
> > -                       continue;
> > -               }
> > -               if (strlen(wwid) > WWID_SIZE - 1) {
> > -                       condlog(3,
> > -                               "Ignoring too large wwid at %u in
> > bindings file", line_nr);
> > -                       continue;
> > -               }
> > -               if (strcmp(alias, map_alias) == 0){
> > -                       condlog(3, "Found matching alias [%s] in
> > bindings file."
> > -                               " Setting wwid to %s", alias,
> > wwid);
> > -                       strlcpy(buff, wwid, WWID_SIZE);
> > -                       return 0;
> > -               }
> > -       }
> > -       condlog(3, "No matching alias [%s] in bindings file.",
> > map_alias);
> > -
> > -       return -1;
> > -}
> > -
> >  static char *
> > -allocate_binding(int fd, const char *wwid, int id, const char
> > *prefix)
> > +allocate_binding(const char *filename, const char *wwid, int id,
> > const char *prefix)
> >  {
> >         STRBUF_ON_STACK(buf);
> > -       off_t offset;
> > -       ssize_t len;
> >         char *alias, *c;
> >  
> >         if (id <= 0) {
> > @@ -460,29 +422,22 @@ allocate_binding(int fd, const char *wwid,
> > int id, const char *prefix)
> >             format_devname(&buf, id) == -1)
> >                 return NULL;
> >  
> > -       if (print_strbuf(&buf, " %s\n", wwid) < 0)
> > -               return NULL;
> > -
> > -       offset = lseek(fd, 0, SEEK_END);
> > -       if (offset < 0){
> > -               condlog(0, "Cannot seek to end of bindings file :
> > %s",
> > -                       strerror(errno));
> > -               return NULL;
> > -       }
> > -
> > -       len = get_strbuf_len(&buf);
> >         alias = steal_strbuf_str(&buf);
> >  
> > -       if (write(fd, alias, len) != len) {
> > -               condlog(0, "Cannot write binding to bindings file :
> > %s",
> > -                       strerror(errno));
> > -               /* clear partial write */
> > -               if (ftruncate(fd, offset))
> > -                       condlog(0, "Cannot truncate the header :
> > %s",
> > -                               strerror(errno));
> > +       if (add_binding(&global_bindings, alias, wwid) !=
> > BINDING_ADDED) {
> > +               condlog(0, "%s: cannot allocate new binding %s for
> > %s",
> > +                       __func__, alias, wwid);
> >                 free(alias);
> >                 return NULL;
> >         }
> > +
> > +       if (update_bindings_file(&global_bindings, filename) == -1)
> > {
> > +               condlog(1, "%s: deleting binding %s for %s",
> > __func__, alias, wwid);
> > +               delete_binding(&global_bindings, wwid);
> > +               free(alias);
> > +               return NULL;
> > +       }
> > +
> 
> We no longer need to end the alias at the space, since we're not
> printing the wwid in the buffer.

Right, thanks. This makes me realize that we don't sanity-check the
alias prefix, but that's a different issue.

> 
> >         c = strchr(alias, ' ');
> >         if (c)
> >                 *c = '\0';
> > @@ -491,144 +446,130 @@ allocate_binding(int fd, const char *wwid,
> > int id, const char *prefix)
> >         return alias;
> >  }
> >  
> > +/*
> > + * get_user_friendly_alias() action table
> > + *
> > + * The table shows the various cases, the actions taken, and the
> > CI
> > + * functions from tests/alias.c that represent them.
> > + *
> > + *  - O: old alias given
> > + *  - A: old alias in table (y: yes, correct WWID; X: yes, wrong
> > WWID)
> > + *  - W: wwid in table
> > + *  - U: old alias is used
> > + *
> > + *  | No | O | A | W | U |
> > action                                     | function
> > gufa_X              |
> > + *  |----+---+---+---+---+----------------------------------------
> > ----+------------------------------|
> > + *  |  1 | n | - | n | - | get new
> > alias                              | nomatch_Y                    |
> > + *  |  2 | n | - | y | - | use alias from
> > bindings                    | match_a_Y                    |
> > + *  |  3 | y | n | n | n | add binding for old
> > alias                  | old_nomatch_nowwidmatch      |
> > + *  |  4 | y | n | n | y | error, no alias (can't add
> > entry)          | old_nomatch_nowwidmatch_used |
> > + *  |  5 | y | n | y | - | use alias from bindings (avoid
> > duplicates) | old_nomatch_wwidmatch        |
> > + *  |  6 | y | y | n | - | [ impossible
> > ]                             | -                            |
> > + *  |  7 | y | y | y | n | use old alias == alias from
> > bindings       | old_match                    |
> > + *  |  8 | y | y | y | y | error, no alias (would be
> > duplicate)       | old_match_used               |
> > + *  |  9 | y | X | n | - | get new
> > alias                              | old_match_other              |
> > + *  | 10 | y | X | y | - | use alias from
> > bindings                    | old_match_other_wwidmatch    |
> > + *
> > + * Notes:
> > + *  - "use alias from bindings" means that the alias from the
> > bindings file will
> > + *     be tried; if it is in use, the alias selection will fail.
> > No other
> > + *     bindings will be attempted.
> > + *  - "get new alias" fails if all aliases are used up, or if
> > writing the
> > + *     bindings file fails.
> > + */
> > +
> >  char *get_user_friendly_alias(const char *wwid, const char *file,
> > const char *alias_old,
> >                               const char *prefix, bool
> > bindings_read_only)
> >  {
> >         char *alias = NULL;
> >         int id = 0;
> > -       int fd, can_write;
> >         bool new_binding = false;
> > -       char buff[WWID_SIZE];
> > -       FILE *f;
> > +       bool old_alias_taken = false;
> > +       const struct binding *bdg;
> >  
> > -       fd = open_file(file, &can_write, bindings_file_header);
> > -       if (fd < 0)
> > -               return NULL;
> > -
> > -       f = fdopen(fd, "r");
> > -       if (!f) {
> > -               condlog(0, "cannot fdopen on bindings file
> > descriptor");
> > -               close(fd);
> > -               return NULL;
> > -       }
> > -
> > -       if (!strlen(alias_old))
> > +       if (!*alias_old)
> >                 goto new_alias;
> >  
> > -       /* lookup the binding. if it exists, the wwid will be in
> > buff
> > -        * either way, id contains the id for the alias
> > -        */
> > -       rlookup_binding(f, buff, alias_old);
> > -
> > -       if (strlen(buff) > 0) {
> > -               /* if buff is our wwid, it's already
> > -                * allocated correctly
> > -                */
> > -               if (strcmp(buff, wwid) == 0) {
> > +       /* See if there's a binding matching both alias_old and
> > wwid */
> > +       bdg = get_binding_for_alias(&global_bindings, alias_old);
> > +       if (bdg) {
> > +               if (!strcmp(bdg->wwid, wwid)) {
> 
> I'm still not convinced that it is possible for mpp->alias_old to be
> set
> when there isn't a multipath device with the alias_old and the
> desired
> wwid. Unless I'm missing something we should be able to skip the
> alias_already_taken() check.

Let's follow up this discussion in the 4/21 thread.


> 
> >                         if (!alias_already_taken(alias_old, wwid))
> >                                 alias = strdup(alias_old);
> >                         else
> >                                 condlog(0, "ERROR: old alias [%s]
> > for wwid [%s] is used by other map",
> >                                         alias_old, wwid);
> >                         goto out;
> > -
> >                 } else {
> >                         condlog(0, "alias %s already bound to wwid
> > %s, cannot reuse",
> > -                               alias_old, buff);
> > -                       goto new_alias;              ;
> > +                               alias_old, bdg->wwid);
> > +                       goto new_alias;
> >                 }
> >         }
> >  
> > -       /*
> > -        * Look for an existing alias in the bindings file.
> > -        * Pass prefix = NULL, so lookup_binding() won't try to
> > allocate a new id.
> > -        */
> > -       id = lookup_binding(f, wwid, &alias, NULL, 0);
> > -       if (alias) {
> > -               if (alias_already_taken(alias, wwid)) {
> > -                       free(alias);
> > -                       alias = NULL;
> > -               } else
> > -                       condlog(3, "Use existing binding [%s] for
> > WWID [%s]",
> > -                               alias, wwid);
> > -               goto out;
> > -       }
> > -
> >         /* allocate the existing alias in the bindings file */
> > -       id = scan_devname(alias_old, prefix);
> > -       if (id > 0 && id_already_taken(id, prefix, wwid)) {
> again, I think it's safe to skip this check, since we're checking
> alias_old.
> > +       if (alias_already_taken(alias_old, wwid)) {
> >                 condlog(0, "ERROR: old alias [%s] for wwid [%s] is
> > used by other map",
> >                         alias_old, wwid);
> > +               old_alias_taken = true;
> > +       } else
> > +               id = scan_devname(alias_old, prefix);
> > +
> > +new_alias:
> > +       /* Check for existing binding of WWID */
> > +       bdg = get_binding_for_wwid(&global_bindings, wwid);
> > +       if (bdg) {
> > +               if (!alias_already_taken(bdg->alias, wwid)) {
> > +                       condlog(3, "Use existing binding [%s] for
> > WWID [%s]",
> > +                               bdg->alias, wwid);
> > +                       alias = strdup(bdg->alias);
> > +               }
> >                 goto out;
> >         }
> >  
> > -new_alias:
> > -       if (id <= 0) {
> > +       /*
> > +        * old_alias_taken means that the WWID is not in the
> > bindings file, but
> > +        * alias_old is currently taken by a different WWID. We
> > shouldn't added
> > +        * a new binding in this case.
> > +        */
> > +       if (id <= 0 && !old_alias_taken) {
> >                 /*
> >                  * no existing alias was provided, or allocating it
> >                  * failed. Try a new one.
> >                  */
> > -               id = lookup_binding(f, wwid, &alias, prefix, 1);
> > -               if (id == 0 && alias_already_taken(alias, wwid)) {
> > -                       free(alias);
> > -                       alias = NULL;
> > -               }
> > +               id = get_free_id(&global_bindings, prefix, wwid);
> >                 if (id <= 0)
> >                         goto out;
> >                 else
> >                         new_binding = true;
> >         }
> >  
> > -       if (fflush(f) != 0) {
> > -               condlog(0, "cannot fflush bindings file stream :
> > %s",
> > -                       strerror(errno));
> > -               goto out;
> > -       }
> > +       if (!bindings_read_only && id > 0)
> > +               alias = allocate_binding(file, wwid, id, prefix);
> >  
> > -       if (can_write && !bindings_read_only) {
> > -               alias = allocate_binding(fd, wwid, id, prefix);
> > -               if (alias && !new_binding)
> > -                       condlog(2, "Allocated existing binding [%s]
> > for WWID [%s]",
> > -                               alias, wwid);
> > -       }
> > +       if (alias && !new_binding)
> > +               condlog(2, "Allocated existing binding [%s] for
> > WWID [%s]",
> > +                       alias, wwid);
> >  
> >  out:
> > -       pthread_cleanup_push(free, alias);
> > -       fclose(f);
> > -       pthread_cleanup_pop(0);
> >         return alias;
> >  }
> >  
> > -int
> > -get_user_friendly_wwid(const char *alias, char *buff, const char
> > *file)
> > +int get_user_friendly_wwid(const char *alias, char *buff)
> >  {
> > -       int fd, unused;
> > -       FILE *f;
> > +       const struct binding *bdg;
> >  
> >         if (!alias || *alias == '\0') {
> >                 condlog(3, "Cannot find binding for empty alias");
> >                 return -1;
> >         }
> >  
> > -       fd = open_file(file, &unused, bindings_file_header);
> > -       if (fd < 0)
> > -               return -1;
> > -
> > -       f = fdopen(fd, "r");
> > -       if (!f) {
> > -               condlog(0, "cannot fdopen on bindings file
> > descriptor : %s",
> > -                       strerror(errno));
> > -               close(fd);
> > +       bdg = get_binding_for_alias(&global_bindings, alias);
> > +       if (!bdg) {
> > +               *buff = '\0';
> >                 return -1;
> >         }
> > -
> > -       rlookup_binding(f, buff, alias);
> > -       if (!strlen(buff)) {
> > -               fclose(f);
> > -               return -1;
> > -       }
> > -
> > -       fclose(f);
> I think you mean bdg->wwid here.
> 

Argh, thanks for spotting this.

Martin

> -Ben
> 
> > +       strlcpy(buff, bdg->alias, WWID_SIZE);
> >         return 0;
> >  }
> >  
> > diff --git a/libmultipath/alias.h b/libmultipath/alias.h
> > index 37b49d9..5ef6720 100644
> > --- a/libmultipath/alias.h
> > +++ b/libmultipath/alias.h
> > @@ -2,7 +2,7 @@
> >  #define _ALIAS_H
> >  
> >  int valid_alias(const char *alias);
> > -int get_user_friendly_wwid(const char *alias, char *buff, const
> > char *file);
> > +int get_user_friendly_wwid(const char *alias, char *buff);
> >  char *get_user_friendly_alias(const char *wwid, const char *file,
> >                               const char *alias_old,
> >                               const char *prefix, bool
> > bindings_read_only);
> > diff --git a/libmultipath/configure.c b/libmultipath/configure.c
> > index 029fbbd..d809490 100644
> > --- a/libmultipath/configure.c
> > +++ b/libmultipath/configure.c
> > @@ -1378,8 +1378,7 @@ static int _get_refwwid(enum mpath_cmds cmd,
> > const char *dev,
> >                         refwwid = tmpwwid;
> >  
> >                 /* or may be a binding */
> > -               else if (get_user_friendly_wwid(dev, tmpwwid,
> > -                                               conf-
> > >bindings_file) == 0)
> > +               else if (get_user_friendly_wwid(dev, tmpwwid) == 0)
> >                         refwwid = tmpwwid;
> >  
> >                 /* or may be an alias */
> > -- 
> > 2.41.0
> 

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 04/21] libmultipath: never allocate an alias that's already taken
  2023-09-07  7:24     ` Martin Wilck
@ 2023-09-07 13:33       ` Martin Wilck
  2023-09-07 14:22         ` Martin Wilck
  0 siblings, 1 reply; 55+ messages in thread
From: Martin Wilck @ 2023-09-07 13:33 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: dm-devel, David Bond

On Thu, 2023-09-07 at 09:24 +0200, Martin Wilck wrote:
> On Wed, 2023-09-06 at 17:42 -0500, Benjamin Marzinski wrote:
> > On Fri, Sep 01, 2023 at 08:02:17PM +0200, mwilck@suse.com wrote:
> > 
> > 
> > Again, unless I'm overlooking something, I don't think we need to
> > check if the alias is already taken here. Since we know that a
> > device
> > already exists with alias_old and the correct WWID, as long as
> > alias_old
> > is a valid user_friendly_name we can just use it.
> 
> Similar reasoning as above. We could perhaps remove these checks, but
> we'd need to replace them by comments explaining why this condition
> can't occur.
> 
> We could (and maybe should) move the call to find_existing_alias()
> from
> add_map_with_path() to get_user_friendly_alias(), so that we have the
> entire alias logic in a single place. The mpp->alias_old field would
> then only be used for ACT_RENAME.

Well, if we do this, we would need to pass vecs->mpvec to
get_user_friendly_alias(), which means that we could use it also for
the alias_already_taken() check. It's not exactly the same because in
one case we look at internal state and in the other case at kernel
state. OTOH, we do trust that the two are in agreement, right? And we
derive alias_old from the mpvec today, anyway.

Do you agree?

Regards
Martin

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 04/21] libmultipath: never allocate an alias that's already taken
  2023-09-07 13:33       ` Martin Wilck
@ 2023-09-07 14:22         ` Martin Wilck
  0 siblings, 0 replies; 55+ messages in thread
From: Martin Wilck @ 2023-09-07 14:22 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: dm-devel, David Bond

On Thu, 2023-09-07 at 15:33 +0200, Martin Wilck wrote:
> On Thu, 2023-09-07 at 09:24 +0200, Martin Wilck wrote:
> > On Wed, 2023-09-06 at 17:42 -0500, Benjamin Marzinski wrote:
> > > On Fri, Sep 01, 2023 at 08:02:17PM +0200, mwilck@suse.com wrote:
> > > 
> > > 
> > > Again, unless I'm overlooking something, I don't think we need to
> > > check if the alias is already taken here. Since we know that a
> > > device
> > > already exists with alias_old and the correct WWID, as long as
> > > alias_old
> > > is a valid user_friendly_name we can just use it.
> > 
> > Similar reasoning as above. We could perhaps remove these checks,
> > but
> > we'd need to replace them by comments explaining why this condition
> > can't occur.
> > 
> > We could (and maybe should) move the call to find_existing_alias()
> > from
> > add_map_with_path() to get_user_friendly_alias(), so that we have
> > the
> > entire alias logic in a single place. The mpp->alias_old field
> > would
> > then only be used for ACT_RENAME.
> 
> Well, if we do this, we would need to pass vecs->mpvec to
> get_user_friendly_alias(), which means that we could use it also for
> the alias_already_taken() check. It's not exactly the same because in
> one case we look at internal state and in the other case at kernel
> state. OTOH, we do trust that the two are in agreement, right? And we
> derive alias_old from the mpvec today, anyway.

Ok, this doesn't work. The kernel device mapper mandates no naming
conventions for map aliases / names, except that they're unique.
The mpvec contains only aliases of multipath maps. But it's not
forbidden that some non-multipath map be called "mpathc".

OTOH, if we have positive evidence that a given WWID has an alias
mpathX assigned, we do know that no other map can have this same name.
It just comes down to whether we trust the mpvec to correctly represent
kernel state, and I suppose we have to, anyway.

Sorry for the spam. I'll just remove these these checks, and the
corresponding test cases.

Martin

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 18/21] libmultipath: keep bindings in memory
  2023-09-07 10:30     ` Martin Wilck
@ 2023-09-07 19:14       ` Benjamin Marzinski
  2023-09-07 20:02         ` Benjamin Marzinski
  0 siblings, 1 reply; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-07 19:14 UTC (permalink / raw)
  To: Martin Wilck; +Cc: dm-devel

On Thu, Sep 07, 2023 at 12:30:53PM +0200, Martin Wilck wrote:
> On Wed, 2023-09-06 at 17:47 -0500, Benjamin Marzinski wrote:
> > On Fri, Sep 01, 2023 at 08:02:31PM +0200, mwilck@suse.com wrote:
> > > From: Martin Wilck <mwilck@suse.com>
> > > 

> > > +       vector_foreach_slot(bindings, bdg, i) {
> > > +               int curr_id = scan_devname(bdg->alias, prefix);
> > 
> > If we know that the bindings will be sorted by alias order, we don't
> > need to do all this. Something like this should work:
> 
> That's true. Unfortunately, we can't ensure ordering "by alias order".
> The reason is that our configuration allows using different alias
> prefixes for different devices. The current sorting is simply
> alphabetical. It detects duplicates, but it ensures "alias order" only
> between "mpatha" and "mpathz".
> 
> I've thought of just removing the "different aliases for different
> devices" feature. I don't know if any users out there actually set
> device-specific alias_prefix values in the devices section of
> multipath.conf. I don't recall having seen such a configuration so far;
> almost every config I have seen simply uses "mpath" everywhere. But I
> recognize that it may feel tempting in some cases. One could use the
> "NETAPP" prefix for some arrays and the "EMC" prefix for others, for
> example, making it easier to see which is what. And we simply don't
> know if we'd break existing user setups with such a change. So if at
> all, we can't do it in a minor release without deprecating it first.
> 
> When we add a binding in add_binding(), we don't know which
> alias_prefix is configured for a given WWID, and we have no easy way to
> figure it out. We know the alias, but we don't know which portion of it
> is the prefix and which is the ID (it's not forbidden to use "aaaa" as
> alias_prefix). I wouldn't want to start guessing.
> 
> If you can think of a way to keep the bindings cleanly sorted, please
> let me know.

Doh! You even mentioned this issue in your "fix alias tests" commit. I
just didn't pay enough attention to it. But I believe there is a way to
make sure that we are dealing with a correctly sorted list of potential
bindings when calling get_free_id(), if we decide it's worth the extra
work.

The global_bindings isn't guaranteed to have all the bindings for our
prefix correctly sorted, but they will all be in a group.  If we wanted,
we could create a new vector that just included these bindings, and sort
it using the current prefix and a comparison function to do alias-sytle
sorting. the msort_r() function takes an extra argument to pass to the
compar() function, which could be the prefix.

It would be great if we could just sort part of the global_bindings
vector in place, once we knew the prefix.  Unfortunately, that would
only work if we could know that no prefix can ever match a starting
substring of another prefix. Otherwise, sorting using one can make the
other not be in a single continuous group.  For instance, if some device
configs used "mpatha" as a prefix, they would expect "mpathaaa" to
follow immediately after "mapthaz", but if the bindings file had already
been sorted using the "mpath" prefix, then "mpathba" would follow
"mpathaz", and "mpathaaa" would come much later. Keeping the global file
alphabetically sorted guarantees that no matter the prefix that devices
were added under, all device aliases that are valid with that prefix
will be in a group together.
 
So, is it worth it to make another vector, copy the alises which are
valid with the current prefix into it, and then sort it? get_free_id()
will be cleaner, and gap detection won't break down after you get past
mpathaa, which it currently does with an alpahbetically sorted bindings
list.

Alternatively, we could just decide that setting a prefix in a device
config that matches the starting substring of a another prefix is a
config error (obviously using the exact same prefix is fine), and ignore
that prefix from the device config when we parse the configuration. Then
we could read in the bindings alphabetically like we currently do, find
the prefix groups in them, and sort them once, in-place. When allocating
a binding, we would need to search backwards through the bindings till
we found the end of the group matching our prefix (or an alias that
comes alphabetically before our prefix). Then we would have to search
backwards through our prefix group using the id, till we found a binding
with a smaller id.

Thoughts?

-Ben

> > 
> >                 if (curr_id <= 0)
> >                         continue;
> > 
> >                 while (id < curr_id) {
> >                         if (!id_already_taken(id, prefix, map_wwid))
> >                                 return id;
> >                         id++;
> >                 }
> >                 if (id == INT_MAX)
> >                         break;
> >                 id++;
> >         }
> >         if (id == INT_MAX)
> >                 condlog(0, "no more available user_friendly_names");
> >         return id < INT_MAX ? id : -1;
> > }
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 18/21] libmultipath: keep bindings in memory
  2023-09-07 19:14       ` Benjamin Marzinski
@ 2023-09-07 20:02         ` Benjamin Marzinski
  2023-09-07 20:43           ` Martin Wilck
  0 siblings, 1 reply; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-07 20:02 UTC (permalink / raw)
  To: Martin Wilck; +Cc: dm-devel

On Thu, Sep 07, 2023 at 02:14:04PM -0500, Benjamin Marzinski wrote:
> On Thu, Sep 07, 2023 at 12:30:53PM +0200, Martin Wilck wrote:
> > On Wed, 2023-09-06 at 17:47 -0500, Benjamin Marzinski wrote:
> > > On Fri, Sep 01, 2023 at 08:02:31PM +0200, mwilck@suse.com wrote:
> > > > From: Martin Wilck <mwilck@suse.com>
> > > > 
> 
> > > > +       vector_foreach_slot(bindings, bdg, i) {
> > > > +               int curr_id = scan_devname(bdg->alias, prefix);
> > > 
> > > If we know that the bindings will be sorted by alias order, we don't
> > > need to do all this. Something like this should work:
> > 
> > That's true. Unfortunately, we can't ensure ordering "by alias order".
> > The reason is that our configuration allows using different alias
> > prefixes for different devices. The current sorting is simply
> > alphabetical. It detects duplicates, but it ensures "alias order" only
> > between "mpatha" and "mpathz".
> > 
> > I've thought of just removing the "different aliases for different
> > devices" feature. I don't know if any users out there actually set
> > device-specific alias_prefix values in the devices section of
> > multipath.conf. I don't recall having seen such a configuration so far;
> > almost every config I have seen simply uses "mpath" everywhere. But I
> > recognize that it may feel tempting in some cases. One could use the
> > "NETAPP" prefix for some arrays and the "EMC" prefix for others, for
> > example, making it easier to see which is what. And we simply don't
> > know if we'd break existing user setups with such a change. So if at
> > all, we can't do it in a minor release without deprecating it first.
> > 
> > When we add a binding in add_binding(), we don't know which
> > alias_prefix is configured for a given WWID, and we have no easy way to
> > figure it out. We know the alias, but we don't know which portion of it
> > is the prefix and which is the ID (it's not forbidden to use "aaaa" as
> > alias_prefix). I wouldn't want to start guessing.
> > 
> > If you can think of a way to keep the bindings cleanly sorted, please
> > let me know.
> 
> Doh! You even mentioned this issue in your "fix alias tests" commit. I
> just didn't pay enough attention to it. But I believe there is a way to
> make sure that we are dealing with a correctly sorted list of potential
> bindings when calling get_free_id(), if we decide it's worth the extra
> work.
> 
> The global_bindings isn't guaranteed to have all the bindings for our
> prefix correctly sorted, but they will all be in a group.  If we wanted,
> we could create a new vector that just included these bindings, and sort
> it using the current prefix and a comparison function to do alias-sytle
> sorting. the msort_r() function takes an extra argument to pass to the
> compar() function, which could be the prefix.
> 
> It would be great if we could just sort part of the global_bindings
> vector in place, once we knew the prefix.  Unfortunately, that would
> only work if we could know that no prefix can ever match a starting
> substring of another prefix. Otherwise, sorting using one can make the
> other not be in a single continuous group.  For instance, if some device
> configs used "mpatha" as a prefix, they would expect "mpathaaa" to
> follow immediately after "mapthaz", but if the bindings file had already
> been sorted using the "mpath" prefix, then "mpathba" would follow
> "mpathaz", and "mpathaaa" would come much later. Keeping the global file
> alphabetically sorted guarantees that no matter the prefix that devices
> were added under, all device aliases that are valid with that prefix
> will be in a group together.
>  
> So, is it worth it to make another vector, copy the alises which are
> valid with the current prefix into it, and then sort it? get_free_id()
> will be cleaner, and gap detection won't break down after you get past
> mpathaa, which it currently does with an alpahbetically sorted bindings
> list.
> 
> Alternatively, we could just decide that setting a prefix in a device
> config that matches the starting substring of a another prefix is a
> config error (obviously using the exact same prefix is fine), and ignore
> that prefix from the device config when we parse the configuration. Then
> we could read in the bindings alphabetically like we currently do, find
> the prefix groups in them, and sort them once, in-place. When allocating
> a binding, we would need to search backwards through the bindings till
> we found the end of the group matching our prefix (or an alias that
> comes alphabetically before our prefix). Then we would have to search
> backwards through our prefix group using the id, till we found a binding
> with a smaller id.

So, thinking about this a little more, assuming we would be writing out
the bindings file in full alias sorted order, it's stupid to sort it
alphabetically, and then resort it back to alias order.  We should
obviously read it in using the same method as when allocating a binding.
Where you search backwards till you find your prefix group, and then
find the proper spot in the prefix group using alias sorting.  Aliases
that aren't valid for any configured prefix (these could have been added
by hand to the bindings file, or possibly they were created with a
different multipath configuration) will just get sorted alphabetically
into the bindings list, between the prefix groups.

Also, it's not enough to just ignore any device config prefix that is a
starting substring of another prefix. The device config prefixes also
cannot be superstrings of the default prefix, so if the default prefix
is "mpath", both of the device config prefixes "mp" and "mpathdev" would
need to be ignored.

-Ben 
 
> Thoughts?
> 
> -Ben
> 
> > > 
> > >                 if (curr_id <= 0)
> > >                         continue;
> > > 
> > >                 while (id < curr_id) {
> > >                         if (!id_already_taken(id, prefix, map_wwid))
> > >                                 return id;
> > >                         id++;
> > >                 }
> > >                 if (id == INT_MAX)
> > >                         break;
> > >                 id++;
> > >         }
> > >         if (id == INT_MAX)
> > >                 condlog(0, "no more available user_friendly_names");
> > >         return id < INT_MAX ? id : -1;
> > > }
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 18/21] libmultipath: keep bindings in memory
  2023-09-07 20:02         ` Benjamin Marzinski
@ 2023-09-07 20:43           ` Martin Wilck
  2023-09-08 17:22             ` Benjamin Marzinski
  0 siblings, 1 reply; 55+ messages in thread
From: Martin Wilck @ 2023-09-07 20:43 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: dm-devel

On Thu, 2023-09-07 at 15:02 -0500, Benjamin Marzinski wrote:
> On Thu, Sep 07, 2023 at 02:14:04PM -0500, Benjamin Marzinski wrote:
> > On Thu, Sep 07, 2023 at 12:30:53PM +0200, Martin Wilck wrote:
> > > On Wed, 2023-09-06 at 17:47 -0500, Benjamin Marzinski wrote:
> > > > On Fri, Sep 01, 2023 at 08:02:31PM +0200,
> > > > mwilck@suse.com wrote:
> > > > > From: Martin Wilck <mwilck@suse.com>
> > > > > 
> > 
> > > > > +       vector_foreach_slot(bindings, bdg, i) {
> > > > > +               int curr_id = scan_devname(bdg->alias,
> > > > > prefix);
> > > > 
> > > > If we know that the bindings will be sorted by alias order, we
> > > > don't
> > > > need to do all this. Something like this should work:
> > > 
> > > That's true. Unfortunately, we can't ensure ordering "by alias
> > > order".
> > > The reason is that our configuration allows using different alias
> > > prefixes for different devices. The current sorting is simply
> > > alphabetical. It detects duplicates, but it ensures "alias order"
> > > only
> > > between "mpatha" and "mpathz".
> > > 
> > > I've thought of just removing the "different aliases for
> > > different
> > > devices" feature. I don't know if any users out there actually
> > > set
> > > device-specific alias_prefix values in the devices section of
> > > multipath.conf. I don't recall having seen such a configuration
> > > so far;
> > > almost every config I have seen simply uses "mpath" everywhere.
> > > But I
> > > recognize that it may feel tempting in some cases. One could use
> > > the
> > > "NETAPP" prefix for some arrays and the "EMC" prefix for others,
> > > for
> > > example, making it easier to see which is what. And we simply
> > > don't
> > > know if we'd break existing user setups with such a change. So if
> > > at
> > > all, we can't do it in a minor release without deprecating it
> > > first.
> > > 
> > > When we add a binding in add_binding(), we don't know which
> > > alias_prefix is configured for a given WWID, and we have no easy
> > > way to
> > > figure it out. We know the alias, but we don't know which portion
> > > of it
> > > is the prefix and which is the ID (it's not forbidden to use
> > > "aaaa" as
> > > alias_prefix). I wouldn't want to start guessing.
> > > 
> > > If you can think of a way to keep the bindings cleanly sorted,
> > > please
> > > let me know.
> > 
> > Doh! You even mentioned this issue in your "fix alias tests"
> > commit. I
> > just didn't pay enough attention to it. But I believe there is a
> > way to
> > make sure that we are dealing with a correctly sorted list of
> > potential
> > bindings when calling get_free_id(), if we decide it's worth the
> > extra
> > work.
> > 
> > The global_bindings isn't guaranteed to have all the bindings for
> > our
> > prefix correctly sorted, but they will all be in a group.  If we
> > wanted,
> > we could create a new vector that just included these bindings, and
> > sort
> > it using the current prefix and a comparison function to do alias-
> > sytle
> > sorting. the msort_r() function takes an extra argument to pass to
> > the
> > compar() function, which could be the prefix.
> > 
> > It would be great if we could just sort part of the global_bindings
> > vector in place, once we knew the prefix.  Unfortunately, that
> > would
> > only work if we could know that no prefix can ever match a starting
> > substring of another prefix. Otherwise, sorting using one can make
> > the
> > other not be in a single continuous group.  For instance, if some
> > device
> > configs used "mpatha" as a prefix, they would expect "mpathaaa" to
> > follow immediately after "mapthaz", but if the bindings file had
> > already
> > been sorted using the "mpath" prefix, then "mpathba" would follow
> > "mpathaz", and "mpathaaa" would come much later. Keeping the global
> > file
> > alphabetically sorted guarantees that no matter the prefix that
> > devices
> > were added under, all device aliases that are valid with that
> > prefix
> > will be in a group together.
> >  
> > So, is it worth it to make another vector, copy the alises which
> > are
> > valid with the current prefix into it, and then sort it?
> > get_free_id()
> > will be cleaner, and gap detection won't break down after you get
> > past
> > mpathaa, which it currently does with an alpahbetically sorted
> > bindings
> > list.
> > 
> > Alternatively, we could just decide that setting a prefix in a
> > device
> > config that matches the starting substring of a another prefix is a
> > config error (obviously using the exact same prefix is fine), and
> > ignore
> > that prefix from the device config when we parse the configuration.
> > Then
> > we could read in the bindings alphabetically like we currently do,
> > find
> > the prefix groups in them, and sort them once, in-place. When
> > allocating
> > a binding, we would need to search backwards through the bindings
> > till
> > we found the end of the group matching our prefix (or an alias that
> > comes alphabetically before our prefix). Then we would have to
> > search
> > backwards through our prefix group using the id, till we found a
> > binding
> > with a smaller id.
> 
> So, thinking about this a little more, assuming we would be writing
> out
> the bindings file in full alias sorted order, it's stupid to sort it
> alphabetically, and then resort it back to alias order.  We should
> obviously read it in using the same method as when allocating a
> binding.
> Where you search backwards till you find your prefix group, and then
> find the proper spot in the prefix group using alias sorting. 
> Aliases
> that aren't valid for any configured prefix (these could have been
> added
> by hand to the bindings file, or possibly they were created with a
> different multipath configuration) will just get sorted
> alphabetically
> into the bindings list, between the prefix groups.
> 
> Also, it's not enough to just ignore any device config prefix that is
> a
> starting substring of another prefix. The device config prefixes also
> cannot be superstrings of the default prefix, so if the default
> prefix
> is "mpath", both of the device config prefixes "mp" and "mpathdev"
> would
> need to be ignored.

I don't quite understand yet. Assume we read the bindings file and
encounter an alias "mpathab". Is this alias #28 with prefix "mpath", or
is it #2 with prefix "mpatha", or perhaps alias guess-what  (order
"thab") with prefix "mpa"? How would we know at this point in the code?
Are you suggesting that we create a list of all configured prefixes and
compare the bindings we read with each of them?

Our bindings list is now partially sorted, which is an improvement wrt
the previous situation. "missing the gap" is not really an awful
problem [*]. Perhaps we could postpone this for after this patch set,
and give it some more time to sink in?

Martin

[*] I admit that with my patch, we _know_ now that the bindings list
will be sub-optimally sorted as soon as mpathaa is reached, whereas
before the ordering might be perfect even with a large number of
aliases, depending on the history of the bindings file. That's not a
change for the better; it will cause the gap to be missed in some
situations where we don't miss it now. I am not sure how bad this is.

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 18/21] libmultipath: keep bindings in memory
  2023-09-07 20:43           ` Martin Wilck
@ 2023-09-08 17:22             ` Benjamin Marzinski
  2023-09-11  6:25               ` Martin Wilck
  0 siblings, 1 reply; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-08 17:22 UTC (permalink / raw)
  To: Martin Wilck; +Cc: dm-devel

On Thu, Sep 07, 2023 at 10:43:27PM +0200, Martin Wilck wrote:
> On Thu, 2023-09-07 at 15:02 -0500, Benjamin Marzinski wrote:
> > On Thu, Sep 07, 2023 at 02:14:04PM -0500, Benjamin Marzinski wrote:
> > > On Thu, Sep 07, 2023 at 12:30:53PM +0200, Martin Wilck wrote:
> > > > On Wed, 2023-09-06 at 17:47 -0500, Benjamin Marzinski wrote:
> > > > > On Fri, Sep 01, 2023 at 08:02:31PM +0200,
> > > > > mwilck@suse.com wrote:
> > > > > > From: Martin Wilck <mwilck@suse.com>
> > > Doh! You even mentioned this issue in your "fix alias tests"
> > > commit. I
> > > just didn't pay enough attention to it. But I believe there is a
> > > way to
> > > make sure that we are dealing with a correctly sorted list of
> > > potential
> > > bindings when calling get_free_id(), if we decide it's worth the
> > > extra
> > > work.
> > > 
> > > The global_bindings isn't guaranteed to have all the bindings for
> > > our
> > > prefix correctly sorted, but they will all be in a group.  If we
> > > wanted,
> > > we could create a new vector that just included these bindings, and
> > > sort
> > > it using the current prefix and a comparison function to do alias-
> > > sytle
> > > sorting. the msort_r() function takes an extra argument to pass to
> > > the
> > > compar() function, which could be the prefix.
> > > 
> > > It would be great if we could just sort part of the global_bindings
> > > vector in place, once we knew the prefix.  Unfortunately, that
> > > would
> > > only work if we could know that no prefix can ever match a starting
> > > substring of another prefix. Otherwise, sorting using one can make
> > > the
> > > other not be in a single continuous group.  For instance, if some
> > > device
> > > configs used "mpatha" as a prefix, they would expect "mpathaaa" to
> > > follow immediately after "mapthaz", but if the bindings file had
> > > already
> > > been sorted using the "mpath" prefix, then "mpathba" would follow
> > > "mpathaz", and "mpathaaa" would come much later. Keeping the global
> > > file
> > > alphabetically sorted guarantees that no matter the prefix that
> > > devices
> > > were added under, all device aliases that are valid with that
> > > prefix
> > > will be in a group together.
> > >  
> > > So, is it worth it to make another vector, copy the alises which
> > > are
> > > valid with the current prefix into it, and then sort it?
> > > get_free_id()
> > > will be cleaner, and gap detection won't break down after you get
> > > past
> > > mpathaa, which it currently does with an alpahbetically sorted
> > > bindings
> > > list.
> > > 
> > > Alternatively, we could just decide that setting a prefix in a
> > > device
> > > config that matches the starting substring of a another prefix is a
> > > config error (obviously using the exact same prefix is fine), and
> > > ignore
> > > that prefix from the device config when we parse the configuration.
> > > Then
> > > we could read in the bindings alphabetically like we currently do,
> > > find
> > > the prefix groups in them, and sort them once, in-place. When
> > > allocating
> > > a binding, we would need to search backwards through the bindings
> > > till
> > > we found the end of the group matching our prefix (or an alias that
> > > comes alphabetically before our prefix). Then we would have to
> > > search
> > > backwards through our prefix group using the id, till we found a
> > > binding
> > > with a smaller id.
> > 
> > So, thinking about this a little more, assuming we would be writing
> > out
> > the bindings file in full alias sorted order, it's stupid to sort it
> > alphabetically, and then resort it back to alias order.  We should
> > obviously read it in using the same method as when allocating a
> > binding.
> > Where you search backwards till you find your prefix group, and then
> > find the proper spot in the prefix group using alias sorting. 
> > Aliases
> > that aren't valid for any configured prefix (these could have been
> > added
> > by hand to the bindings file, or possibly they were created with a
> > different multipath configuration) will just get sorted
> > alphabetically
> > into the bindings list, between the prefix groups.
> > 
> > Also, it's not enough to just ignore any device config prefix that is
> > a
> > starting substring of another prefix. The device config prefixes also
> > cannot be superstrings of the default prefix, so if the default
> > prefix
> > is "mpath", both of the device config prefixes "mp" and "mpathdev"
> > would
> > need to be ignored.
> 
> I don't quite understand yet. Assume we read the bindings file and
> encounter an alias "mpathab". Is this alias #28 with prefix "mpath", or
> is it #2 with prefix "mpatha", or perhaps alias guess-what  (order
> "thab") with prefix "mpa"? How would we know at this point in the code?
> Are you suggesting that we create a list of all configured prefixes and
> compare the bindings we read with each of them?

So, the crux of my idea is that we disallow setting the alias_prefix to
conflicting values like this in the config. After we parse the
configuration, we have to go through all the alias_prefix settings and
sanitize them.  If there's a alias_prefix in the overrides section,
there's nothing to do, since all devices will use the same prefix.
Otherwise, we need to make sure that no prefixes in the devices section
are either starting substrings or starting superstrings of the default
alias prefix. Additionally, we need to make sure that no devices section
alias prefixes are starting substrings of another devices section alias
prefix. If there is an invalid devices section alias_prefix, we need to
print an error message and ignore it.

For instance, say that the default prefix is "mpath". If there was a
devices section prefix that was "mp" or "mpathdev", we would print a
error and simply ignore that setting, since "mp" is a starting
substring of the default prefix, and "mpathdev" is a staring superstring
of the default prefix. Devices of that type would just use the default
"mpath" prefix.  Similarly, if there were two separate device section
configs that had alias prefixes of "emc" and "emcdev", we would ignore
the "emc" prefix, since it is a staring substring of "emcdev" (or we
could choose to ignore the superstring prefixes for conflicts between
two device section configs, it doesn't really matter which one we
ignore).

I realize that this is a new limitiation we are adding to multipath.
Users were previously able to have device specific alias prefixes of
"mp", for instance. But I would argue that we don't need to support
conflicting prefixes.  Obviously, distrubutions may want to avoid adding
this change until they are able to safely make non-backwards compatible
changs. But I have pretty strong doubts that this change would
negatively effect anyone. 

After parsing the config file, we will have a list of valid prefixes
that do not conflict. Thus if strncmp(bdg->alias, prefix,
strlen(prefix)) matches for one of the prefixes, it will not match for
any other. Armed with this list of valid prefixes, when we add bindings
to our list, we are able to sort them in alias order.  Now, when we read
the the bindings from the bindings file, it's possible that some of them
will not match any of our valid prefixes, but that's fine. If an alias
doesn't match a valid prefix, we won't sort it in alias order, because
we can't.  But since we only give out aliases that use the valid
prefixes, we will never need to search through those non-matching
aliases to find a free one. So we don't care how they are ordered, just
so long as they don't come in the middle of a valid prefix group.

To add a new binding, we first figure out if its alias matches a valid
prefix. If it doesn't, we simply scan backwards through our existing
list of bindings till we find an alias that comes alphabetically before
it, and add the binding after that. If it does match a valid prefix, we
scan backwards looking for the first alias that matches the prefix or is
before it alphabetically. If we find a binding whose alias comes before
it, then there are no devices with this prefix, and we add the new
binding after it. If we find a binding with an alias whose start matches
this prefix, we found our prefix group. Then we start searching
backwards by ID until we either find a binding with an ID smaller than
ours or we find a binding whose alias doesn't match our prefix, and we
add the binding after it.


 
> Our bindings list is now partially sorted, which is an improvement wrt
> the previous situation. "missing the gap" is not really an awful
> problem [*]. Perhaps we could postpone this for after this patch set,
> and give it some more time to sink in?

Yep. I'm fine with going ahead with this patchset as it is. Both sorting
the bindings in alias order and updating the bindings if
/etc/multipath/bindings has changed are things that can get looked at
afterwards. And I'm fine with doing this work, if you want.

-Ben
 
> Martin
> 
> [*] I admit that with my patch, we _know_ now that the bindings list
> will be sub-optimally sorted as soon as mpathaa is reached, whereas
> before the ordering might be perfect even with a large number of
> aliases, depending on the history of the bindings file. That's not a
> change for the better; it will cause the gap to be missed in some
> situations where we don't miss it now. I am not sure how bad this is.
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 18/21] libmultipath: keep bindings in memory
  2023-09-08 17:22             ` Benjamin Marzinski
@ 2023-09-11  6:25               ` Martin Wilck
  2023-09-11 14:47                 ` Benjamin Marzinski
  0 siblings, 1 reply; 55+ messages in thread
From: Martin Wilck @ 2023-09-11  6:25 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: dm-devel

On Fri, 2023-09-08 at 12:22 -0500, Benjamin Marzinski wrote:
> On Thu, Sep 07, 2023 at 10:43:27PM +0200, Martin Wilck wrote: 
> > Our bindings list is now partially sorted, which is an improvement
> > wrt
> > the previous situation. "missing the gap" is not really an awful
> > problem [*]. Perhaps we could postpone this for after this patch
> > set,
> > and give it some more time to sink in?
> 
> Yep. I'm fine with going ahead with this patchset as it is. Both
> sorting
> the bindings in alias order and updating the bindings if
> /etc/multipath/bindings has changed are things that can get looked at
> afterwards. And I'm fine with doing this work, if you want.

It so happens that, by sudden inspiration, I've found an elegant
solution to this problem (I think). We can take advantage of the fact
that, for any given prefix, aliases with shorter string length will be
sorted before longer ones ("mpathz" < "mpathaa"). By sorting the
aliases by string length, and use alphabetical sorting only for strings
of equal length, we obtain total ordering for any given prefix. This
works for any number of different prefixes, and even if some prefixes
are substrings of others. In the ordered list, the aliases with a given
prefix will not necessarily be in a contiguous block, but that doesn't
matter. For every prefix, the sub-list of aliases starting with that
prefix is cleanly ordered. This way we avoid the complexity to have to
parse or compare configured prefixes.

I'll post a new patch set with this ordering scheme hopefully later
today.

Regards,
Martin

> 
> -Ben
>  
> > Martin
> > 
> > [*] I admit that with my patch, we _know_ now that the bindings
> > list
> > will be sub-optimally sorted as soon as mpathaa is reached, whereas
> > before the ordering might be perfect even with a large number of
> > aliases, depending on the history of the bindings file. That's not
> > a
> > change for the better; it will cause the gap to be missed in some
> > situations where we don't miss it now. I am not sure how bad this
> > is.
> 

--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [dm-devel] [PATCH 18/21] libmultipath: keep bindings in memory
  2023-09-11  6:25               ` Martin Wilck
@ 2023-09-11 14:47                 ` Benjamin Marzinski
  0 siblings, 0 replies; 55+ messages in thread
From: Benjamin Marzinski @ 2023-09-11 14:47 UTC (permalink / raw)
  To: Martin Wilck; +Cc: dm-devel

On Mon, Sep 11, 2023 at 08:25:05AM +0200, Martin Wilck wrote:
> On Fri, 2023-09-08 at 12:22 -0500, Benjamin Marzinski wrote:
> > On Thu, Sep 07, 2023 at 10:43:27PM +0200, Martin Wilck wrote: 
> > > Our bindings list is now partially sorted, which is an improvement
> > > wrt
> > > the previous situation. "missing the gap" is not really an awful
> > > problem [*]. Perhaps we could postpone this for after this patch
> > > set,
> > > and give it some more time to sink in?
> > 
> > Yep. I'm fine with going ahead with this patchset as it is. Both
> > sorting
> > the bindings in alias order and updating the bindings if
> > /etc/multipath/bindings has changed are things that can get looked at
> > afterwards. And I'm fine with doing this work, if you want.
> 
> It so happens that, by sudden inspiration, I've found an elegant
> solution to this problem (I think). We can take advantage of the fact
> that, for any given prefix, aliases with shorter string length will be
> sorted before longer ones ("mpathz" < "mpathaa"). By sorting the
> aliases by string length, and use alphabetical sorting only for strings
> of equal length, we obtain total ordering for any given prefix. This
> works for any number of different prefixes, and even if some prefixes
> are substrings of others. In the ordered list, the aliases with a given
> prefix will not necessarily be in a contiguous block, but that doesn't
> matter. For every prefix, the sub-list of aliases starting with that
> prefix is cleanly ordered. This way we avoid the complexity to have to
> parse or compare configured prefixes.

Clever. That seems like a good solution.

-Ben 

> 
> I'll post a new patch set with this ordering scheme hopefully later
> today.
> 
> Regards,
> Martin
> 
> > 
> > -Ben
> >  
> > > Martin
> > > 
> > > [*] I admit that with my patch, we _know_ now that the bindings
> > > list
> > > will be sub-optimally sorted as soon as mpathaa is reached, whereas
> > > before the ordering might be perfect even with a large number of
> > > aliases, depending on the history of the bindings file. That's not
> > > a
> > > change for the better; it will cause the gap to be missed in some
> > > situations where we don't miss it now. I am not sure how bad this
> > > is.
> > 
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


^ permalink raw reply	[flat|nested] 55+ messages in thread

end of thread, other threads:[~2023-09-11 14:48 UTC | newest]

Thread overview: 55+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-09-01 18:02 [dm-devel] [PATCH 00/21] multipath-tools: user-friendly names rework mwilck
2023-09-01 18:02 ` [dm-devel] [PATCH 01/21] libmultipath: sysfs_set_scsi_tmo: do nothing for ACT_DRY_RUN mwilck
2023-09-06 22:42   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 02/21] libmultipath: add alias_already_taken() mwilck
2023-09-06 22:42   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 03/21] libmultipath: unify use_existing_alias() and get_user_friendly_alias() mwilck
2023-09-06 22:42   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 04/21] libmultipath: never allocate an alias that's already taken mwilck
2023-09-06 22:42   ` Benjamin Marzinski
2023-09-07  7:24     ` Martin Wilck
2023-09-07 13:33       ` Martin Wilck
2023-09-07 14:22         ` Martin Wilck
2023-09-01 18:02 ` [dm-devel] [PATCH 05/21] libmultipath: lookup_binding: add comment about the algorithm mwilck
2023-09-06 22:43   ` Benjamin Marzinski
2023-09-07  8:22     ` Martin Wilck
2023-09-01 18:02 ` [dm-devel] [PATCH 06/21] multipath-tools test: simplify debugging for condlog mismatch mwilck
2023-09-06 22:43   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 07/21] multipath-tools tests: add tests for get_user_friendly_alias() mwilck
2023-09-06 22:43   ` Benjamin Marzinski
2023-09-07  7:55     ` Martin Wilck
2023-09-01 18:02 ` [dm-devel] [PATCH 08/21] multipath-tools test: consistent use of macros in alias test mwilck
2023-09-06 22:43   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 09/21] multipath-tools tests: convert mock_{failed, used}_alias to macros mwilck
2023-09-06 22:44   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 10/21] multipath-tools test: use mock_bindings_file() consistently mwilck
2023-09-06 22:43   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 11/21] libmultipath: add global variable for current bindings mwilck
2023-09-06 22:44   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 12/21] libmultipath: rename fix_bindings_file() to update_bindings_file() mwilck
2023-09-06 22:44   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 13/21] libmultipath: alias.c: move bindings related code up mwilck
2023-09-06 22:44   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 14/21] libmultipath: update_bindings_file: take filename argument mwilck
2023-09-06 22:45   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 15/21] libmultipath: update_bindings_file: use a single write() mwilck
2023-09-06 22:45   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 16/21] libmultipath: update_bindings_file: don't log temp file name mwilck
2023-09-06 22:45   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 17/21] libmultipath: alias.c: factor out read_binding() mwilck
2023-09-06 22:45   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 18/21] libmultipath: keep bindings in memory mwilck
2023-09-06 22:47   ` Benjamin Marzinski
2023-09-07 10:30     ` Martin Wilck
2023-09-07 19:14       ` Benjamin Marzinski
2023-09-07 20:02         ` Benjamin Marzinski
2023-09-07 20:43           ` Martin Wilck
2023-09-08 17:22             ` Benjamin Marzinski
2023-09-11  6:25               ` Martin Wilck
2023-09-11 14:47                 ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 19/21] multipath-tools tests: fix alias tests mwilck
2023-09-06 22:47   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 20/21] libmultipath: dm_get_uuid(): return emtpy UUID for non-existing maps mwilck
2023-09-06 22:47   ` Benjamin Marzinski
2023-09-01 18:02 ` [dm-devel] [PATCH 21/21] libmultipath: adapt to new semantics of dm_get_uuid() mwilck
2023-09-06 22:47   ` Benjamin Marzinski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox