~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/filesystems/statmount/statmount_test_ns.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-or-later
  2 
  3 #define _GNU_SOURCE
  4 
  5 #include <assert.h>
  6 #include <fcntl.h>
  7 #include <limits.h>
  8 #include <sched.h>
  9 #include <stdlib.h>
 10 #include <sys/mount.h>
 11 #include <sys/stat.h>
 12 #include <sys/wait.h>
 13 #include <linux/nsfs.h>
 14 #include <linux/stat.h>
 15 
 16 #include "statmount.h"
 17 #include "../../kselftest.h"
 18 
 19 #define NSID_PASS 0
 20 #define NSID_FAIL 1
 21 #define NSID_SKIP 2
 22 #define NSID_ERROR 3
 23 
 24 static void handle_result(int ret, const char *testname)
 25 {
 26         if (ret == NSID_PASS)
 27                 ksft_test_result_pass("%s\n", testname);
 28         else if (ret == NSID_FAIL)
 29                 ksft_test_result_fail("%s\n", testname);
 30         else if (ret == NSID_ERROR)
 31                 ksft_exit_fail_msg("%s\n", testname);
 32         else
 33                 ksft_test_result_skip("%s\n", testname);
 34 }
 35 
 36 static inline int wait_for_pid(pid_t pid)
 37 {
 38         int status, ret;
 39 
 40 again:
 41         ret = waitpid(pid, &status, 0);
 42         if (ret == -1) {
 43                 if (errno == EINTR)
 44                         goto again;
 45 
 46                 ksft_print_msg("waitpid returned -1, errno=%d\n", errno);
 47                 return -1;
 48         }
 49 
 50         if (!WIFEXITED(status)) {
 51                 ksft_print_msg(
 52                        "waitpid !WIFEXITED, WIFSIGNALED=%d, WTERMSIG=%d\n",
 53                        WIFSIGNALED(status), WTERMSIG(status));
 54                 return -1;
 55         }
 56 
 57         ret = WEXITSTATUS(status);
 58         return ret;
 59 }
 60 
 61 static int get_mnt_ns_id(const char *mnt_ns, uint64_t *mnt_ns_id)
 62 {
 63         int fd = open(mnt_ns, O_RDONLY);
 64 
 65         if (fd < 0) {
 66                 ksft_print_msg("failed to open for ns %s: %s\n",
 67                                mnt_ns, strerror(errno));
 68                 sleep(60);
 69                 return NSID_ERROR;
 70         }
 71 
 72         if (ioctl(fd, NS_GET_MNTNS_ID, mnt_ns_id) < 0) {
 73                 ksft_print_msg("failed to get the nsid for ns %s: %s\n",
 74                                mnt_ns, strerror(errno));
 75                 return NSID_ERROR;
 76         }
 77         close(fd);
 78         return NSID_PASS;
 79 }
 80 
 81 static int get_mnt_id(const char *path, uint64_t *mnt_id)
 82 {
 83         struct statx sx;
 84         int ret;
 85 
 86         ret = statx(AT_FDCWD, path, 0, STATX_MNT_ID_UNIQUE, &sx);
 87         if (ret == -1) {
 88                 ksft_print_msg("retrieving unique mount ID for %s: %s\n", path,
 89                                strerror(errno));
 90                 return NSID_ERROR;
 91         }
 92 
 93         if (!(sx.stx_mask & STATX_MNT_ID_UNIQUE)) {
 94                 ksft_print_msg("no unique mount ID available for %s\n", path);
 95                 return NSID_ERROR;
 96         }
 97 
 98         *mnt_id = sx.stx_mnt_id;
 99         return NSID_PASS;
100 }
101 
102 static int write_file(const char *path, const char *val)
103 {
104         int fd = open(path, O_WRONLY);
105         size_t len = strlen(val);
106         int ret;
107 
108         if (fd == -1) {
109                 ksft_print_msg("opening %s for write: %s\n", path, strerror(errno));
110                 return NSID_ERROR;
111         }
112 
113         ret = write(fd, val, len);
114         if (ret == -1) {
115                 ksft_print_msg("writing to %s: %s\n", path, strerror(errno));
116                 return NSID_ERROR;
117         }
118         if (ret != len) {
119                 ksft_print_msg("short write to %s\n", path);
120                 return NSID_ERROR;
121         }
122 
123         ret = close(fd);
124         if (ret == -1) {
125                 ksft_print_msg("closing %s\n", path);
126                 return NSID_ERROR;
127         }
128 
129         return NSID_PASS;
130 }
131 
132 static int setup_namespace(void)
133 {
134         int ret;
135         char buf[32];
136         uid_t uid = getuid();
137         gid_t gid = getgid();
138 
139         ret = unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWPID);
140         if (ret == -1)
141                 ksft_exit_fail_msg("unsharing mountns and userns: %s\n",
142                                    strerror(errno));
143 
144         sprintf(buf, "0 %d 1", uid);
145         ret = write_file("/proc/self/uid_map", buf);
146         if (ret != NSID_PASS)
147                 return ret;
148         ret = write_file("/proc/self/setgroups", "deny");
149         if (ret != NSID_PASS)
150                 return ret;
151         sprintf(buf, "0 %d 1", gid);
152         ret = write_file("/proc/self/gid_map", buf);
153         if (ret != NSID_PASS)
154                 return ret;
155 
156         ret = mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL);
157         if (ret == -1) {
158                 ksft_print_msg("making mount tree private: %s\n",
159                                strerror(errno));
160                 return NSID_ERROR;
161         }
162 
163         return NSID_PASS;
164 }
165 
166 static int _test_statmount_mnt_ns_id(void)
167 {
168         struct statmount sm;
169         uint64_t mnt_ns_id;
170         uint64_t root_id;
171         int ret;
172 
173         ret = get_mnt_ns_id("/proc/self/ns/mnt", &mnt_ns_id);
174         if (ret != NSID_PASS)
175                 return ret;
176 
177         ret = get_mnt_id("/", &root_id);
178         if (ret != NSID_PASS)
179                 return ret;
180 
181         ret = statmount(root_id, 0, STATMOUNT_MNT_NS_ID, &sm, sizeof(sm), 0);
182         if (ret == -1) {
183                 ksft_print_msg("statmount mnt ns id: %s\n", strerror(errno));
184                 return NSID_ERROR;
185         }
186 
187         if (sm.size != sizeof(sm)) {
188                 ksft_print_msg("unexpected size: %u != %u\n", sm.size,
189                                (uint32_t)sizeof(sm));
190                 return NSID_FAIL;
191         }
192         if (sm.mask != STATMOUNT_MNT_NS_ID) {
193                 ksft_print_msg("statmount mnt ns id unavailable\n");
194                 return NSID_SKIP;
195         }
196 
197         if (sm.mnt_ns_id != mnt_ns_id) {
198                 ksft_print_msg("unexpected mnt ns ID: 0x%llx != 0x%llx\n",
199                                (unsigned long long)sm.mnt_ns_id,
200                                (unsigned long long)mnt_ns_id);
201                 return NSID_FAIL;
202         }
203 
204         return NSID_PASS;
205 }
206 
207 static void test_statmount_mnt_ns_id(void)
208 {
209         pid_t pid;
210         int ret;
211 
212         pid = fork();
213         if (pid < 0)
214                 ksft_exit_fail_msg("failed to fork: %s\n", strerror(errno));
215 
216         /* We're the original pid, wait for the result. */
217         if (pid != 0) {
218                 ret = wait_for_pid(pid);
219                 handle_result(ret, "test statmount ns id");
220                 return;
221         }
222 
223         ret = setup_namespace();
224         if (ret != NSID_PASS)
225                 exit(ret);
226         ret = _test_statmount_mnt_ns_id();
227         exit(ret);
228 }
229 
230 static int validate_external_listmount(pid_t pid, uint64_t child_nr_mounts)
231 {
232         uint64_t list[256];
233         uint64_t mnt_ns_id;
234         uint64_t nr_mounts;
235         char buf[256];
236         int ret;
237 
238         /* Get the mount ns id for our child. */
239         snprintf(buf, sizeof(buf), "/proc/%lu/ns/mnt", (unsigned long)pid);
240         ret = get_mnt_ns_id(buf, &mnt_ns_id);
241 
242         nr_mounts = listmount(LSMT_ROOT, mnt_ns_id, 0, list, 256, 0);
243         if (nr_mounts == (uint64_t)-1) {
244                 ksft_print_msg("listmount: %s\n", strerror(errno));
245                 return NSID_ERROR;
246         }
247 
248         if (nr_mounts != child_nr_mounts) {
249                 ksft_print_msg("listmount results is %zi != %zi\n", nr_mounts,
250                                child_nr_mounts);
251                 return NSID_FAIL;
252         }
253 
254         /* Validate that all of our entries match our mnt_ns_id. */
255         for (int i = 0; i < nr_mounts; i++) {
256                 struct statmount sm;
257 
258                 ret = statmount(list[i], mnt_ns_id, STATMOUNT_MNT_NS_ID, &sm,
259                                 sizeof(sm), 0);
260                 if (ret < 0) {
261                         ksft_print_msg("statmount mnt ns id: %s\n", strerror(errno));
262                         return NSID_ERROR;
263                 }
264 
265                 if (sm.mask != STATMOUNT_MNT_NS_ID) {
266                         ksft_print_msg("statmount mnt ns id unavailable\n");
267                         return NSID_SKIP;
268                 }
269 
270                 if (sm.mnt_ns_id != mnt_ns_id) {
271                         ksft_print_msg("listmount gave us the wrong ns id: 0x%llx != 0x%llx\n",
272                                        (unsigned long long)sm.mnt_ns_id,
273                                        (unsigned long long)mnt_ns_id);
274                         return NSID_FAIL;
275                 }
276         }
277 
278         return NSID_PASS;
279 }
280 
281 static void test_listmount_ns(void)
282 {
283         uint64_t nr_mounts;
284         char pval;
285         int child_ready_pipe[2];
286         int parent_ready_pipe[2];
287         pid_t pid;
288         int ret, child_ret;
289 
290         if (pipe(child_ready_pipe) < 0)
291                 ksft_exit_fail_msg("failed to create the child pipe: %s\n",
292                                    strerror(errno));
293         if (pipe(parent_ready_pipe) < 0)
294                 ksft_exit_fail_msg("failed to create the parent pipe: %s\n",
295                                    strerror(errno));
296 
297         pid = fork();
298         if (pid < 0)
299                 ksft_exit_fail_msg("failed to fork: %s\n", strerror(errno));
300 
301         if (pid == 0) {
302                 char cval;
303                 uint64_t list[256];
304 
305                 close(child_ready_pipe[0]);
306                 close(parent_ready_pipe[1]);
307 
308                 ret = setup_namespace();
309                 if (ret != NSID_PASS)
310                         exit(ret);
311 
312                 nr_mounts = listmount(LSMT_ROOT, 0, 0, list, 256, 0);
313                 if (nr_mounts == (uint64_t)-1) {
314                         ksft_print_msg("listmount: %s\n", strerror(errno));
315                         exit(NSID_FAIL);
316                 }
317 
318                 /*
319                  * Tell our parent how many mounts we have, and then wait for it
320                  * to tell us we're done.
321                  */
322                 write(child_ready_pipe[1], &nr_mounts, sizeof(nr_mounts));
323                 read(parent_ready_pipe[0], &cval, sizeof(cval));
324                 exit(NSID_PASS);
325         }
326 
327         close(child_ready_pipe[1]);
328         close(parent_ready_pipe[0]);
329 
330         /* Wait until the child has created everything. */
331         if (read(child_ready_pipe[0], &nr_mounts, sizeof(nr_mounts)) !=
332             sizeof(nr_mounts))
333                 ret = NSID_ERROR;
334 
335         ret = validate_external_listmount(pid, nr_mounts);
336 
337         if (write(parent_ready_pipe[1], &pval, sizeof(pval)) != sizeof(pval))
338                 ret = NSID_ERROR;
339 
340         child_ret = wait_for_pid(pid);
341         if (child_ret != NSID_PASS)
342                 ret = child_ret;
343         handle_result(ret, "test listmount ns id");
344 }
345 
346 int main(void)
347 {
348         int ret;
349 
350         ksft_print_header();
351         ret = statmount(0, 0, 0, NULL, 0, 0);
352         assert(ret == -1);
353         if (errno == ENOSYS)
354                 ksft_exit_skip("statmount() syscall not supported\n");
355 
356         ksft_set_plan(2);
357         test_statmount_mnt_ns_id();
358         test_listmount_ns();
359 
360         if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
361                 ksft_exit_fail();
362         else
363                 ksft_exit_pass();
364 }
365 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php