[PATCH v2 042/113] test: self: add dirfd tests

Ahmad Fatoum a.fatoum at pengutronix.de
Mon Mar 4 10:59:27 PST 2024


Adding directory fds involved a bit of rework to the VFS layer.
We have ramfs tests that check the previous behavior, so add now tests
to check that path resolution in openat and statat work as one would expect.

As we need a separate file system to test O_CHROOT, the test has a
dependency on the devfs.

Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
 test/self/Kconfig  |   5 ++
 test/self/Makefile |   1 +
 test/self/dirfd.c  | 129 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 135 insertions(+)
 create mode 100644 test/self/dirfd.c

diff --git a/test/self/Kconfig b/test/self/Kconfig
index 6b9a4bf4a6e4..33e478aee882 100644
--- a/test/self/Kconfig
+++ b/test/self/Kconfig
@@ -34,6 +34,7 @@ config SELFTEST_ENABLE_ALL
 	select SELFTEST_OF_MANIPULATION
 	select SELFTEST_ENVIRONMENT_VARIABLES if ENVIRONMENT_VARIABLES
 	select SELFTEST_FS_RAMFS if FS_RAMFS
+	select SELFTEST_DIRFD if FS_RAMFS && FS_DEVFS
 	select SELFTEST_TFTP if FS_TFTP
 	select SELFTEST_JSON if JSMN
 	select SELFTEST_JWT if JWT
@@ -73,6 +74,10 @@ config SELFTEST_FS_RAMFS
 	bool "ramfs selftest"
 	depends on FS_RAMFS
 
+config SELFTEST_DIRFD
+	bool "dirfd selftest"
+	depends on FS_RAMFS && FS_DEVFS
+
 config SELFTEST_JSON
 	bool "JSON selftest"
 	depends on JSMN
diff --git a/test/self/Makefile b/test/self/Makefile
index 51131474f333..fbc186725487 100644
--- a/test/self/Makefile
+++ b/test/self/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_SELFTEST_PROGRESS_NOTIFIER) += progress-notifier.o
 obj-$(CONFIG_SELFTEST_OF_MANIPULATION) += of_manipulation.o of_manipulation.dtb.o
 obj-$(CONFIG_SELFTEST_ENVIRONMENT_VARIABLES) += envvar.o
 obj-$(CONFIG_SELFTEST_FS_RAMFS) += ramfs.o
+obj-$(CONFIG_SELFTEST_DIRFD) += dirfd.o
 obj-$(CONFIG_SELFTEST_JSON) += json.o
 obj-$(CONFIG_SELFTEST_JWT) += jwt.o jwt_test.pem.o
 obj-$(CONFIG_SELFTEST_DIGEST) += digest.o
