mirror of
git://projects.qi-hardware.com/wernermisc.git
synced 2024-11-25 21:03:08 +02:00
303 lines
6.0 KiB
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)
|
||
|
{
|
||
|
}
|