Add regression test for nsfs
This commit is contained in:
parent
9c99e782ed
commit
78f098fe88
|
|
@ -0,0 +1,230 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <linux/nsfs.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../test.h"
|
||||||
|
|
||||||
|
#define NS_DIR "/proc/self/ns"
|
||||||
|
|
||||||
|
// ns_names contains the namespace names as they appear in readlink output.
|
||||||
|
// For pid_for_children and time_for_children, the readlink output shows "pid" and "time" respectively.
|
||||||
|
// For other namespaces, ns_files and ns_names have identical corresponding strings.
|
||||||
|
char *ns_files[] = { "uts", "mnt", "user" };
|
||||||
|
char *ns_names[] = { "uts", "mnt", "user" };
|
||||||
|
int clone_flags[] = { CLONE_NEWUTS, CLONE_NEWNS, CLONE_NEWUSER };
|
||||||
|
|
||||||
|
FN_TEST(common_fs_operations)
|
||||||
|
{
|
||||||
|
char path[PATH_MAX];
|
||||||
|
char buf[1];
|
||||||
|
size_t ns_count = sizeof(ns_files) / sizeof(ns_files[0]);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ns_count; i++) {
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", NS_DIR, ns_files[i]);
|
||||||
|
|
||||||
|
TEST_ERRNO(open(path, O_RDWR), EPERM);
|
||||||
|
TEST_ERRNO(open(path, O_WRONLY), EPERM);
|
||||||
|
int nsfd = TEST_SUCC(open(path, O_RDONLY));
|
||||||
|
|
||||||
|
TEST_ERRNO(read(nsfd, buf, 1), EINVAL);
|
||||||
|
TEST_ERRNO(write(nsfd, buf, 1), EBADF);
|
||||||
|
|
||||||
|
struct pollfd ns_pfd = { .fd = nsfd,
|
||||||
|
.events = POLLIN | POLLOUT |
|
||||||
|
POLLRDHUP | POLLPRI |
|
||||||
|
POLLRDNORM };
|
||||||
|
TEST_RES(poll(&ns_pfd, 1, -1),
|
||||||
|
ns_pfd.revents == (POLLIN | POLLOUT | POLLRDNORM));
|
||||||
|
|
||||||
|
struct stat64 stat;
|
||||||
|
TEST_RES(fstat64(nsfd, &stat),
|
||||||
|
stat.st_mode == (0444 | S_IFREG));
|
||||||
|
|
||||||
|
TEST_ERRNO(lseek64(nsfd, 0, SEEK_SET), ESPIPE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_TEST(readlink)
|
||||||
|
{
|
||||||
|
char path[PATH_MAX];
|
||||||
|
char buf[256];
|
||||||
|
size_t ns_count = sizeof(ns_files) / sizeof(ns_files[0]);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ns_count; i++) {
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", NS_DIR, ns_files[i]);
|
||||||
|
|
||||||
|
char expected_format[256] = { 0 };
|
||||||
|
int nsfd = TEST_SUCC(open(path, O_RDONLY));
|
||||||
|
struct stat stat;
|
||||||
|
TEST_SUCC(fstat(nsfd, &stat));
|
||||||
|
char *ns_name = ns_names[i];
|
||||||
|
snprintf(expected_format, sizeof(expected_format), "%s:[%u]",
|
||||||
|
ns_name, (unsigned int)stat.st_ino);
|
||||||
|
|
||||||
|
TEST_RES(readlink(path, buf, sizeof(buf) - 1),
|
||||||
|
strcmp(expected_format, buf) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_TEST(zombie_process)
|
||||||
|
{
|
||||||
|
char path[PATH_MAX];
|
||||||
|
size_t ns_count = sizeof(ns_files) / sizeof(ns_files[0]);
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
TEST_RES(pid >= 0, 1);
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
// Child process exits immediately to become zombie
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCC(waitid(P_PID, pid, NULL, WNOWAIT | WEXITED));
|
||||||
|
|
||||||
|
// Try to access zombie process's namespace files
|
||||||
|
for (size_t i = 0; i < ns_count; i++) {
|
||||||
|
char *ns_file = ns_files[i];
|
||||||
|
snprintf(path, sizeof(path), "/proc/%d/ns/%s", pid,
|
||||||
|
ns_files[i]);
|
||||||
|
|
||||||
|
// Should still be able to open zombie's PID/user namespace files
|
||||||
|
if (strcmp(ns_file, "pid") == 0 ||
|
||||||
|
strcmp(ns_file, "user") == 0) {
|
||||||
|
char buf[256] = { 0 };
|
||||||
|
TEST_SUCC(readlink(path, buf, sizeof(buf) - 1));
|
||||||
|
|
||||||
|
int nsfd = TEST_SUCC(open(path, O_RDONLY));
|
||||||
|
TEST_SUCC(close(nsfd));
|
||||||
|
} else {
|
||||||
|
TEST_ERRNO(open(path, O_RDONLY), ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCC(waitpid(pid, NULL, 0));
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_TEST(ioctl)
|
||||||
|
{
|
||||||
|
char path[PATH_MAX];
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(ns_files) / sizeof(ns_files[0]); i++) {
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", NS_DIR, ns_files[i]);
|
||||||
|
|
||||||
|
int nsfd = TEST_SUCC(open(path, O_RDONLY));
|
||||||
|
// NS_GET_USERNS
|
||||||
|
if (strcmp(ns_files[i], "user") != 0) {
|
||||||
|
int userns_fd = TEST_SUCC(ioctl(nsfd, NS_GET_USERNS));
|
||||||
|
TEST_SUCC(close(userns_fd));
|
||||||
|
} else {
|
||||||
|
TEST_ERRNO(ioctl(nsfd, NS_GET_USERNS), EPERM);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NS_GET_PARENT - get parent namespace (should fail for non-hierarchical ns)
|
||||||
|
if (strcmp(ns_files[i], "user") != 0) {
|
||||||
|
TEST_ERRNO(ioctl(nsfd, NS_GET_PARENT), EINVAL);
|
||||||
|
} else {
|
||||||
|
TEST_ERRNO(ioctl(nsfd, NS_GET_PARENT), EPERM);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NS_GET_NSTYPE
|
||||||
|
TEST_RES(ioctl(nsfd, NS_GET_NSTYPE), _ret == clone_flags[i]);
|
||||||
|
|
||||||
|
// NS_GET_OWNER_UID: User namespace only
|
||||||
|
if (strcmp(ns_files[i], "user") != 0) {
|
||||||
|
TEST_ERRNO(ioctl(nsfd, NS_GET_OWNER_UID), EINVAL);
|
||||||
|
} else {
|
||||||
|
TEST_ERRNO(ioctl(nsfd, NS_GET_OWNER_UID, 0), EFAULT);
|
||||||
|
uid_t uid;
|
||||||
|
TEST_SUCC(ioctl(nsfd, NS_GET_OWNER_UID, &uid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_TEST(setns)
|
||||||
|
{
|
||||||
|
char path[PATH_MAX];
|
||||||
|
|
||||||
|
// Test setns with current process's namespace (should succeed)
|
||||||
|
for (size_t i = 0; i < sizeof(ns_files) / sizeof(ns_files[0]); i++) {
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", NS_DIR, ns_files[i]);
|
||||||
|
int nsfd = TEST_SUCC(open(path, O_RDONLY));
|
||||||
|
|
||||||
|
// setns to own namespace should succeed
|
||||||
|
if (strcmp(ns_files[i], "user") != 0) {
|
||||||
|
TEST_SUCC(setns(nsfd, 0));
|
||||||
|
} else {
|
||||||
|
TEST_ERRNO(setns(nsfd, 0), EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCC(close(nsfd));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test setns with invalid fd
|
||||||
|
TEST_ERRNO(setns(-1, 0), EBADF);
|
||||||
|
|
||||||
|
// Test setns across fork
|
||||||
|
pid_t pid = fork();
|
||||||
|
TEST_RES(pid >= 0, 1);
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
// Child: try to join parent's UTS namespace
|
||||||
|
snprintf(path, sizeof(path), "/proc/%d/ns/uts", getppid());
|
||||||
|
int parent_ns = CHECK(open(path, O_RDONLY));
|
||||||
|
|
||||||
|
CHECK(setns(parent_ns, 0));
|
||||||
|
close(parent_ns);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int status;
|
||||||
|
TEST_RES(waitpid(pid, &status, 0), pid);
|
||||||
|
TEST_RES(WIFEXITED(status), 1);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_TEST(proc_fd_name)
|
||||||
|
{
|
||||||
|
char path[PATH_MAX];
|
||||||
|
char buf1[256];
|
||||||
|
char buf2[256];
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(ns_files) / sizeof(ns_files[0]); i++) {
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", NS_DIR, ns_files[i]);
|
||||||
|
|
||||||
|
memset(buf1, 0, sizeof(buf1));
|
||||||
|
TEST_SUCC(readlink(path, buf1, sizeof(buf1)));
|
||||||
|
|
||||||
|
int nsfd = TEST_SUCC(open(path, O_RDONLY));
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
sprintf(path, "/proc/self/fd/%d", nsfd);
|
||||||
|
|
||||||
|
memset(buf2, 0, sizeof(buf2));
|
||||||
|
TEST_RES(readlink(path, buf2, sizeof(buf2)),
|
||||||
|
strcmp(buf1, buf2) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
@ -45,6 +45,7 @@ mmap/mmap_shared_filebacked
|
||||||
mmap/mmap_readahead
|
mmap/mmap_readahead
|
||||||
mmap/mmap_vmrss
|
mmap/mmap_vmrss
|
||||||
namespace/mnt_ns
|
namespace/mnt_ns
|
||||||
|
namespace/proc_nsfs
|
||||||
namespace/setns
|
namespace/setns
|
||||||
namespace/unshare
|
namespace/unshare
|
||||||
process/group_session
|
process/group_session
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue