Path: blob/master/tools/testing/selftests/filesystems/statmount/statmount_test_ns.c
26302 views
// SPDX-License-Identifier: GPL-2.0-or-later12#define _GNU_SOURCE34#include <assert.h>5#include <fcntl.h>6#include <limits.h>7#include <sched.h>8#include <stdlib.h>9#include <sys/mount.h>10#include <sys/stat.h>11#include <sys/wait.h>12#include <linux/nsfs.h>13#include <linux/stat.h>1415#include "statmount.h"16#include "../utils.h"17#include "../../kselftest.h"1819#define NSID_PASS 020#define NSID_FAIL 121#define NSID_SKIP 222#define NSID_ERROR 32324static void handle_result(int ret, const char *testname)25{26if (ret == NSID_PASS)27ksft_test_result_pass("%s\n", testname);28else if (ret == NSID_FAIL)29ksft_test_result_fail("%s\n", testname);30else if (ret == NSID_ERROR)31ksft_exit_fail_msg("%s\n", testname);32else33ksft_test_result_skip("%s\n", testname);34}3536static inline int wait_for_pid(pid_t pid)37{38int status, ret;3940again:41ret = waitpid(pid, &status, 0);42if (ret == -1) {43if (errno == EINTR)44goto again;4546ksft_print_msg("waitpid returned -1, errno=%d\n", errno);47return -1;48}4950if (!WIFEXITED(status)) {51ksft_print_msg(52"waitpid !WIFEXITED, WIFSIGNALED=%d, WTERMSIG=%d\n",53WIFSIGNALED(status), WTERMSIG(status));54return -1;55}5657ret = WEXITSTATUS(status);58return ret;59}6061static int get_mnt_ns_id(const char *mnt_ns, uint64_t *mnt_ns_id)62{63int fd = open(mnt_ns, O_RDONLY);6465if (fd < 0) {66ksft_print_msg("failed to open for ns %s: %s\n",67mnt_ns, strerror(errno));68sleep(60);69return NSID_ERROR;70}7172if (ioctl(fd, NS_GET_MNTNS_ID, mnt_ns_id) < 0) {73ksft_print_msg("failed to get the nsid for ns %s: %s\n",74mnt_ns, strerror(errno));75return NSID_ERROR;76}77close(fd);78return NSID_PASS;79}8081static int setup_namespace(void)82{83if (setup_userns() != 0)84return NSID_ERROR;8586return NSID_PASS;87}8889static int _test_statmount_mnt_ns_id(void)90{91struct statmount sm;92uint64_t mnt_ns_id;93uint64_t root_id;94int ret;9596ret = get_mnt_ns_id("/proc/self/ns/mnt", &mnt_ns_id);97if (ret != NSID_PASS)98return ret;99100root_id = get_unique_mnt_id("/");101if (!root_id)102return NSID_ERROR;103104ret = statmount(root_id, 0, STATMOUNT_MNT_NS_ID, &sm, sizeof(sm), 0);105if (ret == -1) {106ksft_print_msg("statmount mnt ns id: %s\n", strerror(errno));107return NSID_ERROR;108}109110if (sm.size != sizeof(sm)) {111ksft_print_msg("unexpected size: %u != %u\n", sm.size,112(uint32_t)sizeof(sm));113return NSID_FAIL;114}115if (sm.mask != STATMOUNT_MNT_NS_ID) {116ksft_print_msg("statmount mnt ns id unavailable\n");117return NSID_SKIP;118}119120if (sm.mnt_ns_id != mnt_ns_id) {121ksft_print_msg("unexpected mnt ns ID: 0x%llx != 0x%llx\n",122(unsigned long long)sm.mnt_ns_id,123(unsigned long long)mnt_ns_id);124return NSID_FAIL;125}126127return NSID_PASS;128}129130static void test_statmount_mnt_ns_id(void)131{132pid_t pid;133int ret;134135pid = fork();136if (pid < 0)137ksft_exit_fail_msg("failed to fork: %s\n", strerror(errno));138139/* We're the original pid, wait for the result. */140if (pid != 0) {141ret = wait_for_pid(pid);142handle_result(ret, "test statmount ns id");143return;144}145146ret = setup_namespace();147if (ret != NSID_PASS)148exit(ret);149ret = _test_statmount_mnt_ns_id();150exit(ret);151}152153static int validate_external_listmount(pid_t pid, uint64_t child_nr_mounts)154{155uint64_t list[256];156uint64_t mnt_ns_id;157uint64_t nr_mounts;158char buf[256];159int ret;160161/* Get the mount ns id for our child. */162snprintf(buf, sizeof(buf), "/proc/%lu/ns/mnt", (unsigned long)pid);163ret = get_mnt_ns_id(buf, &mnt_ns_id);164165nr_mounts = listmount(LSMT_ROOT, mnt_ns_id, 0, list, 256, 0);166if (nr_mounts == (uint64_t)-1) {167ksft_print_msg("listmount: %s\n", strerror(errno));168return NSID_ERROR;169}170171if (nr_mounts != child_nr_mounts) {172ksft_print_msg("listmount results is %zi != %zi\n", nr_mounts,173child_nr_mounts);174return NSID_FAIL;175}176177/* Validate that all of our entries match our mnt_ns_id. */178for (int i = 0; i < nr_mounts; i++) {179struct statmount sm;180181ret = statmount(list[i], mnt_ns_id, STATMOUNT_MNT_NS_ID, &sm,182sizeof(sm), 0);183if (ret < 0) {184ksft_print_msg("statmount mnt ns id: %s\n", strerror(errno));185return NSID_ERROR;186}187188if (sm.mask != STATMOUNT_MNT_NS_ID) {189ksft_print_msg("statmount mnt ns id unavailable\n");190return NSID_SKIP;191}192193if (sm.mnt_ns_id != mnt_ns_id) {194ksft_print_msg("listmount gave us the wrong ns id: 0x%llx != 0x%llx\n",195(unsigned long long)sm.mnt_ns_id,196(unsigned long long)mnt_ns_id);197return NSID_FAIL;198}199}200201return NSID_PASS;202}203204static void test_listmount_ns(void)205{206uint64_t nr_mounts;207char pval;208int child_ready_pipe[2];209int parent_ready_pipe[2];210pid_t pid;211int ret, child_ret;212213if (pipe(child_ready_pipe) < 0)214ksft_exit_fail_msg("failed to create the child pipe: %s\n",215strerror(errno));216if (pipe(parent_ready_pipe) < 0)217ksft_exit_fail_msg("failed to create the parent pipe: %s\n",218strerror(errno));219220pid = fork();221if (pid < 0)222ksft_exit_fail_msg("failed to fork: %s\n", strerror(errno));223224if (pid == 0) {225char cval;226uint64_t list[256];227228close(child_ready_pipe[0]);229close(parent_ready_pipe[1]);230231ret = setup_namespace();232if (ret != NSID_PASS)233exit(ret);234235nr_mounts = listmount(LSMT_ROOT, 0, 0, list, 256, 0);236if (nr_mounts == (uint64_t)-1) {237ksft_print_msg("listmount: %s\n", strerror(errno));238exit(NSID_FAIL);239}240241/*242* Tell our parent how many mounts we have, and then wait for it243* to tell us we're done.244*/245if (write(child_ready_pipe[1], &nr_mounts, sizeof(nr_mounts)) !=246sizeof(nr_mounts))247ret = NSID_ERROR;248if (read(parent_ready_pipe[0], &cval, sizeof(cval)) != sizeof(cval))249ret = NSID_ERROR;250exit(NSID_PASS);251}252253close(child_ready_pipe[1]);254close(parent_ready_pipe[0]);255256/* Wait until the child has created everything. */257if (read(child_ready_pipe[0], &nr_mounts, sizeof(nr_mounts)) !=258sizeof(nr_mounts))259ret = NSID_ERROR;260261ret = validate_external_listmount(pid, nr_mounts);262263if (write(parent_ready_pipe[1], &pval, sizeof(pval)) != sizeof(pval))264ret = NSID_ERROR;265266child_ret = wait_for_pid(pid);267if (child_ret != NSID_PASS)268ret = child_ret;269handle_result(ret, "test listmount ns id");270}271272int main(void)273{274int ret;275276ksft_print_header();277ret = statmount(0, 0, 0, NULL, 0, 0);278assert(ret == -1);279if (errno == ENOSYS)280ksft_exit_skip("statmount() syscall not supported\n");281282ksft_set_plan(2);283test_statmount_mnt_ns_id();284test_listmount_ns();285286if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)287ksft_exit_fail();288else289ksft_exit_pass();290}291292293