Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

add selftest for statmount/listmount

Initial selftest for the new statmount() and listmount() syscalls.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Link: https://lore.kernel.org/r/20231213161104.403171-1-mszeredi@redhat.com
Signed-off-by: Christian Brauner <brauner@kernel.org>

authored by

Miklos Szeredi and committed by
Christian Brauner
5bd3cf8c 35e27a57

+621
+1
tools/testing/selftests/Makefile
··· 26 26 TARGETS += filesystems/binderfs 27 27 TARGETS += filesystems/epoll 28 28 TARGETS += filesystems/fat 29 + TARGETS += filesystems/statmount 29 30 TARGETS += firmware 30 31 TARGETS += fpu 31 32 TARGETS += ftrace
+2
tools/testing/selftests/filesystems/statmount/.gitignore
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + /*_test
+6
tools/testing/selftests/filesystems/statmount/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-or-later 2 + 3 + CFLAGS += -Wall -O2 -g $(KHDR_INCLUDES) 4 + TEST_GEN_PROGS := statmount_test 5 + 6 + include ../../lib.mk
+612
tools/testing/selftests/filesystems/statmount/statmount_test.c
··· 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 + }