diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c index 73729382d40f82..eab7b4dba44ce3 100644 --- a/tools/testing/selftests/landlock/fs_test.c +++ b/tools/testing/selftests/landlock/fs_test.c @@ -5,6 +5,7 @@ * Copyright © 2017-2020 Mickaël Salaün * Copyright © 2020 ANSSI * Copyright © 2020-2022 Microsoft Corporation + * Copyright © 2025 Tingmao Wang */ #define _GNU_SOURCE @@ -215,14 +216,74 @@ static void mkdir_parents(struct __test_metadata *const _metadata, free(walker); } +static void +maybe_warn_about_permission_on_cwd(struct __test_metadata *const _metadata, + int err) +{ + char abspath_buf[255]; + + if (err == EACCES) { + const char *realp = realpath(".", abspath_buf); + + if (realp == NULL) + realp = "."; + + TH_LOG("Hint: fs_tests requires permissions for uid %u on test directory %s " + "and files under it (even when running as root).", + getuid(), realp); + TH_LOG(" Try chmod a+rwX -R %s", realp); + } +} + +static int try_teardown_layout(struct __test_metadata *const _metadata) +{ + struct stat stat_buf; + + if (stat(TMP_DIR, &stat_buf) < 0) + return -1; + + TH_LOG("Attempting to cleanup layout and retry..."); + + if (umount(TMP_DIR)) { + if (errno != EINVAL && errno != ENOENT) { + TH_LOG("Failed to unmount directory \"%s\": %s", + TMP_DIR, strerror(errno)); + return -1; + } + } + if (rmdir(TMP_DIR)) { + if (errno != ENOENT) { + TH_LOG("Failed to remove directory \"%s\": %s", TMP_DIR, + strerror(errno)); + return -1; + } + } + return 0; +} + static void create_directory(struct __test_metadata *const _metadata, const char *const path) { + bool retried = false; + +retry: mkdir_parents(_metadata, path); - ASSERT_EQ(0, mkdir(path, 0700)) - { + if (mkdir(path, 0700)) { + int err = errno; + TH_LOG("Failed to create directory \"%s\": %s", path, - strerror(errno)); + strerror(err)); + + if (strcmp(path, TMP_DIR) == 0) { + maybe_warn_about_permission_on_cwd(_metadata, err); + if (!retried && errno == EEXIST && + !try_teardown_layout(_metadata)) { + retried = true; + goto retry; + } + } + + ASSERT_TRUE(false); } } @@ -296,17 +357,18 @@ static void prepare_layout_opt(struct __test_metadata *const _metadata, { disable_caps(_metadata); umask(0077); + + /* create_directory may try umounting then rmdir if tmp already mounted */ + set_cap(_metadata, CAP_SYS_ADMIN); create_directory(_metadata, TMP_DIR); /* * Do not pollute the rest of the system: creates a private mount point * for tests relying on pivot_root(2) and move_mount(2). */ - set_cap(_metadata, CAP_SYS_ADMIN); - ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP)); - ASSERT_EQ(0, mount_opt(mnt, TMP_DIR)) + ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP)) { - TH_LOG("Failed to mount the %s filesystem: %s", mnt->type, + TH_LOG("Failed to create new mount namespace: %s", strerror(errno)); /* * FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP() @@ -316,6 +378,12 @@ static void prepare_layout_opt(struct __test_metadata *const _metadata, */ remove_path(TMP_DIR); } + ASSERT_EQ(0, mount_opt(mnt, TMP_DIR)) + { + TH_LOG("Failed to mount the %s filesystem: %s", mnt->type, + strerror(errno)); + remove_path(TMP_DIR); + } ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL)); clear_cap(_metadata, CAP_SYS_ADMIN); } @@ -1979,18 +2047,22 @@ TEST_F_FORK(layout1, relative_chroot_chdir) static void copy_file(struct __test_metadata *const _metadata, const char *const src_path, const char *const dst_path) { - int dst_fd, src_fd; + int dst_fd, src_fd, err; struct stat statbuf; dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC); ASSERT_LE(0, dst_fd) { - TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno)); + err = errno; + TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(err)); + maybe_warn_about_permission_on_cwd(_metadata, err); } src_fd = open(src_path, O_RDONLY | O_CLOEXEC); ASSERT_LE(0, src_fd) { - TH_LOG("Failed to open \"%s\": %s", src_path, strerror(errno)); + err = errno; + TH_LOG("Failed to open \"%s\": %s", src_path, strerror(err)); + maybe_warn_about_permission_on_cwd(_metadata, err); } ASSERT_EQ(0, fstat(src_fd, &statbuf)); ASSERT_EQ(statbuf.st_size,