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