From: Baolin Liu <[email protected]> Add a selftest for the legacy fileattr ioctls (FS_IOC_FSGETXATTR/FS_IOC_FSSETXATTR).
Cover FS_IOC_FSGETXATTR on regular files, directories, and FIFOs, and verify FS_XFLAG_NODUMP and FS_XFLAG_NOATIME round-trips on regular files. Allow the test to run in a caller-specified writable base directory via FILEATTR_TEST_DIR. If unset, fall back to P_tmpdir. Signed-off-by: Baolin Liu <[email protected]> --- tools/testing/selftests/Makefile | 1 + .../selftests/filesystems/fileattr/.gitignore | 2 + .../selftests/filesystems/fileattr/Makefile | 6 + .../filesystems/fileattr/fileattr_test.c | 256 ++++++++++++++++++ 4 files changed, 265 insertions(+) create mode 100644 tools/testing/selftests/filesystems/fileattr/.gitignore create mode 100644 tools/testing/selftests/filesystems/fileattr/Makefile create mode 100644 tools/testing/selftests/filesystems/fileattr/fileattr_test.c diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 450f13ba4cca..459251b08648 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -34,6 +34,7 @@ TARGETS += filesystems TARGETS += filesystems/binderfs TARGETS += filesystems/epoll TARGETS += filesystems/fat +TARGETS += filesystems/fileattr TARGETS += filesystems/overlayfs TARGETS += filesystems/statmount TARGETS += filesystems/mount-notify diff --git a/tools/testing/selftests/filesystems/fileattr/.gitignore b/tools/testing/selftests/filesystems/fileattr/.gitignore new file mode 100644 index 000000000000..2eaf3e266eb6 --- /dev/null +++ b/tools/testing/selftests/filesystems/fileattr/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +fileattr_test diff --git a/tools/testing/selftests/filesystems/fileattr/Makefile b/tools/testing/selftests/filesystems/fileattr/Makefile new file mode 100644 index 000000000000..2295e7c9bdb5 --- /dev/null +++ b/tools/testing/selftests/filesystems/fileattr/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +CFLAGS += -O2 -g -Wall $(KHDR_INCLUDES) +TEST_GEN_PROGS := fileattr_test + +include ../../lib.mk diff --git a/tools/testing/selftests/filesystems/fileattr/fileattr_test.c b/tools/testing/selftests/filesystems/fileattr/fileattr_test.c new file mode 100644 index 000000000000..cd6a6681d2f7 --- /dev/null +++ b/tools/testing/selftests/filesystems/fileattr/fileattr_test.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <linux/fs.h> +#include <linux/limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "kselftest_harness.h" + +FIXTURE(fileattr) { + char workdir[PATH_MAX]; +}; + +static int open_nofollow(const char *path) +{ + return open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK); +} + +static int get_xattr(int fd, struct fsxattr *fa) +{ + memset(fa, 0, sizeof(*fa)); + return ioctl(fd, FS_IOC_FSGETXATTR, fa); +} + +static int set_xattr(int fd, struct fsxattr *fa) +{ + return ioctl(fd, FS_IOC_FSSETXATTR, fa); +} + +static int build_path(char *buf, size_t size, const char *dir, const char *name) +{ + int ret; + + ret = snprintf(buf, size, "%s/%s", dir, name); + if (ret < 0 || ret >= size) { + errno = ENAMETOOLONG; + return -1; + } + + return 0; +} + +static int make_workdir(char *workdir, size_t size) +{ + const char *base = getenv("FILEATTR_TEST_DIR"); + int ret; + + if (!base || !*base) + base = P_tmpdir; + + if (base[0] != '/') { + errno = EINVAL; + return -1; + } + + ret = snprintf(workdir, size, "%s/fileattr.XXXXXX", base); + if (ret < 0 || ret >= (int)size) { + errno = ENAMETOOLONG; + return -1; + } + + return mkdtemp(workdir) ? 0 : -1; +} + +static int cleanup_workdir(const char *workdir) +{ + struct dirent *de; + DIR *dir; + int ret; + + dir = opendir(workdir); + if (!dir) + return -1; + + while ((de = readdir(dir))) { + struct stat st; + char path[PATH_MAX]; + + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + ret = build_path(path, sizeof(path), workdir, de->d_name); + if (ret) { + closedir(dir); + return -1; + } + + ret = lstat(path, &st); + if (ret) { + closedir(dir); + return -1; + } + + if (S_ISDIR(st.st_mode)) + ret = rmdir(path); + else + ret = unlink(path); + if (ret) { + closedir(dir); + return -1; + } + } + + closedir(dir); + return rmdir(workdir); +} + +FIXTURE_SETUP(fileattr) +{ + if (make_workdir(self->workdir, sizeof(self->workdir))) + SKIP(return, "failed to create workdir: %s", strerror(errno)); +} + +FIXTURE_TEARDOWN(fileattr) +{ + EXPECT_EQ(cleanup_workdir(self->workdir), 0); +} + +TEST_F(fileattr, get_regular_file) +{ + char path[PATH_MAX]; + struct fsxattr fa; + int fd; + + ASSERT_EQ(build_path(path, sizeof(path), self->workdir, "regular.XXXXXX"), 0); + + fd = mkstemp(path); + ASSERT_GE(fd, 0); + ASSERT_EQ(close(fd), 0); + + fd = open_nofollow(path); + ASSERT_GE(fd, 0); + + ASSERT_EQ(get_xattr(fd, &fa), 0); + + EXPECT_EQ(close(fd), 0); + EXPECT_EQ(unlink(path), 0); +} + +TEST_F(fileattr, get_directory) +{ + char template[PATH_MAX]; + struct fsxattr fa; + char *dir; + int fd; + + ASSERT_EQ(build_path(template, sizeof(template), self->workdir, "dir.XXXXXX"), 0); + + dir = mkdtemp(template); + ASSERT_NE(dir, NULL); + + fd = open_nofollow(dir); + ASSERT_GE(fd, 0); + + ASSERT_EQ(get_xattr(fd, &fa), 0); + + EXPECT_EQ(close(fd), 0); + EXPECT_EQ(rmdir(dir), 0); +} + +TEST_F(fileattr, get_fifo) +{ + char path[PATH_MAX]; + struct fsxattr fa; + int fd; + + ASSERT_EQ(build_path(path, sizeof(path), self->workdir, "fifo.XXXXXX"), 0); + + fd = mkstemp(path); + ASSERT_GE(fd, 0); + ASSERT_EQ(close(fd), 0); + ASSERT_EQ(unlink(path), 0); + ASSERT_EQ(mkfifo(path, 0600), 0); + + fd = open_nofollow(path); + ASSERT_GE(fd, 0); + + memset(&fa, 0, sizeof(fa)); + ASSERT_LT(ioctl(fd, FS_IOC_FSGETXATTR, &fa), 0); + EXPECT_EQ(errno, ENOTTY); + + EXPECT_EQ(close(fd), 0); + EXPECT_EQ(unlink(path), 0); +} + +TEST_F(fileattr, set_nodump_roundtrip) +{ + char path[PATH_MAX]; + struct fsxattr fa, orig; + int fd; + + ASSERT_EQ(build_path(path, sizeof(path), self->workdir, "nodump.XXXXXX"), 0); + + fd = mkstemp(path); + ASSERT_GE(fd, 0); + ASSERT_EQ(close(fd), 0); + + fd = open_nofollow(path); + ASSERT_GE(fd, 0); + + ASSERT_EQ(get_xattr(fd, &orig), 0); + fa = orig; + fa.fsx_xflags |= FS_XFLAG_NODUMP; + ASSERT_EQ(set_xattr(fd, &fa), 0); + ASSERT_EQ(get_xattr(fd, &fa), 0); + EXPECT_TRUE(fa.fsx_xflags & FS_XFLAG_NODUMP); + + fa = orig; + ASSERT_EQ(set_xattr(fd, &fa), 0); + ASSERT_EQ(get_xattr(fd, &fa), 0); + EXPECT_EQ(fa.fsx_xflags, orig.fsx_xflags); + + EXPECT_EQ(close(fd), 0); + EXPECT_EQ(unlink(path), 0); +} + +TEST_F(fileattr, set_noatime_roundtrip) +{ + char path[PATH_MAX]; + struct fsxattr fa, orig; + int fd; + + ASSERT_EQ(build_path(path, sizeof(path), self->workdir, "noatime.XXXXXX"), 0); + + fd = mkstemp(path); + ASSERT_GE(fd, 0); + ASSERT_EQ(close(fd), 0); + + fd = open_nofollow(path); + ASSERT_GE(fd, 0); + + ASSERT_EQ(get_xattr(fd, &orig), 0); + fa = orig; + fa.fsx_xflags |= FS_XFLAG_NOATIME; + ASSERT_EQ(set_xattr(fd, &fa), 0); + ASSERT_EQ(get_xattr(fd, &fa), 0); + EXPECT_TRUE(fa.fsx_xflags & FS_XFLAG_NOATIME); + + fa = orig; + ASSERT_EQ(set_xattr(fd, &fa), 0); + ASSERT_EQ(get_xattr(fd, &fa), 0); + EXPECT_EQ(fa.fsx_xflags, orig.fsx_xflags); + + EXPECT_EQ(close(fd), 0); + EXPECT_EQ(unlink(path), 0); +} + +TEST_HARNESS_MAIN -- 2.51.0

