/* * 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 #include #include #include #include #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) { }