/* * vlibc — sys/statvfs.h test (todo 34). * * Exercises the whole surface end to end: * * 1. statvfs("/") returns 0 with sane values: a positive block size, a * positive transfer size, a positive total block count whose free * and available sub-counts stay within it (unsigned compares — all * counts are fsblkcnt_t/fsfilcnt_t = unsigned long), and a positive * maximum name length. * 2. fstatvfs on a raw-open descriptor of "/" returns 0 and agrees * with statvfs("/") on the stable fields (block size, total blocks, * name length), since both name the same filesystem. * * The -f mode runs the failure shapes — statvfs on a nonexistent path and * fstatvfs on a closed descriptor (-1) — asserting return values only * (errno is never read: vlibc's errno slot collides with the host TCB). It * leaves via a raw SYS_exit_group so no host cleanup runs after the * library's errno writes (house pattern, tests/test_systime.c). * * No host libc headers are included (the -Iinclude path would shadow * GCC's internal headers); diagnostics go through raw SYS_write. The whole * body mirrors the header's gate: is entirely level 2, so * at level 1 this TU compiles to a no-op runner. */ #include #include "../src/internal/syscall.h" /* Write a NUL-terminated string to fd via the raw syscall layer. The * optimize attribute keeps GCC from lowering the length loop into a * strlen call, which would leave a vlibc-owned symbol undefined in this * host-linked standalone binary (house idiom, see src/string). */ static __attribute__((optimize("no-tree-loop-distribute-patterns"))) void say(int fd, const char *s) { long n = 0; while (s[n] != '\0') { n++; } __syscall3(SYS_write, fd, (long)s, n); } #if VLIBC_LEVEL_GE(2) /* Kernel-UAPI open values, local to this test (include/fcntl.h). */ #define T34_AT_FDCWD (-100) #define T34_O_RDONLY 0 #define T34_MISSING "/nonexistent-vlibc-t34-xyz" static int failures; static void check(int ok, const char *msg) { if (ok) { say(1, "ok "); } else { say(1, "FAIL "); failures++; } say(1, msg); say(1, "\n"); } /* 1 + 2. statvfs("/") sanity, then fstatvfs on a raw descriptor of "/". */ static void happy_scenarios(void) { struct statvfs s = {0}; struct statvfs d = {0}; long fd; check(statvfs("/", &s) == 0, "statvfs(\"/\") returns 0"); check(s.f_bsize > 0, "statvfs f_bsize > 0"); check(s.f_frsize > 0, "statvfs f_frsize > 0"); check(s.f_blocks > 0, "statvfs f_blocks > 0"); check(s.f_bfree <= s.f_blocks, "statvfs f_bfree within f_blocks (unsigned)"); check(s.f_bavail <= s.f_bfree, "statvfs f_bavail within f_bfree (unsigned)"); check(s.f_namemax > 0, "statvfs f_namemax > 0"); fd = __syscall4(SYS_openat, T34_AT_FDCWD, (long)"/", T34_O_RDONLY, 0); check(fd >= 0, "raw openat(\"/\") opens the root directory"); if (fd >= 0) { check(fstatvfs((int)fd, &d) == 0, "fstatvfs(fd of \"/\") returns 0"); check(d.f_bsize == s.f_bsize, "fstatvfs f_bsize matches statvfs"); check(d.f_blocks == s.f_blocks, "fstatvfs f_blocks matches statvfs"); check(d.f_namemax == s.f_namemax, "fstatvfs f_namemax matches statvfs"); check(__syscall1(SYS_close, fd) == 0, "raw close of the root descriptor"); } if (failures == 0) { say(1, "all statvfs tests passed\n"); } else { say(1, "FAILURES\n"); } } /* The failure shapes; return values only, never errno. Exit via a raw * SYS_exit_group: the library's errno writes on these paths corrupt glibc's * private dtv slot at %fs:0+8, so host cleanup must never run (house * pattern, tests/test_systime.c). */ static void failure_scenarios(void) { struct statvfs s = {0}; check(statvfs(T34_MISSING, &s) == -1, "-f statvfs(nonexistent) returns -1"); check(fstatvfs(-1, &s) == -1, "-f fstatvfs(-1) returns -1"); if (failures == 0) { say(1, "all statvfs failure scenarios passed\n"); } __syscall1(SYS_exit_group, failures == 0 ? 0 : 1); /* not reached */ } int main(int argc, char **argv) { if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'f' && argv[1][2] == '\0') { failure_scenarios(); } happy_scenarios(); return failures == 0 ? 0 : 1; } #else /* !VLIBC_LEVEL_GE(2) */ /* * Level 1: every symbol is gated at level 2, so there is * nothing to run. Keep the TU compilable at any configured profile. */ int main(void) { say(1, "SKIP: is level 2, not available here\n"); return 0; } #endif /* VLIBC_LEVEL_GE(2) */