Path: blob/master/tools/testing/selftests/filesystems/statmount/statmount_test_ns.c
50360 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, 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 int _test_statmount_mnt_ns_id_by_fd(void)131{132struct statmount sm;133uint64_t mnt_ns_id;134int ret, fd, mounted = 1, status = NSID_ERROR;135char mnt[] = "/statmount.fd.XXXXXX";136137ret = get_mnt_ns_id("/proc/self/ns/mnt", &mnt_ns_id);138if (ret != NSID_PASS)139return ret;140141if (!mkdtemp(mnt)) {142ksft_print_msg("statmount by fd mnt ns id mkdtemp: %s\n", strerror(errno));143return NSID_ERROR;144}145146if (mount(mnt, mnt, NULL, MS_BIND, 0)) {147ksft_print_msg("statmount by fd mnt ns id mount: %s\n", strerror(errno));148status = NSID_ERROR;149goto err;150}151152fd = open(mnt, O_PATH);153if (fd < 0) {154ksft_print_msg("statmount by fd mnt ns id open: %s\n", strerror(errno));155goto err;156}157158ret = statmount(0, 0, fd, STATMOUNT_MNT_NS_ID, &sm, sizeof(sm), STATMOUNT_BY_FD);159if (ret == -1) {160ksft_print_msg("statmount mnt ns id statmount: %s\n", strerror(errno));161status = NSID_ERROR;162goto out;163}164165if (sm.size != sizeof(sm)) {166ksft_print_msg("unexpected size: %u != %u\n", sm.size,167(uint32_t)sizeof(sm));168status = NSID_FAIL;169goto out;170}171if (sm.mask != STATMOUNT_MNT_NS_ID) {172ksft_print_msg("statmount mnt ns id unavailable\n");173status = NSID_SKIP;174goto out;175}176177if (sm.mnt_ns_id != mnt_ns_id) {178ksft_print_msg("unexpected mnt ns ID: 0x%llx != 0x%llx\n",179(unsigned long long)sm.mnt_ns_id,180(unsigned long long)mnt_ns_id);181status = NSID_FAIL;182goto out;183}184185mounted = 0;186if (umount2(mnt, MNT_DETACH)) {187ksft_print_msg("statmount by fd mnt ns id umount2: %s\n", strerror(errno));188goto out;189}190191ret = statmount(0, 0, fd, STATMOUNT_MNT_NS_ID, &sm, sizeof(sm), STATMOUNT_BY_FD);192if (ret == -1) {193ksft_print_msg("statmount mnt ns id statmount: %s\n", strerror(errno));194status = NSID_ERROR;195goto out;196}197198if (sm.size != sizeof(sm)) {199ksft_print_msg("unexpected size: %u != %u\n", sm.size,200(uint32_t)sizeof(sm));201status = NSID_FAIL;202goto out;203}204205if (sm.mask == STATMOUNT_MNT_NS_ID) {206ksft_print_msg("unexpected STATMOUNT_MNT_NS_ID in mask\n");207status = NSID_FAIL;208goto out;209}210211status = NSID_PASS;212out:213close(fd);214if (mounted)215umount2(mnt, MNT_DETACH);216err:217rmdir(mnt);218return status;219}220221222static void test_statmount_mnt_ns_id(void)223{224pid_t pid;225int ret;226227pid = fork();228if (pid < 0)229ksft_exit_fail_msg("failed to fork: %s\n", strerror(errno));230231/* We're the original pid, wait for the result. */232if (pid != 0) {233ret = wait_for_pid(pid);234handle_result(ret, "test statmount ns id");235return;236}237238ret = setup_namespace();239if (ret != NSID_PASS)240exit(ret);241ret = _test_statmount_mnt_ns_id();242if (ret != NSID_PASS)243exit(ret);244ret = _test_statmount_mnt_ns_id_by_fd();245exit(ret);246}247248static int validate_external_listmount(pid_t pid, uint64_t child_nr_mounts)249{250uint64_t list[256];251uint64_t mnt_ns_id;252uint64_t nr_mounts;253char buf[256];254int ret;255256/* Get the mount ns id for our child. */257snprintf(buf, sizeof(buf), "/proc/%lu/ns/mnt", (unsigned long)pid);258ret = get_mnt_ns_id(buf, &mnt_ns_id);259260nr_mounts = listmount(LSMT_ROOT, mnt_ns_id, 0, list, 256, 0);261if (nr_mounts == (uint64_t)-1) {262ksft_print_msg("listmount: %s\n", strerror(errno));263return NSID_ERROR;264}265266if (nr_mounts != child_nr_mounts) {267ksft_print_msg("listmount results is %zi != %zi\n", nr_mounts,268child_nr_mounts);269return NSID_FAIL;270}271272/* Validate that all of our entries match our mnt_ns_id. */273for (int i = 0; i < nr_mounts; i++) {274struct statmount sm;275276ret = statmount(list[i], mnt_ns_id, 0, STATMOUNT_MNT_NS_ID, &sm,277sizeof(sm), 0);278if (ret < 0) {279ksft_print_msg("statmount mnt ns id: %s\n", strerror(errno));280return NSID_ERROR;281}282283if (sm.mask != STATMOUNT_MNT_NS_ID) {284ksft_print_msg("statmount mnt ns id unavailable\n");285return NSID_SKIP;286}287288if (sm.mnt_ns_id != mnt_ns_id) {289ksft_print_msg("listmount gave us the wrong ns id: 0x%llx != 0x%llx\n",290(unsigned long long)sm.mnt_ns_id,291(unsigned long long)mnt_ns_id);292return NSID_FAIL;293}294}295296return NSID_PASS;297}298299static void test_listmount_ns(void)300{301uint64_t nr_mounts;302char pval;303int child_ready_pipe[2];304int parent_ready_pipe[2];305pid_t pid;306int ret, child_ret;307308if (pipe(child_ready_pipe) < 0)309ksft_exit_fail_msg("failed to create the child pipe: %s\n",310strerror(errno));311if (pipe(parent_ready_pipe) < 0)312ksft_exit_fail_msg("failed to create the parent pipe: %s\n",313strerror(errno));314315pid = fork();316if (pid < 0)317ksft_exit_fail_msg("failed to fork: %s\n", strerror(errno));318319if (pid == 0) {320char cval;321uint64_t list[256];322323close(child_ready_pipe[0]);324close(parent_ready_pipe[1]);325326ret = setup_namespace();327if (ret != NSID_PASS)328exit(ret);329330nr_mounts = listmount(LSMT_ROOT, 0, 0, list, 256, 0);331if (nr_mounts == (uint64_t)-1) {332ksft_print_msg("listmount: %s\n", strerror(errno));333exit(NSID_FAIL);334}335336/*337* Tell our parent how many mounts we have, and then wait for it338* to tell us we're done.339*/340if (write(child_ready_pipe[1], &nr_mounts, sizeof(nr_mounts)) !=341sizeof(nr_mounts))342ret = NSID_ERROR;343if (read(parent_ready_pipe[0], &cval, sizeof(cval)) != sizeof(cval))344ret = NSID_ERROR;345exit(NSID_PASS);346}347348close(child_ready_pipe[1]);349close(parent_ready_pipe[0]);350351/* Wait until the child has created everything. */352if (read(child_ready_pipe[0], &nr_mounts, sizeof(nr_mounts)) !=353sizeof(nr_mounts))354ret = NSID_ERROR;355356ret = validate_external_listmount(pid, nr_mounts);357358if (write(parent_ready_pipe[1], &pval, sizeof(pval)) != sizeof(pval))359ret = NSID_ERROR;360361child_ret = wait_for_pid(pid);362if (child_ret != NSID_PASS)363ret = child_ret;364handle_result(ret, "test listmount ns id");365}366367int main(void)368{369int ret;370371ksft_print_header();372ret = statmount(0, 0, 0, 0, NULL, 0, 0);373assert(ret == -1);374if (errno == ENOSYS)375ksft_exit_skip("statmount() syscall not supported\n");376377ksft_set_plan(2);378test_statmount_mnt_ns_id();379test_listmount_ns();380381if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)382ksft_exit_fail();383else384ksft_exit_pass();385}386387388