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