#include <stdlib.h>
#include <sys/types.h>
#include <sys/statvfs.h>
#include <stdbool.h>
#include <err.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <port.h>
#include <door.h>
static bool
statvfs_fail(const char *path, int exp, struct statvfs *svp)
{
struct statvfs st;
if (svp == NULL) {
svp = &st;
}
if (statvfs(path, svp) == 0) {
warnx("TEST FAILED: statvfs on %s passed, but expected %s",
path, strerrorname_np(exp));
return (false);
}
if (errno != exp) {
warnx("TEST FAILED: statvfs on %s returned wrong errno: "
"expected %s, found %s", path, strerrorname_np(exp),
strerrorname_np(errno));
return (false);
}
(void) printf("TEST PASSED: statvfs on %s correctly returned %s\n",
path, strerrorname_np(exp));
return (true);
}
static bool
statvfs_pass(const char *path, const char *fs)
{
struct statvfs sv;
if (statvfs(path, &sv) != 0) {
warnx("TEST FAILED: statvfs on %s failed with %s, but "
"expected success", path, strerrorname_np(errno));
return (false);
}
(void) printf("TEST PASSED: statvfs on %s worked\n", path);
if (fs == NULL) {
return (true);
}
if (strcmp(sv.f_basetype, fs) != 0) {
warnx("TEST FAILED: statvfs on %s has wrong fs: expected %s, "
"found %s", path, fs, sv.f_basetype);
return (false);
}
(void) printf("TEST PASSED: statvfs on %s correctly indicated fs %s\n",
path, fs);
return (true);
}
typedef struct {
const char *sp_path;
const char *sp_fs;
int sp_ret;
} statvfs_pass_t;
static const statvfs_pass_t statvfs_passes[] = {
{ "/", NULL },
{ "/usr/lib/libc.so.1", NULL },
{ "/var/run", "tmpfs" },
{ "/etc/svc/volatile", "tmpfs" },
{ "/system/boot", "bootfs" },
{ "/system/contract", "ctfs" },
{ "/system/object", "objfs" },
{ "/dev/fd", "fd" },
{ "/etc/mnttab", "mntfs" },
{ "/dev/net", "dev" },
{ "/dev/zero", "devfs" },
{ "/devices/pseudo", "devfs" },
{ "/etc/dfs/sharetab", "sharefs" },
{ "/proc/self/psinfo", "proc" },
{ "/var/run/name_service_door", "namefs" }
};
typedef struct fstatvfs_test {
int (*ft_open)(const struct fstatvfs_test *);
const char *ft_path;
const char *ft_fs;
int ft_ret;
} fstatvfs_test_t;
static int
statvfs_open_file(const fstatvfs_test_t *test)
{
int fd = open(test->ft_path, O_RDONLY);
if (fd < 0) {
err(EXIT_FAILURE, "TEST FAILED: failed to open file %s",
test->ft_path);
}
return (fd);
}
static int
statvfs_open_socket(const fstatvfs_test_t *test)
{
struct sockaddr_in in;
int fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0) {
err(EXIT_FAILURE, "TEST FAILED: failed to create basic "
"socket");
}
(void) memset(&in, 0, sizeof (in));
if (bind(fd, (struct sockaddr *)&in, sizeof (in)) != 0) {
err(EXIT_FAILURE, "TEST FAILED: failed to bind socket");
}
return (fd);
}
static int
statvfs_open_uds(const fstatvfs_test_t *test)
{
int fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
err(EXIT_FAILURE, "TEST FAILED: failed to create UDS");
}
return (fd);
}
static int
statvfs_open_pipe(const fstatvfs_test_t *test)
{
int fds[2];
if (pipe(fds) != 0) {
err(EXIT_FAILURE, "TEST FAILED: failed to create pipe");
}
VERIFY0(close(fds[1]));
return (fds[0]);
}
static int
statvfs_open_negfd(const fstatvfs_test_t *test)
{
return (-1);
}
static int
statvfs_open_bigfd(const fstatvfs_test_t *test)
{
return (0x7777);
}
static int
statvfs_open_portfs(const fstatvfs_test_t *test)
{
int fd = port_create();
if (fd < 0) {
err(EXIT_FAILURE, "TEST FAILED: failed to create event port");
}
return (fd);
}
static void
statvfs_close_door(void *cookie, char *arg, size_t size, door_desc_t *dp,
uint_t ndesc)
{
(void) door_return(NULL, 0, NULL, 0);
}
static int
statvfs_open_door(const fstatvfs_test_t *test)
{
int fd = door_create(statvfs_close_door, NULL, 0);
if (fd < 0) {
err(EXIT_FAILURE, "TEST FAILED: failed to create door");
}
return (fd);
}
static const fstatvfs_test_t fstatvfs_tests[] = {
{ statvfs_open_socket, "localhost socket", "sockfs", 0 },
{ statvfs_open_uds, "UDS socket", "sockfs", 0 },
{ statvfs_open_pipe, "pipe", NULL, ENOSYS },
{ statvfs_open_file, "/dev/tcp", NULL, ENOSYS },
{ statvfs_open_negfd, "bad fd (-1)", NULL, EBADF },
{ statvfs_open_negfd, "bad fd (-1)", NULL, EBADF },
{ statvfs_open_bigfd, "bad fd (0x7777)", NULL, EBADF },
{ statvfs_open_portfs, "event port", NULL, ENOSYS },
{ statvfs_open_door, "door server", NULL, ENOSYS }
};
static bool
fstatvfs_test(const fstatvfs_test_t *test)
{
struct statvfs sv;
int ret, fd, e;
fd = test->ft_open(test);
ret = fstatvfs(fd, &sv);
e = errno;
if (test->ft_ret != EBADF) {
VERIFY0(close(fd));
}
if (ret != 0) {
if (test->ft_ret == 0) {
warnx("TEST FAILED: fstatvfs on %s failed with %s, but "
"expected success", test->ft_path,
strerrorname_np(errno));
return (false);
}
if (e != test->ft_ret) {
warnx("TEST FAILED: fstatvfs on %s returned wrong "
"errno: expected %s, found %s", test->ft_path,
strerrorname_np(test->ft_ret), strerrorname_np(e));
return (false);
}
(void) printf("TEST PASSED: fstatvfs on %s correctly failed "
"with %s\n", test->ft_path, strerrorname_np(test->ft_ret));
return (true);
}
if (test->ft_ret != 0) {
warnx("TEST FAILED: fstatvfs on %s passed, but expected %s",
test->ft_path, strerrorname_np(test->ft_ret));
return (false);
}
(void) printf("TEST PASSED: fstatvfs on %s worked\n", test->ft_path);
if (test->ft_fs == NULL) {
return (true);
}
if (strcmp(sv.f_basetype, test->ft_fs) != 0) {
warnx("TEST FAILED: fstatvfs on %s has wrong fs: expected %s, "
"found %s", test->ft_path, test->ft_fs, sv.f_basetype);
return (false);
}
(void) printf("TEST PASSED: fstatvfs on %s correctly indicated fs %s\n",
test->ft_path, test->ft_fs);
return (true);
}
int
main(void)
{
int ret = EXIT_SUCCESS;
void *unmap;
long page;
page = sysconf(_SC_PAGESIZE);
VERIFY3S(page, >=, sizeof (struct statvfs));
unmap = mmap(NULL, page, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (unmap == MAP_FAILED) {
err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to mmap our "
"empty page");
}
if (!statvfs_fail("/elbe12th!", ENOENT, NULL)) {
ret = EXIT_FAILURE;
}
if (!statvfs_fail("/usr/sbin/dtrace/wait", ENOTDIR, NULL)) {
ret = EXIT_FAILURE;
}
if (!statvfs_fail("/", EFAULT, unmap)) {
ret = EXIT_FAILURE;
}
for (size_t i = 0; i < ARRAY_SIZE(statvfs_passes); i++) {
fstatvfs_test_t ft;
if (!statvfs_pass(statvfs_passes[i].sp_path,
statvfs_passes[i].sp_fs)) {
ret = EXIT_FAILURE;
}
ft.ft_open = statvfs_open_file;
ft.ft_path = statvfs_passes[i].sp_path;
ft.ft_fs = statvfs_passes[i].sp_fs;
ft.ft_ret = 0;
if (!fstatvfs_test(&ft)) {
ret = EXIT_FAILURE;
}
}
for (size_t i = 0; i < ARRAY_SIZE(fstatvfs_tests); i++) {
if (!fstatvfs_test(&fstatvfs_tests[i])) {
ret = EXIT_FAILURE;
}
}
if (ret == EXIT_SUCCESS) {
(void) printf("All tests completed successfully\n");
}
return (ret);
}