diff --git a/test/self/dirfd.c b/test/self/dirfd.c
new file mode 100644
index 000000000000..20b54258715a
--- /dev/null
+++ b/test/self/dirfd.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <fcntl.h>
+#include <fs.h>
+#include <string.h>
+#include <linux/bitfield.h>
+#include <unistd.h>
+#include <bselftest.h>
+
+BSELFTEST_GLOBALS();
+
+#define expect(cond, res, fmt, ...) ({ \
+	int __cond = (cond); \
+	int __res = (res); \
+	total_tests++; \
+	if (__cond != __res) { \
+		failed_tests++; \
+		printf("%s:%d failed: %s == %d: " fmt "\n", \
+		       __func__, __LINE__, #cond, __cond, ##__VA_ARGS__); \
+	} \
+	__cond == __res; \
+})
+
+static void check_statat(const char *at, int dirfd, const char *prefix, unsigned expected)
+{
+	static const char *paths[] = { ".", "..", "zero", "dev" };
+	struct stat s;
+
+	for (int i = 0; i < ARRAY_SIZE(paths); i++) {
+		const char *path = paths[i];
+		char *fullpath = NULL, *testpath = basprintf("%s%s", prefix, path);
+		struct fs_device *fsdev1, *fsdev2;
+		int ret;
+
+		ret = statat(dirfd, testpath, &s);
+		if (!expect(ret == 0, FIELD_GET(BIT(2), expected),
+			    "statat(%s, %s): %m", at, testpath))
+			goto next;
+
+		fullpath = canonicalize_path(dirfd, testpath);
+		if (!expect(fullpath != NULL, FIELD_GET(BIT(1), expected),
+			    "canonicalize_path(%s, %s): %m", at, testpath))
+			goto next;
+
+		if (!fullpath)
+			goto next;
+
+		fsdev1 = get_fsdevice_by_path(AT_FDCWD, fullpath);
+		if (!expect(IS_ERR_OR_NULL(fsdev1), false, "get_fsdevice_by_path(AT_FDCWD, %s)",
+			    fullpath))
+			goto next;
+
+		fsdev2 = get_fsdevice_by_path(dirfd, testpath);
+		if (!expect(IS_ERR_OR_NULL(fsdev1), false, "get_fsdevice_by_path(%s, %s)",
+			    at, testpath))
+			goto next;
+
+		if (!expect(fsdev1 == fsdev2, true,
+			    "get_fsdevice_by_path(%s, %s) != get_fsdevice_by_path(AT_FDCWD, %s)",
+			    fullpath, at, testpath))
+			goto next;
+
+		ret = strcmp_ptr(fsdev1->path, "/dev");
+		if (!expect(ret == 0, FIELD_GET(BIT(0), expected),
+			    "fsdev_of(%s)->path = %s != /dev", fullpath, fsdev1->path))
+			goto next;
+
+next:
+		expected >>= 3;
+		free(testpath);
+		free(fullpath);
+	}
+}
+
+static void do_test_dirfd(const char *at, int dirfd,
+			  unsigned expected1, unsigned expected2,
+			  unsigned expected3, unsigned expected4)
+{
+	if (dirfd < 0 && dirfd != AT_FDCWD)
+		return;
+
+	check_statat(at, dirfd, "",             expected1);
+	check_statat(at, dirfd, "./",           expected1);
+	check_statat(at, dirfd, "/dev/",        expected2);
+	check_statat(at, dirfd, "/dev/./",      expected2);
+	check_statat(at, dirfd, "/dev/../dev/", expected2);
+	check_statat(at, dirfd, "/",            expected3);
+	check_statat(at, dirfd, "../",          expected4);
+
+	if (dirfd >= 0)
+		close(dirfd);
+}
+
+
+static void test_dirfd(void)
+{
+	int fd;
+
+	fd = open("/", O_PATH);
+	if (expect(fd < 0, false, "open(/, O_PATH) = %d", fd))
+		close(fd);
+
+#define B(dot, dotdot, zero, dev) 0b##dev##zero##dotdot##dot
+	/* We do fiften tests for every configuration
+	 * for dir in ./ /dev / ../ ; do
+	 *	for file in . .. zero dev ; do
+	 *		test if file exists
+	 *		test if file can be canonicalized
+	 *		test if parent FS is mounted at /dev
+	 *	done
+	 * done
+	 *
+	 * The bits belows correspond to whether a test fails in the above loop
+	 */
+
+	do_test_dirfd("AT_FDCWD", AT_FDCWD,
+		      B(110,110,000,111), B(111,110,111,000),
+		      B(110,110,000,111), B(110,110,000,111));
+	do_test_dirfd("/dev", open("/dev", O_PATH),
+		      B(111,110,111,000), B(111,110,111,000),
+		      B(110,110,000,111), B(110,110,000,111));
+	do_test_dirfd("/dev O_CHROOT", open("/dev", O_PATH | O_CHROOT),
+		      B(111,111,111,000), B(000,000,000,000),
+		      B(111,111,111,000), B(111,111,111,000));
+}
+bselftest(core, test_dirfd);
-- 
2.39.2




More information about the barebox mailing list