1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <errno.h> 4 #include <fcntl.h> 5 #include <limits.h> 6 #include <sched.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <linux/nsfs.h> 11 #include <sys/mount.h> 12 #include <sys/stat.h> 13 #include <sys/types.h> 14 #include <sys/wait.h> 15 #include <unistd.h> 16 #include "../kselftest_harness.h" 17 #include "../filesystems/utils.h" 18 19 #ifndef FD_NSFS_ROOT 20 #define FD_NSFS_ROOT -10003 /* Root of the nsfs filesystem */ 21 #endif 22 23 /* 24 * Test that initial namespaces can be reopened via file handle. 25 * Initial namespaces should have active ref count of 1 from boot. 26 */ 27 TEST(init_ns_always_active) 28 { 29 struct file_handle *handle; 30 int mount_id; 31 int ret; 32 int fd1, fd2; 33 struct stat st1, st2; 34 35 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ); 36 ASSERT_NE(handle, NULL); 37 38 /* Open initial network namespace */ 39 fd1 = open("/proc/1/ns/net", O_RDONLY); 40 ASSERT_GE(fd1, 0); 41 42 /* Get file handle for initial namespace */ 43 handle->handle_bytes = MAX_HANDLE_SZ; 44 ret = name_to_handle_at(fd1, "", handle, &mount_id, AT_EMPTY_PATH); 45 if (ret < 0 && errno == EOPNOTSUPP) { 46 SKIP(free(handle); close(fd1); 47 return, "nsfs doesn't support file handles"); 48 } 49 ASSERT_EQ(ret, 0); 50 51 /* Close the namespace fd */ 52 close(fd1); 53 54 /* Try to reopen via file handle - should succeed since init ns is always active */ 55 fd2 = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); 56 if (fd2 < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) { 57 SKIP(free(handle); 58 return, "open_by_handle_at with FD_NSFS_ROOT not supported"); 59 } 60 ASSERT_GE(fd2, 0); 61 62 /* Verify we opened the same namespace */ 63 fd1 = open("/proc/1/ns/net", O_RDONLY); 64 ASSERT_GE(fd1, 0); 65 ASSERT_EQ(fstat(fd1, &st1), 0); 66 ASSERT_EQ(fstat(fd2, &st2), 0); 67 ASSERT_EQ(st1.st_ino, st2.st_ino); 68 69 close(fd1); 70 close(fd2); 71 free(handle); 72 } 73 74 /* 75 * Test namespace lifecycle: create a namespace in a child process, 76 * get a file handle while it's active, then try to reopen after 77 * the process exits (namespace becomes inactive). 78 */ 79 TEST(ns_inactive_after_exit) 80 { 81 struct file_handle *handle; 82 int mount_id; 83 int ret; 84 int fd; 85 int pipefd[2]; 86 pid_t pid; 87 int status; 88 char buf[sizeof(*handle) + MAX_HANDLE_SZ]; 89 90 /* Create pipe for passing file handle from child */ 91 ASSERT_EQ(pipe(pipefd), 0); 92 93 pid = fork(); 94 ASSERT_GE(pid, 0); 95 96 if (pid == 0) { 97 /* Child process */ 98 close(pipefd[0]); 99 100 /* Create new network namespace */ 101 ret = unshare(CLONE_NEWNET); 102 if (ret < 0) { 103 close(pipefd[1]); 104 exit(1); 105 } 106 107 /* Open our new namespace */ 108 fd = open("/proc/self/ns/net", O_RDONLY); 109 if (fd < 0) { 110 close(pipefd[1]); 111 exit(1); 112 } 113 114 /* Get file handle for the namespace */ 115 handle = (struct file_handle *)buf; 116 handle->handle_bytes = MAX_HANDLE_SZ; 117 ret = name_to_handle_at(fd, "", handle, &mount_id, AT_EMPTY_PATH); 118 close(fd); 119 120 if (ret < 0) { 121 close(pipefd[1]); 122 exit(1); 123 } 124 125 /* Send handle to parent */ 126 write(pipefd[1], buf, sizeof(*handle) + handle->handle_bytes); 127 close(pipefd[1]); 128 129 /* Exit - namespace should become inactive */ 130 exit(0); 131 } 132 133 /* Parent process */ 134 close(pipefd[1]); 135 136 /* Read file handle from child */ 137 ret = read(pipefd[0], buf, sizeof(buf)); 138 close(pipefd[0]); 139 140 /* Wait for child to exit */ 141 waitpid(pid, &status, 0); 142 ASSERT_TRUE(WIFEXITED(status)); 143 ASSERT_EQ(WEXITSTATUS(status), 0); 144 145 ASSERT_GT(ret, 0); 146 handle = (struct file_handle *)buf; 147 148 /* Try to reopen namespace - should fail with ENOENT since it's inactive */ 149 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); 150 ASSERT_LT(fd, 0); 151 /* Should fail with ENOENT (namespace inactive) or ESTALE */ 152 ASSERT_TRUE(errno == ENOENT || errno == ESTALE); 153 } 154 155 TEST_HARNESS_MAIN 156