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