wernermisc/fakefile/fakefile.c

303 lines
6.0 KiB
C

/*
* fakefile.c - Programmed file system illusion
*
* Copyright 2012 by Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <stdlib.h>
#include <stdio.h>
#include <poll.h>
#include <assert.h>
#include <sys/wait.h>
#include "util.h"
#include "internal.h"
#include "fakefile.h"
struct event {
struct fakefile_event ev;
struct fakefile *ff;
void *tmp;
void (*respond)(struct event *ev);
};
static struct fakefile *fakefiles = NULL;
static struct pollfd *fds = NULL;
static int nfds = 0;
static void map_slave_fd(struct fakefile *ff, int fd, void *handle)
{
struct fd_map *map;
map = alloc_type(struct fd_map);
map->fd = fd;
map->handle = handle;
map->next = ff->map;
ff->map = map;
}
static void *lookup_slave_fd(struct fakefile *ff, int fd)
{
const struct fd_map *map;
for (map = ff->map; map; map = map->next)
if (map->fd == fd)
return map->handle;
abort();
}
static void unmap_slave_fd(struct fakefile *ff, int fd)
{
struct fd_map **map, *next;
for (map = &ff->map; (*map)->fd != fd; map = &(*map)->next);
next = (*map)->next;
free(*map);
*map = next;
}
struct fakefile *fakefile_execv(const char *path, char *const argv[])
{
struct fakefile *ff;
ff = fakefile_launch(path, argv);
ff->map = NULL;
ff->next = fakefiles;
fakefiles = ff;
fds = realloc(fds, sizeof(struct pollfd)*(nfds+1));
if (!fds) {
perror("realloc");
exit(1);
}
fds[nfds].fd = fakefile_peer_fd(ff->peer);
fds[nfds].events = POLLIN | POLLHUP;
nfds++;
return ff;
}
static void respond_open(struct event *ev)
{
const struct fakefile_open *prm = &ev->ev.u.open;
struct fakefile *ff = ev->ff;
struct fakefile_msg *msg;
msg = fakefile_msg_new(ff->peer, sizeof(int));
if (prm->res > 0)
map_slave_fd(ff, prm->res, prm->handle);
fakefile_msg_add_int(msg, prm->res);
fakefile_msg_end(msg);
free(prm->name);
}
static void respond_read(struct event *ev)
{
const struct fakefile_read *prm = &ev->ev.u.read;
struct fakefile *ff = ev->ff;
struct fakefile_msg *msg;
ssize_t size;
size = prm->len < 0 ? 0 : prm->len;
msg = fakefile_msg_new(ff->peer, sizeof(ssize_t)+size);
fakefile_msg_add_ssize_t(msg, prm->len);
if (size)
fakefile_msg_add(msg, prm->buf, size);
fakefile_msg_end(msg);
free(ev->tmp);
}
static void respond_fstat(struct event *ev)
{
const struct fakefile_fstat *prm = &ev->ev.u.fstat;
struct fakefile *ff = ev->ff;
struct fakefile_msg *msg;
ssize_t size;
size = prm->res < 0 ? 0 : sizeof(struct stat);
msg = fakefile_msg_new(ff->peer, sizeof(int)+size);
fakefile_msg_add_int(msg, prm->res);
if (size)
fakefile_msg_add(msg, &prm->st, sizeof(struct stat));
fakefile_msg_end(msg);
}
static void respond_exit(struct event *ev)
{
}
static void respond_close(struct event *ev)
{
const struct fakefile_close *prm = &ev->ev.u.close;
struct fakefile *ff = ev->ff;
struct fakefile_msg *msg;
msg = fakefile_msg_new(ff->peer, sizeof(int));
fakefile_msg_add_int(msg, prm->res);
fakefile_msg_end(msg);
}
static struct fakefile_event *get_event(int fd)
{
struct fakefile *ff;
struct event *ev;
struct fakefile_msg *msg;
for (ff = fakefiles; ff; ff = ff->next)
if (fakefile_peer_fd(ff->peer) == fd)
break;
assert(ff);
ev = alloc_type(struct event);
ev->ff = ff;
msg = fakefile_msg_recv(ff->peer);
ev->ev.type = fakefile_msg_get_int(msg);
switch (ev->ev.type) {
case ff_et_exit: {
struct fakefile_exit *prm = &ev->ev.u.exit;
ev->respond = respond_exit;
prm->status = fakefile_msg_get_int(msg);
break;
}
case ff_et_open: {
struct fakefile_open *prm = &ev->ev.u.open;
int len;
ev->respond = respond_open;
prm->flags = fakefile_msg_get_int(msg);
prm->mode = fakefile_msg_get_int(msg);
prm->res = fakefile_msg_get_int(msg);
len = fakefile_msg_get_int(msg);
prm->name = alloc_size(len+1);
fakefile_msg_get(msg, prm->name, len);
prm->name[len] = 0;
prm->handle = NULL;
break;
}
case ff_et_read: {
struct fakefile_read *prm = &ev->ev.u.read;
int fd;
ev->respond = respond_read;
fd = fakefile_msg_get_int(msg);
prm->handle = lookup_slave_fd(ff, fd);
prm->len = fakefile_msg_get_size_t(msg);
prm->buf = ev->tmp = alloc_size(prm->len);
break;
}
case ff_et_write: {
abort();
}
case ff_et_close: {
struct fakefile_close *prm = &ev->ev.u.close;
int fd;
ev->respond = respond_close;
fd = fakefile_msg_get_int(msg);
prm->handle = lookup_slave_fd(ff, fd);
prm->res = 0;
break;
}
case ff_et_unlink:
abort();
case ff_et_rename:
abort();
case ff_et_fstat: {
struct fakefile_fstat *prm = &ev->ev.u.fstat;
int fd;
ev->respond = respond_fstat;
fd = fakefile_msg_get_int(msg);
prm->handle = lookup_slave_fd(ff, fd);
prm->res = fstat(fd, &prm->st);
break;
}
default:
abort();
}
fakefile_msg_end(msg);
if (fakefile_internal_event(ev))
return NULL;
return &ev->ev;
}
static struct fakefile_event *handle_exit(int fd)
{
struct fakefile *ff;
struct fakefile_event *event;
for (ff = fakefiles; ff; ff = ff->next)
if (fakefile_peer_fd(ff->peer) == fd)
break;
assert(ff);
event = alloc_type(struct fakefile_event);
event->type = ff_et_exit;
if (waitpid(ff->pid, &event->u.exit.status, 0) < 0) {
perror("waitpid");
exit(1);
}
//@@@ destroy
return event;
}
struct fakefile_event *fakefile_poll(void)
{
struct fakefile_event *event;
struct pollfd *p;
int n;
while (1) {
n = poll(fds, nfds, -1);
if (n < 0) {
perror("poll");
exit(1);
}
if (!n) {
fprintf(stderr, "poll() returned 0\n");
exit(1);
}
for (p = fds; n && p != fds+nfds; p++) {
if (p->revents & POLLIN) {
event = get_event(p->fd);
if (event)
return event;
}
if (p->revents & POLLHUP)
return handle_exit(p->fd);
}
}
}
void fakefile_respond(struct fakefile_event *event)
{
struct event *ev = (struct event *) event;
ev->respond(ev);
free(ev);
}
void fakefile_end(struct fakefile *ff)
{
}