Set _FILE_OFFSET_BITS=64 in order to avoid EOVERFLOW error for getdents() syscalls on some file systems (in particular, ext4) in emultated 32bit environments on 64 bit kernel, e.g. when running in qemu. This error occured in arm/v7 CI runs on Ubuntu runners on GitHub, which can be enabled after applying this change. See e.g. https://sourceware.org/bugzilla/show_bug.cgi?id=23960. This change requires fixes for the unit tests: If -D_FILE_OFFSET_BITS=64 is set, glibc headers replace some functions with their 64bit equivalents. open() is replaced by open64(), etc. cmocka wrappers for these functions must have correct name: defining __wrap_open() has no effect for calls to open64(). Unit tests using the wrongly named wrapper functions will fail in non-obvious ways. Use CPP macros to substitute the correct name for affected functions. Also, the Makefile rule that builds the -Wl,wrap= options must parse the C preprocessor output to generate suitable command line parameters for the linker. Signed-off-by: Martin Wilck <mwilck@xxxxxxxx> --- Makefile.inc | 1 + tests/Makefile | 5 ++++- tests/alias.c | 4 ++-- tests/directio.c | 7 +++--- tests/dmevents.c | 8 +++---- tests/sysfs.c | 57 ++++++++++++++++++++++++------------------------ tests/test-lib.c | 9 +++++--- tests/wrap64.h | 48 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 98 insertions(+), 41 deletions(-) create mode 100644 tests/wrap64.h diff --git a/Makefile.inc b/Makefile.inc index a0e36c4..5668e63 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -100,6 +100,7 @@ WARNFLAGS := -Werror -Wall -Wextra -Wformat=2 $(WFORMATOVERFLOW) -Werror=implici -Werror=implicit-function-declaration -Werror=format-security \ $(WNOCLOBBERED) -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS) $(W_URCU_TYPE_LIMITS) CPPFLAGS := $(FORTIFY_OPT) $(CPPFLAGS) $(D_URCU_VERSION) \ + -D_FILE_OFFSET_BITS=64 \ -DBIN_DIR=\"$(bindir)\" -DMULTIPATH_DIR=\"$(plugindir)\" \ -DRUNTIME_DIR=\"$(runtimedir)\" -DCONFIG_DIR=\"$(configdir)\" \ -DDEFAULT_CONFIGFILE=\"$(configfile)\" -DSTATE_DIR=\"$(statedir)\" \ diff --git a/tests/Makefile b/tests/Makefile index 7dac8a8..7dde8bd 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -105,8 +105,11 @@ include $(wildcard $(OBJS:.o=.d)) dep_clean: $(Q)$(RM) $(OBJS:.o=.d) +# Parse the C code for __wrap_xyz() functions and generate linker options from them. +# See comment in wrap64.h %.o.wrap: %.c - @sed -n 's/^.*__wrap_\([a-zA-Z0-9_]*\).*$$/-Wl,--wrap=\1/p' $< | \ + $(Q)$(CC) $(CPPFLAGS) $($*-test_FLAGS) -E $< | \ + sed -n 's/^.*__wrap_\([a-zA-Z0-9_]*\).*$$/-Wl,--wrap=\1/p' | \ sort -u | tr '\n' ' ' >$@ # Pass the original values of CFLAGS etc. to the sub-make, which will include diff --git a/tests/alias.c b/tests/alias.c index c2ae915..95ce994 100644 --- a/tests/alias.c +++ b/tests/alias.c @@ -9,7 +9,7 @@ #include "test-log.h" #include <errno.h> #include <string.h> - +#include "wrap64.h" #include "globals.c" #include "../libmultipath/alias.c" @@ -71,7 +71,7 @@ int __wrap_rename(const char *old, const char *new) return __set_errno(mock_type(int)); } -int __wrap_mkstemp(char *template) +int WRAP_FUNC(mkstemp)(char *template) { return 10; } diff --git a/tests/directio.c b/tests/directio.c index 5201d21..cbedcc9 100644 --- a/tests/directio.c +++ b/tests/directio.c @@ -24,6 +24,7 @@ #include <setjmp.h> #include <stdlib.h> #include <cmocka.h> +#include "wrap64.h" #include "globals.c" #include "../libmultipath/checkers/directio.c" @@ -66,12 +67,12 @@ int __wrap_ioctl(int fd, ioctl_request_t request, void *argp) #endif } -int __real_fcntl(int fd, int cmd, long arg); +int REAL_FCNTL (int fd, int cmd, long arg); -int __wrap_fcntl(int fd, int cmd, long arg) +int WRAP_FCNTL (int fd, int cmd, long arg) { #ifdef DIO_TEST_DEV - return __real_fcntl(fd, cmd, arg); + return REAL_FCNTL(fd, cmd, arg); #else assert_int_equal(fd, test_fd); assert_int_equal(cmd, F_GETFL); diff --git a/tests/dmevents.c b/tests/dmevents.c index f08e649..540bf0d 100644 --- a/tests/dmevents.c +++ b/tests/dmevents.c @@ -29,7 +29,7 @@ #include <fcntl.h> #include "structs.h" #include "structs_vec.h" - +#include "wrap64.h" #include "globals.c" /* I have to do this to get at the static variables */ #include "../multipathd/dmevents.c" @@ -207,7 +207,7 @@ static int teardown(void **state) return 0; } -int __wrap_open(const char *pathname, int flags) +int WRAP_FUNC(open)(const char *pathname, int flags) { assert_ptr_equal(pathname, "/dev/mapper/control"); assert_int_equal(flags, O_RDWR); @@ -389,7 +389,7 @@ static void test_init_waiter_bad1(void **state) struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); - will_return(__wrap_open, -1); + wrap_will_return(WRAP_FUNC(open), -1); assert_int_equal(init_dmevent_waiter(&datap->vecs), -1); assert_ptr_equal(waiter, NULL); } @@ -400,7 +400,7 @@ static void test_init_waiter_good0(void **state) struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); - will_return(__wrap_open, 2); + wrap_will_return(WRAP_FUNC(open), 2); assert_int_equal(init_dmevent_waiter(&datap->vecs), 0); assert_ptr_not_equal(waiter, NULL); } diff --git a/tests/sysfs.c b/tests/sysfs.c index 6e07efd..fc256d8 100644 --- a/tests/sysfs.c +++ b/tests/sysfs.c @@ -18,6 +18,7 @@ #include "test-log.h" #include "sysfs.h" #include "util.h" +#include "wrap64.h" #define TEST_FD 123 @@ -28,7 +29,7 @@ char *__wrap_udev_device_get_syspath(struct udev_device *ud) return val; } -int __wrap_open(const char *pathname, int flags) +int WRAP_FUNC(open)(const char *pathname, int flags) { int ret; @@ -166,10 +167,10 @@ static void test_sagv_open_fail(void **state) will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/bar'"); - expect_string(__wrap_open, pathname, "/foo/bar"); - expect_value(__wrap_open, flags, O_RDONLY); + expect_string(WRAP_FUNC(open), pathname, "/foo/bar"); + expect_value(WRAP_FUNC(open), flags, O_RDONLY); errno = ENOENT; - will_return(__wrap_open, -1); + wrap_will_return(WRAP_FUNC(open), -1); expect_condlog(3, "__sysfs_attr_get_value: attribute '/foo/bar' cannot be opened"); assert_int_equal(sysfs_attr_get_value((void *)state, "bar", buf, sizeof(buf)), -ENOENT); @@ -181,9 +182,9 @@ static void test_sagv_read_fail(void **state) will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/bar'"); - expect_string(__wrap_open, pathname, "/foo/bar"); - expect_value(__wrap_open, flags, O_RDONLY); - will_return(__wrap_open, TEST_FD); + expect_string(WRAP_FUNC(open), pathname, "/foo/bar"); + expect_value(WRAP_FUNC(open), flags, O_RDONLY); + wrap_will_return(WRAP_FUNC(open), TEST_FD); expect_value(__wrap_read, fd, TEST_FD); expect_value(__wrap_read, count, sizeof(buf)); errno = EISDIR; @@ -196,9 +197,9 @@ static void test_sagv_read_fail(void **state) will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/baz'"); - expect_string(__wrap_open, pathname, "/foo/baz"); - expect_value(__wrap_open, flags, O_RDONLY); - will_return(__wrap_open, TEST_FD); + expect_string(WRAP_FUNC(open), pathname, "/foo/baz"); + expect_value(WRAP_FUNC(open), flags, O_RDONLY); + wrap_will_return(WRAP_FUNC(open), TEST_FD); expect_value(__wrap_read, fd, TEST_FD); expect_value(__wrap_read, count, sizeof(buf)); errno = EPERM; @@ -222,9 +223,9 @@ static void _test_sagv_read(void **state, unsigned int bufsz) memset(buf, '.', sizeof(buf)); will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/bar'"); - expect_string(__wrap_open, pathname, "/foo/bar"); - expect_value(__wrap_open, flags, O_RDONLY); - will_return(__wrap_open, TEST_FD); + expect_string(WRAP_FUNC(open), pathname, "/foo/bar"); + expect_value(WRAP_FUNC(open), flags, O_RDONLY); + wrap_will_return(WRAP_FUNC(open), TEST_FD); expect_value(__wrap_read, fd, TEST_FD); expect_value(__wrap_read, count, bufsz); will_return(__wrap_read, sizeof(input) - 1); @@ -249,9 +250,9 @@ static void _test_sagv_read(void **state, unsigned int bufsz) memset(buf, '.', sizeof(buf)); will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/baz'"); - expect_string(__wrap_open, pathname, "/foo/baz"); - expect_value(__wrap_open, flags, O_RDONLY); - will_return(__wrap_open, TEST_FD); + expect_string(WRAP_FUNC(open), pathname, "/foo/baz"); + expect_value(WRAP_FUNC(open), flags, O_RDONLY); + wrap_will_return(WRAP_FUNC(open), TEST_FD); expect_value(__wrap_read, fd, TEST_FD); expect_value(__wrap_read, count, bufsz); will_return(__wrap_read, sizeof(input) - 1); @@ -300,9 +301,9 @@ static void _test_sagv_read_zeroes(void **state, unsigned int bufsz) memset(buf, '.', sizeof(buf)); will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/bar'"); - expect_string(__wrap_open, pathname, "/foo/bar"); - expect_value(__wrap_open, flags, O_RDONLY); - will_return(__wrap_open, TEST_FD); + expect_string(WRAP_FUNC(open), pathname, "/foo/bar"); + expect_value(WRAP_FUNC(open), flags, O_RDONLY); + wrap_will_return(WRAP_FUNC(open), TEST_FD); expect_value(__wrap_read, fd, TEST_FD); expect_value(__wrap_read, count, bufsz); will_return(__wrap_read, sizeof(input) - 1); @@ -385,10 +386,10 @@ static void test_sasv_open_fail(void **state) will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/bar'"); - expect_string(__wrap_open, pathname, "/foo/bar"); - expect_value(__wrap_open, flags, O_WRONLY); + expect_string(WRAP_FUNC(open), pathname, "/foo/bar"); + expect_value(WRAP_FUNC(open), flags, O_WRONLY); errno = EPERM; - will_return(__wrap_open, -1); + wrap_will_return(WRAP_FUNC(open), -1); expect_condlog(3, "sysfs_attr_set_value: attribute '/foo/bar' cannot be opened"); assert_int_equal(sysfs_attr_set_value((void *)state, "bar", buf, sizeof(buf)), -EPERM); @@ -400,9 +401,9 @@ static void test_sasv_write_fail(void **state) will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/bar'"); - expect_string(__wrap_open, pathname, "/foo/bar"); - expect_value(__wrap_open, flags, O_WRONLY); - will_return(__wrap_open, TEST_FD); + expect_string(WRAP_FUNC(open), pathname, "/foo/bar"); + expect_value(WRAP_FUNC(open), flags, O_WRONLY); + wrap_will_return(WRAP_FUNC(open), TEST_FD); expect_value(__wrap_write, fd, TEST_FD); expect_value(__wrap_write, count, sizeof(buf)); errno = EISDIR; @@ -421,9 +422,9 @@ static void _test_sasv_write(void **state, unsigned int n_written) assert_in_range(n_written, 0, sizeof(buf)); will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/bar'"); - expect_string(__wrap_open, pathname, "/foo/bar"); - expect_value(__wrap_open, flags, O_WRONLY); - will_return(__wrap_open, TEST_FD); + expect_string(WRAP_FUNC(open), pathname, "/foo/bar"); + expect_value(WRAP_FUNC(open), flags, O_WRONLY); + wrap_will_return(WRAP_FUNC(open), TEST_FD); expect_value(__wrap_write, fd, TEST_FD); expect_value(__wrap_write, count, sizeof(buf)); will_return(__wrap_write, n_written); diff --git a/tests/test-lib.c b/tests/test-lib.c index f75ea31..665d438 100644 --- a/tests/test-lib.c +++ b/tests/test-lib.c @@ -16,6 +16,7 @@ #include "propsel.h" #include "unaligned.h" #include "test-lib.h" +#include "wrap64.h" const int default_mask = (DI_SYSFS|DI_BLACKLIST|DI_WWID|DI_CHECKER|DI_PRIO|DI_SERIAL); const char default_devnode[] = "sdxTEST"; @@ -38,16 +39,18 @@ const char default_wwid_1[] = "TEST-WWID-1"; * resolved by the assembler before the linking stage. */ -int __real_open(const char *path, int flags, int mode); + +int REAL_FUNC(open)(const char *path, int flags, int mode); static const char _mocked_filename[] = "mocked_path"; -int __wrap_open(const char *path, int flags, int mode) + +int WRAP_FUNC(open)(const char *path, int flags, int mode) { condlog(4, "%s: %s", __func__, path); if (!strcmp(path, _mocked_filename)) return 111; - return __real_open(path, flags, mode); + return REAL_FUNC(open)(path, flags, mode); } int __wrap_libmp_get_version(int which, unsigned int version[3]) diff --git a/tests/wrap64.h b/tests/wrap64.h new file mode 100644 index 0000000..8c91d27 --- /dev/null +++ b/tests/wrap64.h @@ -0,0 +1,48 @@ +#ifndef _WRAP64_H +#define _WRAP64_H 1 +#include "util.h" + +/* + * Make cmocka work with _FILE_OFFSET_BITS == 64 + * + * If -D_FILE_OFFSET_BITS=64 is set, glibc headers replace some functions with + * their 64bit equivalents. "open()" is replaced by "open64()", etc. cmocka + * wrappers for these functions must have correct name __wrap_open() doesn't + * work if the function called is not open() but open64(). Consequently, unit + * tests using such wrappers will fail. + * Use some CPP trickery to insert the correct name. The Makefile rule that + * creates the .wrap file must parse the C preprocessor output to generate the + * correct -Wl,wrap= option. + */ + +/* Without this indirection, WRAP_FUNC(x) would be expanded to __wrap_WRAP_NAME(x) */ +#define CONCAT2_(x, y) x ## y +#define CONCAT2(x, y) CONCAT2_(x, y) + +#if defined(__GLIBC__) && _FILE_OFFSET_BITS == 64 +#define WRAP_NAME(x) x ## 64 +#else +#define WRAP_NAME(x) x +#endif +#define WRAP_FUNC(x) CONCAT2(__wrap_, WRAP_NAME(x)) +#define REAL_FUNC(x) CONCAT2(__real_, WRAP_NAME(x)) + +/* + * fcntl() needs special treatment; fcntl64() has been introduced in 2.28. + * https://savannah.gnu.org/forum/forum.php?forum_id=9205 + */ +#if defined(__GLIBC__) && __GLIBC_PREREQ(2, 28) +#define WRAP_FCNTL_NAME WRAP_NAME(fcntl) +#else +#define WRAP_FCNTL_NAME fcntl +#endif +#define WRAP_FCNTL CONCAT2(__wrap_, WRAP_FCNTL_NAME) +#define REAL_FCNTL CONCAT2(__real_, WRAP_FCNTL_NAME) + +/* + * will_return() is itself a macro that uses CPP "stringizing". We need a + * macro indirection to make sure the *value* of WRAP_FUNC() is stringized + * (see https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html). + */ +#define wrap_will_return(x, y) will_return(x, y) +#endif -- 2.43.0