156 lines
4.6 KiB
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) */
|