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