]> Git Repo - linux.git/blob - tools/testing/selftests/filesystems/statmount/statmount_test.c
Merge patch series "riscv: Extension parsing fixes"
[linux.git] / tools / testing / selftests / filesystems / statmount / statmount_test.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 #define _GNU_SOURCE
4
5 #include <assert.h>
6 #include <stddef.h>
7 #include <stdint.h>
8 #include <sched.h>
9 #include <fcntl.h>
10 #include <sys/param.h>
11 #include <sys/mount.h>
12 #include <sys/stat.h>
13 #include <sys/statfs.h>
14 #include <linux/mount.h>
15 #include <linux/stat.h>
16 #include <asm/unistd.h>
17
18 #include "../../kselftest.h"
19
20 static const char *const known_fs[] = {
21         "9p", "adfs", "affs", "afs", "aio", "anon_inodefs", "apparmorfs",
22         "autofs", "bcachefs", "bdev", "befs", "bfs", "binder", "binfmt_misc",
23         "bpf", "btrfs", "btrfs_test_fs", "ceph", "cgroup", "cgroup2", "cifs",
24         "coda", "configfs", "cpuset", "cramfs", "cxl", "dax", "debugfs",
25         "devpts", "devtmpfs", "dmabuf", "drm", "ecryptfs", "efivarfs", "efs",
26         "erofs", "exfat", "ext2", "ext3", "ext4", "f2fs", "functionfs",
27         "fuse", "fuseblk", "fusectl", "gadgetfs", "gfs2", "gfs2meta", "hfs",
28         "hfsplus", "hostfs", "hpfs", "hugetlbfs", "ibmasmfs", "iomem",
29         "ipathfs", "iso9660", "jffs2", "jfs", "minix", "mqueue", "msdos",
30         "nfs", "nfs4", "nfsd", "nilfs2", "nsfs", "ntfs", "ntfs3", "ocfs2",
31         "ocfs2_dlmfs", "ocxlflash", "omfs", "openpromfs", "overlay", "pipefs",
32         "proc", "pstore", "pvfs2", "qnx4", "qnx6", "ramfs", "reiserfs",
33         "resctrl", "romfs", "rootfs", "rpc_pipefs", "s390_hypfs", "secretmem",
34         "securityfs", "selinuxfs", "smackfs", "smb3", "sockfs", "spufs",
35         "squashfs", "sysfs", "sysv", "tmpfs", "tracefs", "ubifs", "udf",
36         "ufs", "v7", "vboxsf", "vfat", "virtiofs", "vxfs", "xenfs", "xfs",
37         "zonefs", NULL };
38
39 static int statmount(uint64_t mnt_id, uint64_t mask, struct statmount *buf,
40                      size_t bufsize, unsigned int flags)
41 {
42         struct mnt_id_req req = {
43                 .size = MNT_ID_REQ_SIZE_VER0,
44                 .mnt_id = mnt_id,
45                 .param = mask,
46         };
47
48         return syscall(__NR_statmount, &req, buf, bufsize, flags);
49 }
50
51 static struct statmount *statmount_alloc(uint64_t mnt_id, uint64_t mask, unsigned int flags)
52 {
53         size_t bufsize = 1 << 15;
54         struct statmount *buf = NULL, *tmp = alloca(bufsize);
55         int tofree = 0;
56         int ret;
57
58         for (;;) {
59                 ret = statmount(mnt_id, mask, tmp, bufsize, flags);
60                 if (ret != -1)
61                         break;
62                 if (tofree)
63                         free(tmp);
64                 if (errno != EOVERFLOW)
65                         return NULL;
66                 bufsize <<= 1;
67                 tofree = 1;
68                 tmp = malloc(bufsize);
69                 if (!tmp)
70                         return NULL;
71         }
72         buf = malloc(tmp->size);
73         if (buf)
74                 memcpy(buf, tmp, tmp->size);
75         if (tofree)
76                 free(tmp);
77
78         return buf;
79 }
80
81 static void write_file(const char *path, const char *val)
82 {
83         int fd = open(path, O_WRONLY);
84         size_t len = strlen(val);
85         int ret;
86
87         if (fd == -1)
88                 ksft_exit_fail_msg("opening %s for write: %s\n", path, strerror(errno));
89
90         ret = write(fd, val, len);
91         if (ret == -1)
92                 ksft_exit_fail_msg("writing to %s: %s\n", path, strerror(errno));
93         if (ret != len)
94                 ksft_exit_fail_msg("short write to %s\n", path);
95
96         ret = close(fd);
97         if (ret == -1)
98                 ksft_exit_fail_msg("closing %s\n", path);
99 }
100
101 static uint64_t get_mnt_id(const char *name, const char *path, uint64_t mask)
102 {
103         struct statx sx;
104         int ret;
105
106         ret = statx(AT_FDCWD, path, 0, mask, &sx);
107         if (ret == -1)
108                 ksft_exit_fail_msg("retrieving %s mount ID for %s: %s\n",
109                                    mask & STATX_MNT_ID_UNIQUE ? "unique" : "old",
110                                    name, strerror(errno));
111         if (!(sx.stx_mask & mask))
112                 ksft_exit_fail_msg("no %s mount ID available for %s\n",
113                                    mask & STATX_MNT_ID_UNIQUE ? "unique" : "old",
114                                    name);
115
116         return sx.stx_mnt_id;
117 }
118
119
120 static char root_mntpoint[] = "/tmp/statmount_test_root.XXXXXX";
121 static int orig_root;
122 static uint64_t root_id, parent_id;
123 static uint32_t old_root_id, old_parent_id;
124
125
126 static void cleanup_namespace(void)
127 {
128         fchdir(orig_root);
129         chroot(".");
130         umount2(root_mntpoint, MNT_DETACH);
131         rmdir(root_mntpoint);
132 }
133
134 static void setup_namespace(void)
135 {
136         int ret;
137         char buf[32];
138         uid_t uid = getuid();
139         gid_t gid = getgid();
140
141         ret = unshare(CLONE_NEWNS|CLONE_NEWUSER);
142         if (ret == -1)
143                 ksft_exit_fail_msg("unsharing mountns and userns: %s\n",
144                                    strerror(errno));
145
146         sprintf(buf, "0 %d 1", uid);
147         write_file("/proc/self/uid_map", buf);
148         write_file("/proc/self/setgroups", "deny");
149         sprintf(buf, "0 %d 1", gid);
150         write_file("/proc/self/gid_map", buf);
151
152         ret = mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL);
153         if (ret == -1)
154                 ksft_exit_fail_msg("making mount tree private: %s\n",
155                                    strerror(errno));
156
157         if (!mkdtemp(root_mntpoint))
158                 ksft_exit_fail_msg("creating temporary directory %s: %s\n",
159                                    root_mntpoint, strerror(errno));
160
161         old_parent_id = get_mnt_id("parent", root_mntpoint, STATX_MNT_ID);
162         parent_id = get_mnt_id("parent", root_mntpoint, STATX_MNT_ID_UNIQUE);
163
164         orig_root = open("/", O_PATH);
165         if (orig_root == -1)
166                 ksft_exit_fail_msg("opening root directory: %s",
167                                    strerror(errno));
168
169         atexit(cleanup_namespace);
170
171         ret = mount(root_mntpoint, root_mntpoint, NULL, MS_BIND, NULL);
172         if (ret == -1)
173                 ksft_exit_fail_msg("mounting temp root %s: %s\n",
174                                    root_mntpoint, strerror(errno));
175
176         ret = chroot(root_mntpoint);
177         if (ret == -1)
178                 ksft_exit_fail_msg("chroot to temp root %s: %s\n",
179                                    root_mntpoint, strerror(errno));
180
181         ret = chdir("/");
182         if (ret == -1)
183                 ksft_exit_fail_msg("chdir to root: %s\n", strerror(errno));
184
185         old_root_id = get_mnt_id("root", "/", STATX_MNT_ID);
186         root_id = get_mnt_id("root", "/", STATX_MNT_ID_UNIQUE);
187 }
188
189 static int setup_mount_tree(int log2_num)
190 {
191         int ret, i;
192
193         ret = mount("", "/", NULL, MS_REC|MS_SHARED, NULL);
194         if (ret == -1) {
195                 ksft_test_result_fail("making mount tree shared: %s\n",
196                                    strerror(errno));
197                 return -1;
198         }
199
200         for (i = 0; i < log2_num; i++) {
201                 ret = mount("/", "/", NULL, MS_BIND, NULL);
202                 if (ret == -1) {
203                         ksft_test_result_fail("mounting submount %s: %s\n",
204                                               root_mntpoint, strerror(errno));
205                         return -1;
206                 }
207         }
208         return 0;
209 }
210
211 static ssize_t listmount(uint64_t mnt_id, uint64_t last_mnt_id,
212                          uint64_t list[], size_t num, unsigned int flags)
213 {
214         struct mnt_id_req req = {
215                 .size = MNT_ID_REQ_SIZE_VER0,
216                 .mnt_id = mnt_id,
217                 .param = last_mnt_id,
218         };
219
220         return syscall(__NR_listmount, &req, list, num, flags);
221 }
222
223 static void test_listmount_empty_root(void)
224 {
225         ssize_t res;
226         const unsigned int size = 32;
227         uint64_t list[size];
228
229         res = listmount(LSMT_ROOT, 0, list, size, 0);
230         if (res == -1) {
231                 ksft_test_result_fail("listmount: %s\n", strerror(errno));
232                 return;
233         }
234         if (res != 1) {
235                 ksft_test_result_fail("listmount result is %zi != 1\n", res);
236                 return;
237         }
238
239         if (list[0] != root_id) {
240                 ksft_test_result_fail("listmount ID doesn't match 0x%llx != 0x%llx\n",
241                                       (unsigned long long) list[0],
242                                       (unsigned long long) root_id);
243                 return;
244         }
245
246         ksft_test_result_pass("listmount empty root\n");
247 }
248
249 static void test_statmount_zero_mask(void)
250 {
251         struct statmount sm;
252         int ret;
253
254         ret = statmount(root_id, 0, &sm, sizeof(sm), 0);
255         if (ret == -1) {
256                 ksft_test_result_fail("statmount zero mask: %s\n",
257                                       strerror(errno));
258                 return;
259         }
260         if (sm.size != sizeof(sm)) {
261                 ksft_test_result_fail("unexpected size: %u != %u\n",
262                                       sm.size, (uint32_t) sizeof(sm));
263                 return;
264         }
265         if (sm.mask != 0) {
266                 ksft_test_result_fail("unexpected mask: 0x%llx != 0x0\n",
267                                       (unsigned long long) sm.mask);
268                 return;
269         }
270
271         ksft_test_result_pass("statmount zero mask\n");
272 }
273
274 static void test_statmount_mnt_basic(void)
275 {
276         struct statmount sm;
277         int ret;
278         uint64_t mask = STATMOUNT_MNT_BASIC;
279
280         ret = statmount(root_id, mask, &sm, sizeof(sm), 0);
281         if (ret == -1) {
282                 ksft_test_result_fail("statmount mnt basic: %s\n",
283                                       strerror(errno));
284                 return;
285         }
286         if (sm.size != sizeof(sm)) {
287                 ksft_test_result_fail("unexpected size: %u != %u\n",
288                                       sm.size, (uint32_t) sizeof(sm));
289                 return;
290         }
291         if (sm.mask != mask) {
292                 ksft_test_result_skip("statmount mnt basic unavailable\n");
293                 return;
294         }
295
296         if (sm.mnt_id != root_id) {
297                 ksft_test_result_fail("unexpected root ID: 0x%llx != 0x%llx\n",
298                                       (unsigned long long) sm.mnt_id,
299                                       (unsigned long long) root_id);
300                 return;
301         }
302
303         if (sm.mnt_id_old != old_root_id) {
304                 ksft_test_result_fail("unexpected old root ID: %u != %u\n",
305                                       sm.mnt_id_old, old_root_id);
306                 return;
307         }
308
309         if (sm.mnt_parent_id != parent_id) {
310                 ksft_test_result_fail("unexpected parent ID: 0x%llx != 0x%llx\n",
311                                       (unsigned long long) sm.mnt_parent_id,
312                                       (unsigned long long) parent_id);
313                 return;
314         }
315
316         if (sm.mnt_parent_id_old != old_parent_id) {
317                 ksft_test_result_fail("unexpected old parent ID: %u != %u\n",
318                                       sm.mnt_parent_id_old, old_parent_id);
319                 return;
320         }
321
322         if (sm.mnt_propagation != MS_PRIVATE) {
323                 ksft_test_result_fail("unexpected propagation: 0x%llx\n",
324                                       (unsigned long long) sm.mnt_propagation);
325                 return;
326         }
327
328         ksft_test_result_pass("statmount mnt basic\n");
329 }
330
331
332 static void test_statmount_sb_basic(void)
333 {
334         struct statmount sm;
335         int ret;
336         uint64_t mask = STATMOUNT_SB_BASIC;
337         struct statx sx;
338         struct statfs sf;
339
340         ret = statmount(root_id, mask, &sm, sizeof(sm), 0);
341         if (ret == -1) {
342                 ksft_test_result_fail("statmount sb basic: %s\n",
343                                       strerror(errno));
344                 return;
345         }
346         if (sm.size != sizeof(sm)) {
347                 ksft_test_result_fail("unexpected size: %u != %u\n",
348                                       sm.size, (uint32_t) sizeof(sm));
349                 return;
350         }
351         if (sm.mask != mask) {
352                 ksft_test_result_skip("statmount sb basic unavailable\n");
353                 return;
354         }
355
356         ret = statx(AT_FDCWD, "/", 0, 0, &sx);
357         if (ret == -1) {
358                 ksft_test_result_fail("stat root failed: %s\n",
359                                       strerror(errno));
360                 return;
361         }
362
363         if (sm.sb_dev_major != sx.stx_dev_major ||
364             sm.sb_dev_minor != sx.stx_dev_minor) {
365                 ksft_test_result_fail("unexpected sb dev %u:%u != %u:%u\n",
366                                       sm.sb_dev_major, sm.sb_dev_minor,
367                                       sx.stx_dev_major, sx.stx_dev_minor);
368                 return;
369         }
370
371         ret = statfs("/", &sf);
372         if (ret == -1) {
373                 ksft_test_result_fail("statfs root failed: %s\n",
374                                       strerror(errno));
375                 return;
376         }
377
378         if (sm.sb_magic != sf.f_type) {
379                 ksft_test_result_fail("unexpected sb magic: 0x%llx != 0x%lx\n",
380                                       (unsigned long long) sm.sb_magic,
381                                       sf.f_type);
382                 return;
383         }
384
385         ksft_test_result_pass("statmount sb basic\n");
386 }
387
388 static void test_statmount_mnt_point(void)
389 {
390         struct statmount *sm;
391
392         sm = statmount_alloc(root_id, STATMOUNT_MNT_POINT, 0);
393         if (!sm) {
394                 ksft_test_result_fail("statmount mount point: %s\n",
395                                       strerror(errno));
396                 return;
397         }
398
399         if (strcmp(sm->str + sm->mnt_point, "/") != 0) {
400                 ksft_test_result_fail("unexpected mount point: '%s' != '/'\n",
401                                       sm->str + sm->mnt_point);
402                 goto out;
403         }
404         ksft_test_result_pass("statmount mount point\n");
405 out:
406         free(sm);
407 }
408
409 static void test_statmount_mnt_root(void)
410 {
411         struct statmount *sm;
412         const char *mnt_root, *last_dir, *last_root;
413
414         last_dir = strrchr(root_mntpoint, '/');
415         assert(last_dir);
416         last_dir++;
417
418         sm = statmount_alloc(root_id, STATMOUNT_MNT_ROOT, 0);
419         if (!sm) {
420                 ksft_test_result_fail("statmount mount root: %s\n",
421                                       strerror(errno));
422                 return;
423         }
424         mnt_root = sm->str + sm->mnt_root;
425         last_root = strrchr(mnt_root, '/');
426         if (last_root)
427                 last_root++;
428         else
429                 last_root = mnt_root;
430
431         if (strcmp(last_dir, last_root) != 0) {
432                 ksft_test_result_fail("unexpected mount root last component: '%s' != '%s'\n",
433                                       last_root, last_dir);
434                 goto out;
435         }
436         ksft_test_result_pass("statmount mount root\n");
437 out:
438         free(sm);
439 }
440
441 static void test_statmount_fs_type(void)
442 {
443         struct statmount *sm;
444         const char *fs_type;
445         const char *const *s;
446
447         sm = statmount_alloc(root_id, STATMOUNT_FS_TYPE, 0);
448         if (!sm) {
449                 ksft_test_result_fail("statmount fs type: %s\n",
450                                       strerror(errno));
451                 return;
452         }
453         fs_type = sm->str + sm->fs_type;
454         for (s = known_fs; s != NULL; s++) {
455                 if (strcmp(fs_type, *s) == 0)
456                         break;
457         }
458         if (!s)
459                 ksft_print_msg("unknown filesystem type: %s\n", fs_type);
460
461         ksft_test_result_pass("statmount fs type\n");
462         free(sm);
463 }
464
465 static void test_statmount_string(uint64_t mask, size_t off, const char *name)
466 {
467         struct statmount *sm;
468         size_t len, shortsize, exactsize;
469         uint32_t start, i;
470         int ret;
471
472         sm = statmount_alloc(root_id, mask, 0);
473         if (!sm) {
474                 ksft_test_result_fail("statmount %s: %s\n", name,
475                                       strerror(errno));
476                 goto out;
477         }
478         if (sm->size < sizeof(*sm)) {
479                 ksft_test_result_fail("unexpected size: %u < %u\n",
480                                       sm->size, (uint32_t) sizeof(*sm));
481                 goto out;
482         }
483         if (sm->mask != mask) {
484                 ksft_test_result_skip("statmount %s unavailable\n", name);
485                 goto out;
486         }
487         len = sm->size - sizeof(*sm);
488         start = ((uint32_t *) sm)[off];
489
490         for (i = start;; i++) {
491                 if (i >= len) {
492                         ksft_test_result_fail("string out of bounds\n");
493                         goto out;
494                 }
495                 if (!sm->str[i])
496                         break;
497         }
498         exactsize = sm->size;
499         shortsize = sizeof(*sm) + i;
500
501         ret = statmount(root_id, mask, sm, exactsize, 0);
502         if (ret == -1) {
503                 ksft_test_result_fail("statmount exact size: %s\n",
504                                       strerror(errno));
505                 goto out;
506         }
507         errno = 0;
508         ret = statmount(root_id, mask, sm, shortsize, 0);
509         if (ret != -1 || errno != EOVERFLOW) {
510                 ksft_test_result_fail("should have failed with EOVERFLOW: %s\n",
511                                       strerror(errno));
512                 goto out;
513         }
514
515         ksft_test_result_pass("statmount string %s\n", name);
516 out:
517         free(sm);
518 }
519
520 static void test_listmount_tree(void)
521 {
522         ssize_t res;
523         const unsigned int log2_num = 4;
524         const unsigned int step = 3;
525         const unsigned int size = (1 << log2_num) + step + 1;
526         size_t num, expect = 1 << log2_num;
527         uint64_t list[size];
528         uint64_t list2[size];
529         size_t i;
530
531
532         res = setup_mount_tree(log2_num);
533         if (res == -1)
534                 return;
535
536         num = res = listmount(LSMT_ROOT, 0, list, size, 0);
537         if (res == -1) {
538                 ksft_test_result_fail("listmount: %s\n", strerror(errno));
539                 return;
540         }
541         if (num != expect) {
542                 ksft_test_result_fail("listmount result is %zi != %zi\n",
543                                       res, expect);
544                 return;
545         }
546
547         for (i = 0; i < size - step;) {
548                 res = listmount(LSMT_ROOT, i ? list2[i - 1] : 0, list2 + i, step, 0);
549                 if (res == -1)
550                         ksft_test_result_fail("short listmount: %s\n",
551                                               strerror(errno));
552                 i += res;
553                 if (res < step)
554                         break;
555         }
556         if (i != num) {
557                 ksft_test_result_fail("different number of entries: %zu != %zu\n",
558                                       i, num);
559                 return;
560         }
561         for (i = 0; i < num; i++) {
562                 if (list2[i] != list[i]) {
563                         ksft_test_result_fail("different value for entry %zu: 0x%llx != 0x%llx\n",
564                                               i,
565                                               (unsigned long long) list2[i],
566                                               (unsigned long long) list[i]);
567                 }
568         }
569
570         ksft_test_result_pass("listmount tree\n");
571 }
572
573 #define str_off(memb) (offsetof(struct statmount, memb) / sizeof(uint32_t))
574
575 int main(void)
576 {
577         int ret;
578         uint64_t all_mask = STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC |
579                 STATMOUNT_PROPAGATE_FROM | STATMOUNT_MNT_ROOT |
580                 STATMOUNT_MNT_POINT | STATMOUNT_FS_TYPE;
581
582         ksft_print_header();
583
584         ret = statmount(0, 0, NULL, 0, 0);
585         assert(ret == -1);
586         if (errno == ENOSYS)
587                 ksft_exit_skip("statmount() syscall not supported\n");
588
589         setup_namespace();
590
591         ksft_set_plan(14);
592         test_listmount_empty_root();
593         test_statmount_zero_mask();
594         test_statmount_mnt_basic();
595         test_statmount_sb_basic();
596         test_statmount_mnt_root();
597         test_statmount_mnt_point();
598         test_statmount_fs_type();
599         test_statmount_string(STATMOUNT_MNT_ROOT, str_off(mnt_root), "mount root");
600         test_statmount_string(STATMOUNT_MNT_POINT, str_off(mnt_point), "mount point");
601         test_statmount_string(STATMOUNT_FS_TYPE, str_off(fs_type), "fs type");
602         test_statmount_string(all_mask, str_off(mnt_root), "mount root & all");
603         test_statmount_string(all_mask, str_off(mnt_point), "mount point & all");
604         test_statmount_string(all_mask, str_off(fs_type), "fs type & all");
605
606         test_listmount_tree();
607
608
609         if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
610                 ksft_exit_fail();
611         else
612                 ksft_exit_pass();
613 }
This page took 0.073899 seconds and 4 git commands to generate.