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 fchdir(orig_root); 129 chroot("."); 130 umount2(root_mntpoint, MNT_DETACH); 131 rmdir(root_mntpoint); 132 } 133 134 static void setup_namespace(void) 135 { 136 int ret; 137 char buf[32]; 138 uid_t uid = getuid(); 139 gid_t gid = getgid(); 140 141 ret = unshare(CLONE_NEWNS|CLONE_NEWUSER); 142 if (ret == -1) 143 ksft_exit_fail_msg("unsharing mountns and userns: %s\n", 144 strerror(errno)); 145 146 sprintf(buf, "0 %d 1", uid); 147 write_file("/proc/self/uid_map", buf); 148 write_file("/proc/self/setgroups", "deny"); 149 sprintf(buf, "0 %d 1", gid); 150 write_file("/proc/self/gid_map", buf); 151 152 ret = mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL); 153 if (ret == -1) 154 ksft_exit_fail_msg("making mount tree private: %s\n", 155 strerror(errno)); 156 157 if (!mkdtemp(root_mntpoint)) 158 ksft_exit_fail_msg("creating temporary directory %s: %s\n", 159 root_mntpoint, strerror(errno)); 160 161 old_parent_id = get_mnt_id("parent", root_mntpoint, STATX_MNT_ID); 162 parent_id = get_mnt_id("parent", root_mntpoint, STATX_MNT_ID_UNIQUE); 163 164 orig_root = open("/", O_PATH); 165 if (orig_root == -1) 166 ksft_exit_fail_msg("opening root directory: %s", 167 strerror(errno)); 168 169 atexit(cleanup_namespace); 170 171 ret = mount(root_mntpoint, root_mntpoint, NULL, MS_BIND, NULL); 172 if (ret == -1) 173 ksft_exit_fail_msg("mounting temp root %s: %s\n", 174 root_mntpoint, strerror(errno)); 175 176 ret = chroot(root_mntpoint); 177 if (ret == -1) 178 ksft_exit_fail_msg("chroot to temp root %s: %s\n", 179 root_mntpoint, strerror(errno)); 180 181 ret = chdir("/"); 182 if (ret == -1) 183 ksft_exit_fail_msg("chdir to root: %s\n", strerror(errno)); 184 185 old_root_id = get_mnt_id("root", "/", STATX_MNT_ID); 186 root_id = get_mnt_id("root", "/", STATX_MNT_ID_UNIQUE); 187 } 188 189 static int setup_mount_tree(int log2_num) 190 { 191 int ret, i; 192 193 ret = mount("", "/", NULL, MS_REC|MS_SHARED, NULL); 194 if (ret == -1) { 195 ksft_test_result_fail("making mount tree shared: %s\n", 196 strerror(errno)); 197 return -1; 198 } 199 200 for (i = 0; i < log2_num; i++) { 201 ret = mount("/", "/", NULL, MS_BIND, NULL); 202 if (ret == -1) { 203 ksft_test_result_fail("mounting submount %s: %s\n", 204 root_mntpoint, strerror(errno)); 205 return -1; 206 } 207 } 208 return 0; 209 } 210 211 static ssize_t listmount(uint64_t mnt_id, uint64_t last_mnt_id, 212 uint64_t list[], size_t num, unsigned int flags) 213 { 214 struct mnt_id_req req = { 215 .size = MNT_ID_REQ_SIZE_VER0, 216 .mnt_id = mnt_id, 217 .param = last_mnt_id, 218 }; 219 220 return syscall(__NR_listmount, &req, list, num, flags); 221 } 222 223 static void test_listmount_empty_root(void) 224 { 225 ssize_t res; 226 const unsigned int size = 32; 227 uint64_t list[size]; 228 229 res = listmount(LSMT_ROOT, 0, list, size, 0); 230 if (res == -1) { 231 ksft_test_result_fail("listmount: %s\n", strerror(errno)); 232 return; 233 } 234 if (res != 1) { 235 ksft_test_result_fail("listmount result is %zi != 1\n", res); 236 return; 237 } 238 239 if (list[0] != root_id) { 240 ksft_test_result_fail("listmount ID doesn't match 0x%llx != 0x%llx\n", 241 (unsigned long long) list[0], 242 (unsigned long long) root_id); 243 return; 244 } 245 246 ksft_test_result_pass("listmount empty root\n"); 247 } 248 249 static void test_statmount_zero_mask(void) 250 { 251 struct statmount sm; 252 int ret; 253 254 ret = statmount(root_id, 0, &sm, sizeof(sm), 0); 255 if (ret == -1) { 256 ksft_test_result_fail("statmount zero mask: %s\n", 257 strerror(errno)); 258 return; 259 } 260 if (sm.size != sizeof(sm)) { 261 ksft_test_result_fail("unexpected size: %u != %u\n", 262 sm.size, (uint32_t) sizeof(sm)); 263 return; 264 } 265 if (sm.mask != 0) { 266 ksft_test_result_fail("unexpected mask: 0x%llx != 0x0\n", 267 (unsigned long long) sm.mask); 268 return; 269 } 270 271 ksft_test_result_pass("statmount zero mask\n"); 272 } 273 274 static void test_statmount_mnt_basic(void) 275 { 276 struct statmount sm; 277 int ret; 278 uint64_t mask = STATMOUNT_MNT_BASIC; 279 280 ret = statmount(root_id, mask, &sm, sizeof(sm), 0); 281 if (ret == -1) { 282 ksft_test_result_fail("statmount mnt basic: %s\n", 283 strerror(errno)); 284 return; 285 } 286 if (sm.size != sizeof(sm)) { 287 ksft_test_result_fail("unexpected size: %u != %u\n", 288 sm.size, (uint32_t) sizeof(sm)); 289 return; 290 } 291 if (sm.mask != mask) { 292 ksft_test_result_skip("statmount mnt basic unavailable\n"); 293 return; 294 } 295 296 if (sm.mnt_id != root_id) { 297 ksft_test_result_fail("unexpected root ID: 0x%llx != 0x%llx\n", 298 (unsigned long long) sm.mnt_id, 299 (unsigned long long) root_id); 300 return; 301 } 302 303 if (sm.mnt_id_old != old_root_id) { 304 ksft_test_result_fail("unexpected old root ID: %u != %u\n", 305 sm.mnt_id_old, old_root_id); 306 return; 307 } 308 309 if (sm.mnt_parent_id != parent_id) { 310 ksft_test_result_fail("unexpected parent ID: 0x%llx != 0x%llx\n", 311 (unsigned long long) sm.mnt_parent_id, 312 (unsigned long long) parent_id); 313 return; 314 } 315 316 if (sm.mnt_parent_id_old != old_parent_id) { 317 ksft_test_result_fail("unexpected old parent ID: %u != %u\n", 318 sm.mnt_parent_id_old, old_parent_id); 319 return; 320 } 321 322 if (sm.mnt_propagation != MS_PRIVATE) { 323 ksft_test_result_fail("unexpected propagation: 0x%llx\n", 324 (unsigned long long) sm.mnt_propagation); 325 return; 326 } 327 328 ksft_test_result_pass("statmount mnt basic\n"); 329 } 330 331 332 static void test_statmount_sb_basic(void) 333 { 334 struct statmount sm; 335 int ret; 336 uint64_t mask = STATMOUNT_SB_BASIC; 337 struct statx sx; 338 struct statfs sf; 339 340 ret = statmount(root_id, mask, &sm, sizeof(sm), 0); 341 if (ret == -1) { 342 ksft_test_result_fail("statmount sb basic: %s\n", 343 strerror(errno)); 344 return; 345 } 346 if (sm.size != sizeof(sm)) { 347 ksft_test_result_fail("unexpected size: %u != %u\n", 348 sm.size, (uint32_t) sizeof(sm)); 349 return; 350 } 351 if (sm.mask != mask) { 352 ksft_test_result_skip("statmount sb basic unavailable\n"); 353 return; 354 } 355 356 ret = statx(AT_FDCWD, "/", 0, 0, &sx); 357 if (ret == -1) { 358 ksft_test_result_fail("stat root failed: %s\n", 359 strerror(errno)); 360 return; 361 } 362 363 if (sm.sb_dev_major != sx.stx_dev_major || 364 sm.sb_dev_minor != sx.stx_dev_minor) { 365 ksft_test_result_fail("unexpected sb dev %u:%u != %u:%u\n", 366 sm.sb_dev_major, sm.sb_dev_minor, 367 sx.stx_dev_major, sx.stx_dev_minor); 368 return; 369 } 370 371 ret = statfs("/", &sf); 372 if (ret == -1) { 373 ksft_test_result_fail("statfs root failed: %s\n", 374 strerror(errno)); 375 return; 376 } 377 378 if (sm.sb_magic != sf.f_type) { 379 ksft_test_result_fail("unexpected sb magic: 0x%llx != 0x%lx\n", 380 (unsigned long long) sm.sb_magic, 381 sf.f_type); 382 return; 383 } 384 385 ksft_test_result_pass("statmount sb basic\n"); 386 } 387 388 static void test_statmount_mnt_point(void) 389 { 390 struct statmount *sm; 391 392 sm = statmount_alloc(root_id, STATMOUNT_MNT_POINT, 0); 393 if (!sm) { 394 ksft_test_result_fail("statmount mount point: %s\n", 395 strerror(errno)); 396 return; 397 } 398 399 if (strcmp(sm->str + sm->mnt_point, "/") != 0) { 400 ksft_test_result_fail("unexpected mount point: '%s' != '/'\n", 401 sm->str + sm->mnt_point); 402 goto out; 403 } 404 ksft_test_result_pass("statmount mount point\n"); 405 out: 406 free(sm); 407 } 408 409 static void test_statmount_mnt_root(void) 410 { 411 struct statmount *sm; 412 const char *mnt_root, *last_dir, *last_root; 413 414 last_dir = strrchr(root_mntpoint, '/'); 415 assert(last_dir); 416 last_dir++; 417 418 sm = statmount_alloc(root_id, STATMOUNT_MNT_ROOT, 0); 419 if (!sm) { 420 ksft_test_result_fail("statmount mount root: %s\n", 421 strerror(errno)); 422 return; 423 } 424 mnt_root = sm->str + sm->mnt_root; 425 last_root = strrchr(mnt_root, '/'); 426 if (last_root) 427 last_root++; 428 else 429 last_root = mnt_root; 430 431 if (strcmp(last_dir, last_root) != 0) { 432 ksft_test_result_fail("unexpected mount root last component: '%s' != '%s'\n", 433 last_root, last_dir); 434 goto out; 435 } 436 ksft_test_result_pass("statmount mount root\n"); 437 out: 438 free(sm); 439 } 440 441 static void test_statmount_fs_type(void) 442 { 443 struct statmount *sm; 444 const char *fs_type; 445 const char *const *s; 446 447 sm = statmount_alloc(root_id, STATMOUNT_FS_TYPE, 0); 448 if (!sm) { 449 ksft_test_result_fail("statmount fs type: %s\n", 450 strerror(errno)); 451 return; 452 } 453 fs_type = sm->str + sm->fs_type; 454 for (s = known_fs; s != NULL; s++) { 455 if (strcmp(fs_type, *s) == 0) 456 break; 457 } 458 if (!s) 459 ksft_print_msg("unknown filesystem type: %s\n", fs_type); 460 461 ksft_test_result_pass("statmount fs type\n"); 462 free(sm); 463 } 464 465 static void test_statmount_string(uint64_t mask, size_t off, const char *name) 466 { 467 struct statmount *sm; 468 size_t len, shortsize, exactsize; 469 uint32_t start, i; 470 int ret; 471 472 sm = statmount_alloc(root_id, mask, 0); 473 if (!sm) { 474 ksft_test_result_fail("statmount %s: %s\n", name, 475 strerror(errno)); 476 goto out; 477 } 478 if (sm->size < sizeof(*sm)) { 479 ksft_test_result_fail("unexpected size: %u < %u\n", 480 sm->size, (uint32_t) sizeof(*sm)); 481 goto out; 482 } 483 if (sm->mask != mask) { 484 ksft_test_result_skip("statmount %s unavailable\n", name); 485 goto out; 486 } 487 len = sm->size - sizeof(*sm); 488 start = ((uint32_t *) sm)[off]; 489 490 for (i = start;; i++) { 491 if (i >= len) { 492 ksft_test_result_fail("string out of bounds\n"); 493 goto out; 494 } 495 if (!sm->str[i]) 496 break; 497 } 498 exactsize = sm->size; 499 shortsize = sizeof(*sm) + i; 500 501 ret = statmount(root_id, mask, sm, exactsize, 0); 502 if (ret == -1) { 503 ksft_test_result_fail("statmount exact size: %s\n", 504 strerror(errno)); 505 goto out; 506 } 507 errno = 0; 508 ret = statmount(root_id, mask, sm, shortsize, 0); 509 if (ret != -1 || errno != EOVERFLOW) { 510 ksft_test_result_fail("should have failed with EOVERFLOW: %s\n", 511 strerror(errno)); 512 goto out; 513 } 514 515 ksft_test_result_pass("statmount string %s\n", name); 516 out: 517 free(sm); 518 } 519 520 static void test_listmount_tree(void) 521 { 522 ssize_t res; 523 const unsigned int log2_num = 4; 524 const unsigned int step = 3; 525 const unsigned int size = (1 << log2_num) + step + 1; 526 size_t num, expect = 1 << log2_num; 527 uint64_t list[size]; 528 uint64_t list2[size]; 529 size_t i; 530 531 532 res = setup_mount_tree(log2_num); 533 if (res == -1) 534 return; 535 536 num = res = listmount(LSMT_ROOT, 0, list, size, 0); 537 if (res == -1) { 538 ksft_test_result_fail("listmount: %s\n", strerror(errno)); 539 return; 540 } 541 if (num != expect) { 542 ksft_test_result_fail("listmount result is %zi != %zi\n", 543 res, expect); 544 return; 545 } 546 547 for (i = 0; i < size - step;) { 548 res = listmount(LSMT_ROOT, i ? list2[i - 1] : 0, list2 + i, step, 0); 549 if (res == -1) 550 ksft_test_result_fail("short listmount: %s\n", 551 strerror(errno)); 552 i += res; 553 if (res < step) 554 break; 555 } 556 if (i != num) { 557 ksft_test_result_fail("different number of entries: %zu != %zu\n", 558 i, num); 559 return; 560 } 561 for (i = 0; i < num; i++) { 562 if (list2[i] != list[i]) { 563 ksft_test_result_fail("different value for entry %zu: 0x%llx != 0x%llx\n", 564 i, 565 (unsigned long long) list2[i], 566 (unsigned long long) list[i]); 567 } 568 } 569 570 ksft_test_result_pass("listmount tree\n"); 571 } 572 573 #define str_off(memb) (offsetof(struct statmount, memb) / sizeof(uint32_t)) 574 575 int main(void) 576 { 577 int ret; 578 uint64_t all_mask = STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC | 579 STATMOUNT_PROPAGATE_FROM | STATMOUNT_MNT_ROOT | 580 STATMOUNT_MNT_POINT | STATMOUNT_FS_TYPE; 581 582 ksft_print_header(); 583 584 ret = statmount(0, 0, NULL, 0, 0); 585 assert(ret == -1); 586 if (errno == ENOSYS) 587 ksft_exit_skip("statmount() syscall not supported\n"); 588 589 setup_namespace(); 590 591 ksft_set_plan(14); 592 test_listmount_empty_root(); 593 test_statmount_zero_mask(); 594 test_statmount_mnt_basic(); 595 test_statmount_sb_basic(); 596 test_statmount_mnt_root(); 597 test_statmount_mnt_point(); 598 test_statmount_fs_type(); 599 test_statmount_string(STATMOUNT_MNT_ROOT, str_off(mnt_root), "mount root"); 600 test_statmount_string(STATMOUNT_MNT_POINT, str_off(mnt_point), "mount point"); 601 test_statmount_string(STATMOUNT_FS_TYPE, str_off(fs_type), "fs type"); 602 test_statmount_string(all_mask, str_off(mnt_root), "mount root & all"); 603 test_statmount_string(all_mask, str_off(mnt_point), "mount point & all"); 604 test_statmount_string(all_mask, str_off(fs_type), "fs type & all"); 605 606 test_listmount_tree(); 607 608 609 if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0) 610 ksft_exit_fail(); 611 else 612 ksft_exit_pass(); 613 } 614