From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Fri, 25 Aug 2023 17:55:55 +0200 Subject: [PATCH] multipath-tools tests: fix alias tests 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 Reviewed-by: Benjamin Marzinski Signed-off-by: Benjamin Marzinski --- tests/alias.c | 957 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 614 insertions(+), 343 deletions(-) diff --git a/tests/alias.c b/tests/alias.c index f334f928..50a21ecf 100644 --- a/tests/alias.c +++ b/tests/alias.c @@ -3,10 +3,12 @@ #include #include #include +#include "strbuf.h" #include "util.h" #include "alias.h" #include "test-log.h" #include +#include #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,23 +33,44 @@ static int __set_errno(int err) } } -off_t __wrap_lseek(int fd, off_t offset, int whence) +/* + * 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) { - return __set_errno(mock_type(int)); + 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); + assert_ptr_not_equal(start, NULL); + return __set_errno(mock_type(int)); } -ssize_t __wrap_write(int fd, const void *buf, size_t count) +int __wrap_rename(const char *old, const char *new) { - check_expected(count); - check_expected(buf); return __set_errno(mock_type(int)); } -int __wrap_ftruncate(int fd, off_t length) +int __wrap_mkstemp(char *template) { - check_expected(length); - return __set_errno(mock_type(int)); + return 10; } int __wrap_dm_map_present(const char * 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) +{ + 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 *)) { - static char cnt[1024]; - char *token; + 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,84 +859,200 @@ 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, 3); + assert_int_equal(rc, 2); assert_ptr_equal(alias, NULL); } -static void lb_nomatch_b_a(void **state) +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", -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); } -static void lb_nomatch_b_a_3_used(void **state) +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", -1); - mock_used_alias("MPATHaa", "WWID2"); - mock_used_alias("MPATHab", "WWID2"); - mock_used_alias("MPATHac", "WWID2"); - mock_unused_alias("MPATHad"); + "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, 30); + assert_int_equal(rc, 3); assert_ptr_equal(alias, NULL); } -#ifdef MPATH_ID_INT_MAX -static void do_lb_nomatch_int_max(void **state, int check_if_taken) +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, i; + 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" - "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); + "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_int_max(void **state) +static void lb_nomatch_b_a_3_used(void **state) { - do_lb_nomatch_int_max(state, 0); + int rc; + char *alias; + STRBUF_ON_STACK(buf); + + 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); } -static void lb_nomatch_int_max_check(void **state) +#ifdef MPATH_ID_INT_MAX +/* + * 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) { - do_lb_nomatch_int_max(state, 1); + int rc; + char *alias; + STRBUF_ON_STACK(buf); + + 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, "WWIDNOMORE", &alias, "MPATH", 1); + assert_int_equal(rc, -1); + assert_ptr_equal(alias, NULL); } 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,67 +1349,133 @@ 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_write_partial(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, 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_lseek_err(void **state) +static void al_write_short(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, 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")); @@ -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,32 +1613,59 @@ 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_allocate_binding("MPATHz", "WWID0"); @@ -1380,11 +1678,9 @@ static void gufa_old_empty(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")); alias = get_user_friendly_alias("WWID0", "x", "MPATHz", "MPATH", false); @@ -1394,19 +1690,15 @@ static void gufa_old_match(void **state) { static void gufa_old_match_other(void **state) { char *alias; - static const char bindings[] = "MPATHz WWID9"; - - will_return(__wrap_open_file, true); + static const char bindings[] = "MPATHz WWID9\n"; - 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"); @@ -1415,21 +1707,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); @@ -1439,15 +1726,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"); @@ -1459,13 +1744,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"); @@ -1477,12 +1758,8 @@ 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); 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")); @@ -1495,12 +1772,9 @@ 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); expect_condlog(3, NOMATCH_STR("MPATHz")); - - mock_bindings_file(bindings, 0); expect_condlog(3, FOUND_STR("MPATHa", "WWID0")); mock_used_alias("MPATHa", "WWID0"); @@ -1510,17 +1784,13 @@ 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"; - - will_return(__wrap_open_file, true); + static const char bindings[] = "MPATHb WWID1\n"; - 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_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); @@ -1531,26 +1801,28 @@ static void gufa_old_nomatch_nowwidmatch(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_match), - 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_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_match, 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), }; return cmocka_run_group_tests(tests, NULL, NULL); @@ -1566,7 +1838,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;