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