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