Files
vlibc/tests/test_statvfs.c

156 lines
4.6 KiB
C

/*
* vlibc — sys/statvfs.h test (todo 34).
*
* Exercises the whole <sys/statvfs.h> 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: <sys/statvfs.h> is entirely level 2, so
* at level 1 this TU compiles to a no-op runner.
*/
#include <sys/statvfs.h>
#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 <sys/statvfs.h> 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: <sys/statvfs.h> is level 2, not available here\n");
return 0;
}
#endif /* VLIBC_LEVEL_GE(2) */