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, int fd, uint64_t mask, unsigned int flags) 37 { 38 size_t bufsize = 1 << 15; 39 struct statmount *buf = NULL, *tmp = NULL; 40 int tofree = 0; 41 int ret; 42 43 if (flags & STATMOUNT_BY_FD && fd < 0) 44 return NULL; 45 46 tmp = alloca(bufsize); 47 48 for (;;) { 49 if (flags & STATMOUNT_BY_FD) 50 ret = statmount(0, 0, (uint32_t) fd, mask, tmp, bufsize, flags); 51 else 52 ret = statmount(mnt_id, 0, 0, mask, tmp, bufsize, flags); 53 54 if (ret != -1) 55 break; 56 if (tofree) 57 free(tmp); 58 if (errno != EOVERFLOW) 59 return NULL; 60 bufsize <<= 1; 61 tofree = 1; 62 tmp = malloc(bufsize); 63 if (!tmp) 64 return NULL; 65 } 66 buf = malloc(tmp->size); 67 if (buf) 68 memcpy(buf, tmp, tmp->size); 69 if (tofree) 70 free(tmp); 71 72 return buf; 73 } 74 75 static void write_file(const char *path, const char *val) 76 { 77 int fd = open(path, O_WRONLY); 78 size_t len = strlen(val); 79 int ret; 80 81 if (fd == -1) 82 ksft_exit_fail_msg("opening %s for write: %s\n", path, strerror(errno)); 83 84 ret = write(fd, val, len); 85 if (ret == -1) 86 ksft_exit_fail_msg("writing to %s: %s\n", path, strerror(errno)); 87 if (ret != len) 88 ksft_exit_fail_msg("short write to %s\n", path); 89 90 ret = close(fd); 91 if (ret == -1) 92 ksft_exit_fail_msg("closing %s\n", path); 93 } 94 95 static uint64_t get_mnt_id(const char *name, const char *path, uint64_t mask) 96 { 97 struct statx sx; 98 int ret; 99 100 ret = statx(AT_FDCWD, path, 0, mask, &sx); 101 if (ret == -1) 102 ksft_exit_fail_msg("retrieving %s mount ID for %s: %s\n", 103 mask & STATX_MNT_ID_UNIQUE ? "unique" : "old", 104 name, strerror(errno)); 105 if (!(sx.stx_mask & mask)) 106 ksft_exit_fail_msg("no %s mount ID available for %s\n", 107 mask & STATX_MNT_ID_UNIQUE ? "unique" : "old", 108 name); 109 110 return sx.stx_mnt_id; 111 } 112 113 114 static char root_mntpoint[] = "/tmp/statmount_test_root.XXXXXX"; 115 static int orig_root; 116 static uint64_t root_id, parent_id; 117 static uint32_t old_root_id, old_parent_id; 118 static FILE *f_mountinfo; 119 120 static void cleanup_namespace(void) 121 { 122 int ret; 123 124 ret = fchdir(orig_root); 125 if (ret == -1) 126 ksft_perror("fchdir to original root"); 127 128 ret = chroot("."); 129 if (ret == -1) 130 ksft_perror("chroot to original root"); 131 132 umount2(root_mntpoint, MNT_DETACH); 133 rmdir(root_mntpoint); 134 } 135 136 static void setup_namespace(void) 137 { 138 int ret; 139 char buf[32]; 140 uid_t uid = getuid(); 141 gid_t gid = getgid(); 142 143 ret = unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWPID); 144 if (ret == -1) 145 ksft_exit_fail_msg("unsharing mountns and userns: %s\n", 146 strerror(errno)); 147 148 sprintf(buf, "0 %d 1", uid); 149 write_file("/proc/self/uid_map", buf); 150 write_file("/proc/self/setgroups", "deny"); 151 sprintf(buf, "0 %d 1", gid); 152 write_file("/proc/self/gid_map", buf); 153 154 f_mountinfo = fopen("/proc/self/mountinfo", "re"); 155 if (!f_mountinfo) 156 ksft_exit_fail_msg("failed to open mountinfo: %s\n", 157 strerror(errno)); 158 159 ret = mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL); 160 if (ret == -1) 161 ksft_exit_fail_msg("making mount tree private: %s\n", 162 strerror(errno)); 163 164 if (!mkdtemp(root_mntpoint)) 165 ksft_exit_fail_msg("creating temporary directory %s: %s\n", 166 root_mntpoint, strerror(errno)); 167 168 old_parent_id = get_mnt_id("parent", root_mntpoint, STATX_MNT_ID); 169 parent_id = get_mnt_id("parent", root_mntpoint, STATX_MNT_ID_UNIQUE); 170 171 orig_root = open("/", O_PATH); 172 if (orig_root == -1) 173 ksft_exit_fail_msg("opening root directory: %s", 174 strerror(errno)); 175 176 atexit(cleanup_namespace); 177 178 ret = mount(root_mntpoint, root_mntpoint, NULL, MS_BIND, NULL); 179 if (ret == -1) 180 ksft_exit_fail_msg("mounting temp root %s: %s\n", 181 root_mntpoint, strerror(errno)); 182 183 ret = chroot(root_mntpoint); 184 if (ret == -1) 185 ksft_exit_fail_msg("chroot to temp root %s: %s\n", 186 root_mntpoint, strerror(errno)); 187 188 ret = chdir("/"); 189 if (ret == -1) 190 ksft_exit_fail_msg("chdir to root: %s\n", strerror(errno)); 191 192 old_root_id = get_mnt_id("root", "/", STATX_MNT_ID); 193 root_id = get_mnt_id("root", "/", STATX_MNT_ID_UNIQUE); 194 } 195 196 static int setup_mount_tree(int log2_num) 197 { 198 int ret, i; 199 200 ret = mount("", "/", NULL, MS_REC|MS_SHARED, NULL); 201 if (ret == -1) { 202 ksft_test_result_fail("making mount tree shared: %s\n", 203 strerror(errno)); 204 return -1; 205 } 206 207 for (i = 0; i < log2_num; i++) { 208 ret = mount("/", "/", NULL, MS_BIND, NULL); 209 if (ret == -1) { 210 ksft_test_result_fail("mounting submount %s: %s\n", 211 root_mntpoint, strerror(errno)); 212 return -1; 213 } 214 } 215 return 0; 216 } 217 218 static void test_listmount_empty_root(void) 219 { 220 ssize_t res; 221 const unsigned int size = 32; 222 uint64_t list[size]; 223 224 res = listmount(LSMT_ROOT, 0, 0, list, size, 0); 225 if (res == -1) { 226 ksft_test_result_fail("listmount: %s\n", strerror(errno)); 227 return; 228 } 229 if (res != 1) { 230 ksft_test_result_fail("listmount result is %zi != 1\n", res); 231 return; 232 } 233 234 if (list[0] != root_id) { 235 ksft_test_result_fail("listmount ID doesn't match 0x%llx != 0x%llx\n", 236 (unsigned long long) list[0], 237 (unsigned long long) root_id); 238 return; 239 } 240 241 ksft_test_result_pass("listmount empty root\n"); 242 } 243 244 static void test_statmount_zero_mask(void) 245 { 246 struct statmount sm; 247 int ret; 248 249 ret = statmount(root_id, 0, 0, 0, &sm, sizeof(sm), 0); 250 if (ret == -1) { 251 ksft_test_result_fail("statmount zero mask: %s\n", 252 strerror(errno)); 253 return; 254 } 255 if (sm.size != sizeof(sm)) { 256 ksft_test_result_fail("unexpected size: %u != %u\n", 257 sm.size, (uint32_t) sizeof(sm)); 258 return; 259 } 260 if (sm.mask != 0) { 261 ksft_test_result_fail("unexpected mask: 0x%llx != 0x0\n", 262 (unsigned long long) sm.mask); 263 return; 264 } 265 266 ksft_test_result_pass("statmount zero mask\n"); 267 } 268 269 static void test_statmount_mnt_basic(void) 270 { 271 struct statmount sm; 272 int ret; 273 uint64_t mask = STATMOUNT_MNT_BASIC; 274 275 ret = statmount(root_id, 0, 0, mask, &sm, sizeof(sm), 0); 276 if (ret == -1) { 277 ksft_test_result_fail("statmount mnt basic: %s\n", 278 strerror(errno)); 279 return; 280 } 281 if (sm.size != sizeof(sm)) { 282 ksft_test_result_fail("unexpected size: %u != %u\n", 283 sm.size, (uint32_t) sizeof(sm)); 284 return; 285 } 286 if (sm.mask != mask) { 287 ksft_test_result_skip("statmount mnt basic unavailable\n"); 288 return; 289 } 290 291 if (sm.mnt_id != root_id) { 292 ksft_test_result_fail("unexpected root ID: 0x%llx != 0x%llx\n", 293 (unsigned long long) sm.mnt_id, 294 (unsigned long long) root_id); 295 return; 296 } 297 298 if (sm.mnt_id_old != old_root_id) { 299 ksft_test_result_fail("unexpected old root ID: %u != %u\n", 300 sm.mnt_id_old, old_root_id); 301 return; 302 } 303 304 if (sm.mnt_parent_id != parent_id) { 305 ksft_test_result_fail("unexpected parent ID: 0x%llx != 0x%llx\n", 306 (unsigned long long) sm.mnt_parent_id, 307 (unsigned long long) parent_id); 308 return; 309 } 310 311 if (sm.mnt_parent_id_old != old_parent_id) { 312 ksft_test_result_fail("unexpected old parent ID: %u != %u\n", 313 sm.mnt_parent_id_old, old_parent_id); 314 return; 315 } 316 317 if (sm.mnt_propagation != MS_PRIVATE) { 318 ksft_test_result_fail("unexpected propagation: 0x%llx\n", 319 (unsigned long long) sm.mnt_propagation); 320 return; 321 } 322 323 ksft_test_result_pass("statmount mnt basic\n"); 324 } 325 326 327 static void test_statmount_sb_basic(void) 328 { 329 struct statmount sm; 330 int ret; 331 uint64_t mask = STATMOUNT_SB_BASIC; 332 struct statx sx; 333 struct statfs sf; 334 335 ret = statmount(root_id, 0, 0, mask, &sm, sizeof(sm), 0); 336 if (ret == -1) { 337 ksft_test_result_fail("statmount sb basic: %s\n", 338 strerror(errno)); 339 return; 340 } 341 if (sm.size != sizeof(sm)) { 342 ksft_test_result_fail("unexpected size: %u != %u\n", 343 sm.size, (uint32_t) sizeof(sm)); 344 return; 345 } 346 if (sm.mask != mask) { 347 ksft_test_result_skip("statmount sb basic unavailable\n"); 348 return; 349 } 350 351 ret = statx(AT_FDCWD, "/", 0, 0, &sx); 352 if (ret == -1) { 353 ksft_test_result_fail("stat root failed: %s\n", 354 strerror(errno)); 355 return; 356 } 357 358 if (sm.sb_dev_major != sx.stx_dev_major || 359 sm.sb_dev_minor != sx.stx_dev_minor) { 360 ksft_test_result_fail("unexpected sb dev %u:%u != %u:%u\n", 361 sm.sb_dev_major, sm.sb_dev_minor, 362 sx.stx_dev_major, sx.stx_dev_minor); 363 return; 364 } 365 366 ret = statfs("/", &sf); 367 if (ret == -1) { 368 ksft_test_result_fail("statfs root failed: %s\n", 369 strerror(errno)); 370 return; 371 } 372 373 if (sm.sb_magic != sf.f_type) { 374 ksft_test_result_fail("unexpected sb magic: 0x%llx != 0x%lx\n", 375 (unsigned long long) sm.sb_magic, 376 sf.f_type); 377 return; 378 } 379 380 ksft_test_result_pass("statmount sb basic\n"); 381 } 382 383 static void test_statmount_mnt_point(void) 384 { 385 struct statmount *sm; 386 387 sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_POINT, 0); 388 if (!sm) { 389 ksft_test_result_fail("statmount mount point: %s\n", 390 strerror(errno)); 391 return; 392 } 393 394 if (!(sm->mask & STATMOUNT_MNT_POINT)) { 395 ksft_test_result_fail("missing STATMOUNT_MNT_POINT in mask\n"); 396 return; 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, 0, STATMOUNT_MNT_ROOT, 0); 418 if (!sm) { 419 ksft_test_result_fail("statmount mount root: %s\n", 420 strerror(errno)); 421 return; 422 } 423 if (!(sm->mask & STATMOUNT_MNT_ROOT)) { 424 ksft_test_result_fail("missing STATMOUNT_MNT_ROOT in mask\n"); 425 return; 426 } 427 mnt_root = sm->str + sm->mnt_root; 428 last_root = strrchr(mnt_root, '/'); 429 if (last_root) 430 last_root++; 431 else 432 last_root = mnt_root; 433 434 if (strcmp(last_dir, last_root) != 0) { 435 ksft_test_result_fail("unexpected mount root last component: '%s' != '%s'\n", 436 last_root, last_dir); 437 goto out; 438 } 439 ksft_test_result_pass("statmount mount root\n"); 440 out: 441 free(sm); 442 } 443 444 static void test_statmount_fs_type(void) 445 { 446 struct statmount *sm; 447 const char *fs_type; 448 const char *const *s; 449 450 sm = statmount_alloc(root_id, 0, STATMOUNT_FS_TYPE, 0); 451 if (!sm) { 452 ksft_test_result_fail("statmount fs type: %s\n", 453 strerror(errno)); 454 return; 455 } 456 if (!(sm->mask & STATMOUNT_FS_TYPE)) { 457 ksft_test_result_fail("missing STATMOUNT_FS_TYPE in mask\n"); 458 return; 459 } 460 fs_type = sm->str + sm->fs_type; 461 for (s = known_fs; s != NULL; s++) { 462 if (strcmp(fs_type, *s) == 0) 463 break; 464 } 465 if (!s) 466 ksft_print_msg("unknown filesystem type: %s\n", fs_type); 467 468 ksft_test_result_pass("statmount fs type\n"); 469 free(sm); 470 } 471 472 static void test_statmount_mnt_opts(void) 473 { 474 struct statmount *sm; 475 const char *statmount_opts; 476 char *line = NULL; 477 size_t len = 0; 478 479 sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_BASIC | STATMOUNT_MNT_OPTS, 480 0); 481 if (!sm) { 482 ksft_test_result_fail("statmount mnt opts: %s\n", 483 strerror(errno)); 484 return; 485 } 486 487 if (!(sm->mask & STATMOUNT_MNT_BASIC)) { 488 ksft_test_result_fail("missing STATMOUNT_MNT_BASIC in mask\n"); 489 return; 490 } 491 492 while (getline(&line, &len, f_mountinfo) != -1) { 493 int i; 494 char *p, *p2; 495 unsigned int old_mnt_id; 496 497 old_mnt_id = atoi(line); 498 if (old_mnt_id != sm->mnt_id_old) 499 continue; 500 501 for (p = line, i = 0; p && i < 5; i++) 502 p = strchr(p + 1, ' '); 503 if (!p) 504 continue; 505 506 p2 = strchr(p + 1, ' '); 507 if (!p2) 508 continue; 509 *p2 = '\0'; 510 p = strchr(p2 + 1, '-'); 511 if (!p) 512 continue; 513 for (p++, i = 0; p && i < 2; i++) 514 p = strchr(p + 1, ' '); 515 if (!p) 516 continue; 517 p++; 518 519 /* skip generic superblock options */ 520 if (strncmp(p, "ro", 2) == 0) 521 p += 2; 522 else if (strncmp(p, "rw", 2) == 0) 523 p += 2; 524 if (*p == ',') 525 p++; 526 if (strncmp(p, "sync", 4) == 0) 527 p += 4; 528 if (*p == ',') 529 p++; 530 if (strncmp(p, "dirsync", 7) == 0) 531 p += 7; 532 if (*p == ',') 533 p++; 534 if (strncmp(p, "lazytime", 8) == 0) 535 p += 8; 536 if (*p == ',') 537 p++; 538 p2 = strrchr(p, '\n'); 539 if (p2) 540 *p2 = '\0'; 541 542 if (sm->mask & STATMOUNT_MNT_OPTS) 543 statmount_opts = sm->str + sm->mnt_opts; 544 else 545 statmount_opts = ""; 546 if (strcmp(statmount_opts, p) != 0) 547 ksft_test_result_fail( 548 "unexpected mount options: '%s' != '%s'\n", 549 statmount_opts, p); 550 else 551 ksft_test_result_pass("statmount mount options\n"); 552 free(sm); 553 free(line); 554 return; 555 } 556 557 ksft_test_result_fail("didnt't find mount entry\n"); 558 free(sm); 559 free(line); 560 } 561 562 static void test_statmount_string(uint64_t mask, size_t off, const char *name) 563 { 564 struct statmount *sm; 565 size_t len, shortsize, exactsize; 566 uint32_t start, i; 567 int ret; 568 569 sm = statmount_alloc(root_id, 0, mask, 0); 570 if (!sm) { 571 ksft_test_result_fail("statmount %s: %s\n", name, 572 strerror(errno)); 573 goto out; 574 } 575 if (sm->size < sizeof(*sm)) { 576 ksft_test_result_fail("unexpected size: %u < %u\n", 577 sm->size, (uint32_t) sizeof(*sm)); 578 goto out; 579 } 580 if (sm->mask != mask) { 581 ksft_test_result_skip("statmount %s unavailable\n", name); 582 goto out; 583 } 584 len = sm->size - sizeof(*sm); 585 start = ((uint32_t *) sm)[off]; 586 587 for (i = start;; i++) { 588 if (i >= len) { 589 ksft_test_result_fail("string out of bounds\n"); 590 goto out; 591 } 592 if (!sm->str[i]) 593 break; 594 } 595 exactsize = sm->size; 596 shortsize = sizeof(*sm) + i; 597 598 ret = statmount(root_id, 0, 0, mask, sm, exactsize, 0); 599 if (ret == -1) { 600 ksft_test_result_fail("statmount exact size: %s\n", 601 strerror(errno)); 602 goto out; 603 } 604 errno = 0; 605 ret = statmount(root_id, 0, 0, mask, sm, shortsize, 0); 606 if (ret != -1 || errno != EOVERFLOW) { 607 ksft_test_result_fail("should have failed with EOVERFLOW: %s\n", 608 strerror(errno)); 609 goto out; 610 } 611 612 ksft_test_result_pass("statmount string %s\n", name); 613 out: 614 free(sm); 615 } 616 617 static void test_listmount_tree(void) 618 { 619 ssize_t res; 620 const unsigned int log2_num = 4; 621 const unsigned int step = 3; 622 const unsigned int size = (1 << log2_num) + step + 1; 623 size_t num, expect = 1 << log2_num; 624 uint64_t list[size]; 625 uint64_t list2[size]; 626 size_t i; 627 628 629 res = setup_mount_tree(log2_num); 630 if (res == -1) 631 return; 632 633 num = res = listmount(LSMT_ROOT, 0, 0, list, size, 0); 634 if (res == -1) { 635 ksft_test_result_fail("listmount: %s\n", strerror(errno)); 636 return; 637 } 638 if (num != expect) { 639 ksft_test_result_fail("listmount result is %zi != %zi\n", 640 res, expect); 641 return; 642 } 643 644 for (i = 0; i < size - step;) { 645 res = listmount(LSMT_ROOT, 0, i ? list2[i - 1] : 0, list2 + i, step, 0); 646 if (res == -1) 647 ksft_test_result_fail("short listmount: %s\n", 648 strerror(errno)); 649 i += res; 650 if (res < step) 651 break; 652 } 653 if (i != num) { 654 ksft_test_result_fail("different number of entries: %zu != %zu\n", 655 i, num); 656 return; 657 } 658 for (i = 0; i < num; i++) { 659 if (list2[i] != list[i]) { 660 ksft_test_result_fail("different value for entry %zu: 0x%llx != 0x%llx\n", 661 i, 662 (unsigned long long) list2[i], 663 (unsigned long long) list[i]); 664 } 665 } 666 667 ksft_test_result_pass("listmount tree\n"); 668 } 669 670 static void test_statmount_by_fd(void) 671 { 672 struct statmount *sm = NULL; 673 char tmpdir[] = "/statmount.fd.XXXXXX"; 674 const char root[] = "/test"; 675 char subdir[PATH_MAX], tmproot[PATH_MAX]; 676 int fd; 677 678 if (!mkdtemp(tmpdir)) { 679 ksft_perror("mkdtemp"); 680 return; 681 } 682 683 if (mount("statmount.test", tmpdir, "tmpfs", 0, NULL)) { 684 ksft_perror("mount"); 685 rmdir(tmpdir); 686 return; 687 } 688 689 snprintf(subdir, PATH_MAX, "%s%s", tmpdir, root); 690 snprintf(tmproot, PATH_MAX, "%s/%s", tmpdir, "chroot"); 691 692 if (mkdir(subdir, 0755)) { 693 ksft_perror("mkdir"); 694 goto err_tmpdir; 695 } 696 697 if (mount(subdir, subdir, NULL, MS_BIND, 0)) { 698 ksft_perror("mount"); 699 goto err_subdir; 700 } 701 702 if (mkdir(tmproot, 0755)) { 703 ksft_perror("mkdir"); 704 goto err_subdir; 705 } 706 707 fd = open(subdir, O_PATH); 708 if (fd < 0) { 709 ksft_perror("open"); 710 goto err_tmproot; 711 } 712 713 if (chroot(tmproot)) { 714 ksft_perror("chroot"); 715 goto err_fd; 716 } 717 718 sm = statmount_alloc(0, fd, STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT, STATMOUNT_BY_FD); 719 if (!sm) { 720 ksft_test_result_fail("statmount by fd failed: %s\n", strerror(errno)); 721 goto err_chroot; 722 } 723 724 if (sm->size < sizeof(*sm)) { 725 ksft_test_result_fail("unexpected size: %u < %u\n", 726 sm->size, (uint32_t) sizeof(*sm)); 727 goto err_chroot; 728 } 729 730 if (sm->mask & STATMOUNT_MNT_POINT) { 731 ksft_test_result_fail("STATMOUNT_MNT_POINT unexpectedly set in statmount\n"); 732 goto err_chroot; 733 } 734 735 if (!(sm->mask & STATMOUNT_MNT_ROOT)) { 736 ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in statmount\n"); 737 goto err_chroot; 738 } 739 740 if (strcmp(root, sm->str + sm->mnt_root) != 0) { 741 ksft_test_result_fail("statmount returned incorrect mnt_root," 742 "statmount mnt_root: %s != %s\n", 743 sm->str + sm->mnt_root, root); 744 goto err_chroot; 745 } 746 747 if (chroot(".")) { 748 ksft_perror("chroot"); 749 goto out; 750 } 751 752 free(sm); 753 sm = statmount_alloc(0, fd, STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT, STATMOUNT_BY_FD); 754 if (!sm) { 755 ksft_test_result_fail("statmount by fd failed: %s\n", strerror(errno)); 756 goto err_fd; 757 } 758 759 if (sm->size < sizeof(*sm)) { 760 ksft_test_result_fail("unexpected size: %u < %u\n", 761 sm->size, (uint32_t) sizeof(*sm)); 762 goto out; 763 } 764 765 if (!(sm->mask & STATMOUNT_MNT_POINT)) { 766 ksft_test_result_fail("STATMOUNT_MNT_POINT not set in statmount\n"); 767 goto out; 768 } 769 770 if (!(sm->mask & STATMOUNT_MNT_ROOT)) { 771 ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in statmount\n"); 772 goto out; 773 } 774 775 if (strcmp(subdir, sm->str + sm->mnt_point) != 0) { 776 ksft_test_result_fail("statmount returned incorrect mnt_point," 777 "statmount mnt_point: %s != %s\n", sm->str + sm->mnt_point, subdir); 778 goto out; 779 } 780 781 if (strcmp(root, sm->str + sm->mnt_root) != 0) { 782 ksft_test_result_fail("statmount returned incorrect mnt_root," 783 "statmount mnt_root: %s != %s\n", sm->str + sm->mnt_root, root); 784 goto out; 785 } 786 787 ksft_test_result_pass("statmount by fd\n"); 788 goto out; 789 err_chroot: 790 chroot("."); 791 out: 792 free(sm); 793 err_fd: 794 close(fd); 795 err_tmproot: 796 rmdir(tmproot); 797 err_subdir: 798 umount2(subdir, MNT_DETACH); 799 rmdir(subdir); 800 err_tmpdir: 801 umount2(tmpdir, MNT_DETACH); 802 rmdir(tmpdir); 803 } 804 805 static void test_statmount_by_fd_unmounted(void) 806 { 807 const char root[] = "/test.unmounted"; 808 char tmpdir[] = "/statmount.fd.XXXXXX"; 809 char subdir[PATH_MAX]; 810 int fd; 811 struct statmount *sm = NULL; 812 813 if (!mkdtemp(tmpdir)) { 814 ksft_perror("mkdtemp"); 815 return; 816 } 817 818 if (mount("statmount.test", tmpdir, "tmpfs", 0, NULL)) { 819 ksft_perror("mount"); 820 rmdir(tmpdir); 821 return; 822 } 823 824 snprintf(subdir, PATH_MAX, "%s%s", tmpdir, root); 825 826 if (mkdir(subdir, 0755)) { 827 ksft_perror("mkdir"); 828 goto err_tmpdir; 829 } 830 831 if (mount(subdir, subdir, 0, MS_BIND, NULL)) { 832 ksft_perror("mount"); 833 goto err_subdir; 834 } 835 836 fd = open(subdir, O_PATH); 837 if (fd < 0) { 838 ksft_perror("open"); 839 goto err_subdir; 840 } 841 842 if (umount2(tmpdir, MNT_DETACH)) { 843 ksft_perror("umount2"); 844 goto err_fd; 845 } 846 847 sm = statmount_alloc(0, fd, STATMOUNT_MNT_POINT | STATMOUNT_MNT_ROOT, STATMOUNT_BY_FD); 848 if (!sm) { 849 ksft_test_result_fail("statmount by fd unmounted: %s\n", 850 strerror(errno)); 851 goto err_sm; 852 } 853 854 if (sm->size < sizeof(*sm)) { 855 ksft_test_result_fail("unexpected size: %u < %u\n", 856 sm->size, (uint32_t) sizeof(*sm)); 857 goto err_sm; 858 } 859 860 if (sm->mask & STATMOUNT_MNT_POINT) { 861 ksft_test_result_fail("STATMOUNT_MNT_POINT unexpectedly set in mask\n"); 862 goto err_sm; 863 } 864 865 if (!(sm->mask & STATMOUNT_MNT_ROOT)) { 866 ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in mask\n"); 867 goto err_sm; 868 } 869 870 if (strcmp(sm->str + sm->mnt_root, root) != 0) { 871 ksft_test_result_fail("statmount returned incorrect mnt_root," 872 "statmount mnt_root: %s != %s\n", 873 sm->str + sm->mnt_root, root); 874 goto err_sm; 875 } 876 877 ksft_test_result_pass("statmount by fd on unmounted mount\n"); 878 err_sm: 879 free(sm); 880 err_fd: 881 close(fd); 882 err_subdir: 883 umount2(subdir, MNT_DETACH); 884 rmdir(subdir); 885 err_tmpdir: 886 umount2(tmpdir, MNT_DETACH); 887 rmdir(tmpdir); 888 } 889 890 #define str_off(memb) (offsetof(struct statmount, memb) / sizeof(uint32_t)) 891 892 int main(void) 893 { 894 int ret; 895 uint64_t all_mask = STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC | 896 STATMOUNT_PROPAGATE_FROM | STATMOUNT_MNT_ROOT | 897 STATMOUNT_MNT_POINT | STATMOUNT_FS_TYPE | STATMOUNT_MNT_NS_ID; 898 899 ksft_print_header(); 900 901 ret = statmount(0, 0, 0, 0, NULL, 0, 0); 902 assert(ret == -1); 903 if (errno == ENOSYS) 904 ksft_exit_skip("statmount() syscall not supported\n"); 905 906 setup_namespace(); 907 908 ksft_set_plan(17); 909 test_listmount_empty_root(); 910 test_statmount_zero_mask(); 911 test_statmount_mnt_basic(); 912 test_statmount_sb_basic(); 913 test_statmount_mnt_root(); 914 test_statmount_mnt_point(); 915 test_statmount_fs_type(); 916 test_statmount_mnt_opts(); 917 test_statmount_string(STATMOUNT_MNT_ROOT, str_off(mnt_root), "mount root"); 918 test_statmount_string(STATMOUNT_MNT_POINT, str_off(mnt_point), "mount point"); 919 test_statmount_string(STATMOUNT_FS_TYPE, str_off(fs_type), "fs type"); 920 test_statmount_string(all_mask, str_off(mnt_root), "mount root & all"); 921 test_statmount_string(all_mask, str_off(mnt_point), "mount point & all"); 922 test_statmount_string(all_mask, str_off(fs_type), "fs type & all"); 923 924 test_listmount_tree(); 925 test_statmount_by_fd_unmounted(); 926 test_statmount_by_fd(); 927 928 929 if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0) 930 ksft_exit_fail(); 931 else 932 ksft_exit_pass(); 933 } 934