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