diff --git a/fakefile/Makefile b/fakefile/Makefile new file mode 100644 index 0000000..85476fc --- /dev/null +++ b/fakefile/Makefile @@ -0,0 +1,55 @@ +# +# Makefile - Build the Fakefile libraries +# +# 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. +# + +CFLAGS = -Wall -g -Wextra -Wno-unused-parameter -fPIC +LDFLAGS = -L. -Wl,-rpath=$(shell pwd) +LDLIBS = -lfakefile + +LIBFAKEFILE_OBJS = comm.o fakefile.o file.o launch.o +LIBFFSLAVE_OBJS = comm.o slave.o +TARGETS = libfakefile.so libfakefile_slave.so demo + +CC_normal := $(CC) +LD_normal := $(LD) +DEPEND_normal := $(CPP) $(CFLAGS) -MM -MG + +CC_quiet = @echo " CC " $@ && $(CC_normal) +LD_quiet = @echo " LD " $@ && $(LD_normal) +GEN_quiet = @echo " GENERATE " $@ && +DEPEND_quiet = @$(DEPEND_normal) + +ifeq ($(V),1) + CC = $(CC_normal) + LD = $(LD_normal) + GEN = + DEPEND = $(DEPEND_normal) +else + CC = $(CC_quiet) + LD = $(LD_quiet) + GEN = $(GEN_quiet) + DEPEND = $(DEPEND_quiet) +endif + +.PHONY: all clean spotless + +all: $(TARGETS) + +libfakefile.so: $(LIBFAKEFILE_OBJS) + $(LD) -shared -o $@ $^ + +libfakefile_slave.so: $(LIBFFSLAVE_OBJS) + $(LD) -shared -o $@ $^ -ldl + +clean: + rm -f $(LIBFAKEFILE_OBJS) $(LIBFFSLAVE_OBJS) + +spotless: clean + rm -f $(TARGETS) diff --git a/fakefile/README b/fakefile/README new file mode 100644 index 0000000..35532ad --- /dev/null +++ b/fakefile/README @@ -0,0 +1,3 @@ +Don't use this yet ! This was intended as a means to run Kicad in a +"jail", but that didn't pan out. Probably have to convert it to use +ptrace. diff --git a/fakefile/comm.c b/fakefile/comm.c new file mode 100644 index 0000000..600a8f8 --- /dev/null +++ b/fakefile/comm.c @@ -0,0 +1,254 @@ +/* + * comm.c - Fakefile IPC functions + * + * 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 +#include + +#include "util.h" +#include "comm.h" + + +#define BUF_SIZE 4096 + + +struct fakefile_peer { + int in, out; + uint8_t buf[BUF_SIZE]; + void *pos; + const void *end; +}; + + +struct fakefile_msg { + struct fakefile_peer *peer; + void *buf; /* NULL if reading */ + void *pos; /* current position in buffer; write only */ + size_t len; /* amount of data read/written so far */ + size_t msg_size; /* declared size */ + size_t buf_size; /* allocated size; write only */ +}; + + +static int master_fd(const char *name) +{ + const char *s; + char *end; + long n; + + s = getenv(name); + if (!s) { + fprintf(stderr, "%s not set\n", name); + exit(1); + } + n = strtol(s, &end, 0); + if (*end || n < 0) { + fprintf(stderr, "invalid fd \"%s\" for %s\n", s, name); + exit(1); + } + return n; +} + + +static struct fakefile_peer *sanitize_peer(struct fakefile_peer *peer) +{ + static int have_peer = 0; + static struct fakefile_peer master; + + if (peer) + return peer; + if (!have_peer) { + master.in = master_fd(FAKEFILE_MASTER_OUT_VAR); + master.out = master_fd(FAKEFILE_MASTER_IN_VAR); + master.pos = master.buf; + master.end = master.buf; + have_peer = 1; + } + return &master; +} + + +struct fakefile_peer *fakefile_peer(int in, int out) +{ + struct fakefile_peer *peer; + + peer = alloc_type(struct fakefile_peer); + peer->in = in; + peer->out = out; + peer->pos = peer->buf; + peer->end = peer->buf; + return peer; +} + + +void fakefile_peer_close(struct fakefile_peer *peer) +{ + (void) close(peer->in); + (void) close(peer->out); + free(peer); +} + + +int fakefile_peer_fd(const struct fakefile_peer *peer) +{ + return peer->in; +} + + +struct fakefile_msg *fakefile_msg_new(struct fakefile_peer *peer, int size) +{ + struct fakefile_msg *msg; + size_t buf_size; + + size += sizeof(int); + buf_size = size > BUF_SIZE ? BUF_SIZE : size; + msg = alloc_size(sizeof(struct fakefile_msg)+buf_size); + msg->peer = sanitize_peer(peer); + msg->buf = msg->pos = msg+1; + msg->len = 0; + msg->msg_size = size; + msg->buf_size = buf_size; + fakefile_msg_add_int(msg, size); + return msg; +} + + +static void flush_out(struct fakefile_msg *msg) +{ + const void *p; + ssize_t len; + + if (msg->pos == msg->buf) + return; + for (p = msg->buf; p != msg->pos; p += len) { + len = write(msg->peer->out, p, msg->pos-p); + if (len <= 0) { + perror("fakefile IPC write"); + exit(1); + } + } +} + + +struct fakefile_msg *fakefile_msg_recv(struct fakefile_peer *peer) +{ + struct fakefile_msg *msg; + + msg = alloc_type(struct fakefile_msg); + msg->peer = sanitize_peer(peer); + msg->buf = NULL; + msg->len = 0; + msg->msg_size = sizeof(int); + msg->msg_size = fakefile_msg_get_int(msg); + return msg; +} + + +void fakefile_msg_end(struct fakefile_msg *msg) +{ +#if 0 +fprintf(stderr, "end: len %d size %d (%p)\n", + (int) msg->len, (int) msg->msg_size, msg->buf); +#endif + assert(msg->len == msg->msg_size); + if (msg->buf) + flush_out(msg); + free(msg); +} + + +static void add_chunk(struct fakefile_msg *msg, const void *buf, size_t len) +{ + memcpy(msg->pos, buf, len); + msg->pos += len; + if (msg->pos == msg->buf+msg->buf_size) { + flush_out(msg); + msg->pos = msg->buf; + } +} + + +void fakefile_msg_add(struct fakefile_msg *msg, const void *buf, size_t len) +{ + size_t chunk; + +#if 0 +fprintf(stderr, "add: %d+%d/%d (%p)\n", + (int) msg->len, (int) len, (int) msg->msg_size, msg->buf); +#endif + assert(msg->len+len <= msg->msg_size); + while (len && msg->pos+len > msg->buf+msg->buf_size) { + chunk = msg->buf+msg->buf_size-msg->pos; + if (chunk > len) + chunk = len; + add_chunk(msg, buf, chunk); + buf += chunk; + len -= chunk; + msg->len += chunk; + } + if (len) { + add_chunk(msg, buf, len); + msg->len += len; + } +} + + +static size_t get_chunk(struct fakefile_peer *peer, void *buf, size_t len) +{ + ssize_t got; + + got = read(peer->in, buf, len); + if (got < 0) { + perror("fakefile IPC read"); + exit(1); + } + if (!got) { + fprintf(stderr, "fakefile EOF\n"); + exit(1); + } + return got; +} + + +void fakefile_msg_get(struct fakefile_msg *msg, void *buf, size_t len) +{ + struct fakefile_peer *peer = msg->peer; + size_t chunk, got; + + assert(msg->len+len <= msg->msg_size); + if (!len) + return; + if (peer->pos != peer->end) { + chunk = peer->end-peer->pos; + if (chunk > len) + chunk = len; + memcpy(buf, peer->pos, chunk); + peer->pos += chunk; + buf += chunk; + len -= chunk; + msg->len += chunk; + } + while (len >= BUF_SIZE) { + got = get_chunk(peer, buf, BUF_SIZE); + buf += got; + len -= got; + msg->len += got; + } + if (!len) + return; + peer->pos = peer->buf; + peer->end = peer->buf+get_chunk(peer, peer->buf, BUF_SIZE); + fakefile_msg_get(msg, buf, len); +} diff --git a/fakefile/comm.h b/fakefile/comm.h new file mode 100644 index 0000000..fb83553 --- /dev/null +++ b/fakefile/comm.h @@ -0,0 +1,65 @@ +/* + * comm.h - Fakefile IPC functions + * + * 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. + */ + +#ifndef COMM_H +#define COMM_H + +#include + + +#define FAKEFILE_MASTER_OUT_VAR "FAKEFILE_MASTER_OUT" +#define FAKEFILE_MASTER_IN_VAR "FAKEFILE_MASTER_IN" + + +struct fakefile_peer; +struct fakefile_msg; + +struct fakefile_peer *fakefile_peer(int in, int out); +void fakefile_peer_close(struct fakefile_peer *peer); +int fakefile_peer_fd(const struct fakefile_peer *peer); + +struct fakefile_msg *fakefile_msg_new(struct fakefile_peer *peer, + int size); +struct fakefile_msg *fakefile_msg_recv(struct fakefile_peer *peer); +void fakefile_msg_end(struct fakefile_msg *msg); + +void fakefile_msg_add(struct fakefile_msg *msg, const void *buf, size_t len); +void fakefile_msg_get(struct fakefile_msg *msg, void *buf, size_t len); + + +#define decl_add(type) \ + static inline void fakefile_msg_add_##type( \ + struct fakefile_msg *msg, type value) \ + { \ + fakefile_msg_add(msg, &value, sizeof(value)); \ + } + +#define decl_get(type) \ + static inline type fakefile_msg_get_##type( \ + struct fakefile_msg *msg) \ + { \ + type value; \ + \ + fakefile_msg_get(msg, &value, sizeof(value)); \ + return value; \ + } + +#define decl_access(type) decl_add(type) decl_get(type) + +decl_access(int) +decl_access(size_t) +decl_access(ssize_t) + +#undef decl_add +#undef decl_get +#undef decl_access + +#endif /* !COMM_H */ diff --git a/fakefile/demo.c b/fakefile/demo.c new file mode 100644 index 0000000..456ff03 --- /dev/null +++ b/fakefile/demo.c @@ -0,0 +1,94 @@ +/* + * demo.c - Fakefile demo + * + * 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 "fakefile.h" + + +static void usage(const char *name) +{ + fprintf(stderr, + "usage: %s command [arg ...]\n" + " %s -e path errno command\n" + " %s -f path content command\n", + name, name, name); + exit(1); +} + + +int main(int argc, char **argv) +{ + struct fakefile *ff; + struct fakefile_event *ev; + int error = 0; + const char *path = NULL, *fake = NULL; + char *end; + + if (argc == 1) + usage(*argv); + if (*argv[1] == '-') { + if (argc < 5) + usage(*argv); + path = argv[2]; + switch (argv[1][1]) { + case 'e': + error = strtoul(argv[3], &end, 0); + if (*end) + usage(*argv); + break; + case 'f': + fake = argv[3]; + break; + default: + usage(*argv); + } + argv += 3; + } + ff = fakefile_execv(argv[1], argv+1); + while (1) { + ev = fakefile_poll(); + switch (ev->type) { + case ff_et_open: + fprintf(stderr, "\"%s\"\n", ev->u.open.name); + if (path && !strcmp(ev->u.open.name, path)) { + if (!fake) + ev->u.open.res = -error; + } else { + ev->u.open.res = 0; + } + break; + case ff_et_read: { + size_t len, left; + + len = ev->u.read.len; + left = strlen(fake); + if (len > left) + len = left; + ev->u.read.buf = (void *) fake; + ev->u.read.len = len; + fake += len; + break; + } + case ff_et_exit: + goto out; + default: + break; + } + fakefile_respond(ev); + } +out: + fakefile_end(ff); + return 0; +} diff --git a/fakefile/fakefile.c b/fakefile/fakefile.c new file mode 100644 index 0000000..015459b --- /dev/null +++ b/fakefile/fakefile.c @@ -0,0 +1,302 @@ +/* + * 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) +{ +} diff --git a/fakefile/fakefile.h b/fakefile/fakefile.h new file mode 100644 index 0000000..2f9d49c --- /dev/null +++ b/fakefile/fakefile.h @@ -0,0 +1,91 @@ +/* + * fakefile.h - 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. + */ + +#ifndef FAKEFILE_H +#define FAKEFILE_H + +#include +#include + + +enum fakefile_event_type { + ff_et_exit, + ff_et_open, + ff_et_read, + ff_et_write, + ff_et_close, + ff_et_unlink, + ff_et_rename, + ff_et_fstat, +}; + +struct fakefile_event { + enum fakefile_event_type type; + union { + struct fakefile_exit { + /* in */ + int status; + } exit; + struct fakefile_open { + /* in */ + char *name; + int flags; + int mode; + /* out */ + void *handle; + int res; /* < 0: -errno; 0: regular open (in slave) */ + } open; + struct fakefile_read { + /* in */ + const void *handle; + /* in/out */ + void *buf; + ssize_t len; + } read; + struct fakefile_write { + /* in */ + const void *handle; + void *buf; + /* in/out */ + ssize_t len; + } write; + struct fakefile_close { + /* in */ + const void *handle; + /* out */ + int res; + } close; + struct fakefile_unlink { + } unlink; + struct fakefile_rename { + } rename; + struct fakefile_fstat { + /* in */ + const void *handle; + /* out */ + struct stat st; + int res; + } fstat; + } u; + void *user; +}; + + +struct fakefile *fakefile_execv(const char *path, char *const argv[]); +struct fakefile_event *fakefile_poll(void); +void fakefile_respond(struct fakefile_event *event); +void fakefile_end(struct fakefile *ff); + +void fakefile_file(struct fakefile *ff, const char *name, + const void *in, size_t len, void **out); +void fakefile_antifile(struct fakefile *ff, const char *name); + +#endif /* !FAKEFILE_H */ diff --git a/fakefile/file.c b/fakefile/file.c new file mode 100644 index 0000000..6089ee6 --- /dev/null +++ b/fakefile/file.c @@ -0,0 +1,41 @@ +/* + * file.c - Whole file operations + * + * 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 "fakefile.h" +#include "internal.h" + + +int fakefile_internal_event(struct event *ev) +{ + (void) ev; + return 0; +} + + +static void add_file(struct fakefile *ff, const char *name, + const void *in, size_t len, void **out) +{ + /* ... */ +} + + +void fakefile_file(struct fakefile *ff, const char *name, + const void *in, size_t len, void **out) +{ + add_file(ff, name, in, len, out); +} + + +void fakefile_antifile(struct fakefile *ff, const char *name) +{ + add_file(ff, name, NULL, 0, NULL); +} diff --git a/fakefile/internal.h b/fakefile/internal.h new file mode 100644 index 0000000..9637f86 --- /dev/null +++ b/fakefile/internal.h @@ -0,0 +1,40 @@ +/* + * internal.h - Internal functions and data structures at master + * + * 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. + */ + +#ifndef INTERNAL_H +#define INTERNAL_H + +#include + +#include "comm.h" + + +struct fd_map { + int fd; + void *handle; + struct fd_map *next; +}; + +struct fakefile { + pid_t pid; + struct fakefile_peer *peer; + struct fd_map *map; + struct fakefile *next; +}; + + +struct event; + + +struct fakefile *fakefile_launch(const char *path, char *const argv[]); +int fakefile_internal_event(struct event *ev); + +#endif /* !INTERNAL_H */ diff --git a/fakefile/launch.c b/fakefile/launch.c new file mode 100644 index 0000000..f65683b --- /dev/null +++ b/fakefile/launch.c @@ -0,0 +1,162 @@ +/* + * launch.c - Fakefile process launcher + * + * 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 +#include + +#include "util.h" +#include "fakefile.h" +#include "comm.h" +#include "internal.h" + + +#define FD_DIR "/proc/self/fd" + + +static void safe_setenvf(const char *var, const char *fmt, ...) +{ + va_list ap; + char *buf; + ssize_t len; + + va_start(ap, fmt); + len = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + if (len < 0) { + perror("vsprintf"); + exit(1); + } + + buf = alloc_size(len+1); + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + + if (setenv(var, buf, 1) < 0) { + perror("setenv"); + exit(1); + } + + free(buf); +} + + +static void env_append(const char *var, const char *s) +{ + const char *old; + char *tmp; + size_t len1, len2; + + old = getenv(var); + if (!old) { + safe_setenvf(var, "%s", s+1); + return; + } + len1 = strlen(old); + len2 = strlen(s); + tmp = alloc_size(len1+len2+1); + memcpy(tmp, old, len1); + memcpy(tmp+len1, s, len2+1); + safe_setenvf(var, "%s", tmp); + free(tmp); +} + + +static void closefds(int preserve, ...) +{ + va_list ap; + DIR *dir; + int fd; + struct dirent *de; + int n; + + fd = open(FD_DIR, O_RDONLY); + if (fd < 0) { + perror(FD_DIR); + exit(1); + } + dir = fdopendir(fd); + if (!dir) { + perror("fdopendir"); + exit(1); + } + while (1) { +next: + de = readdir(dir); + if (!de) + break; + n = atoi(de->d_name); + if (n <= 2 || n == fd) + continue; + if (preserve > 0) { + if (n == preserve) + continue; + va_start(ap, preserve); + while (1) { + n = va_arg(ap, int); + if (n < 0) + break; + if (n == n) + goto next; + } + va_end(ap); + } + (void) close(n); + } + if (closedir(dir) < 0) { + perror("closedir"); + exit(1); + } +} + + +struct fakefile *fakefile_launch(const char *path, char *const argv[]) +{ + struct fakefile *ff; + int m2s[2], s2m[2]; + pid_t pid; + + if (pipe(m2s) < 0 || pipe(s2m) < 0) { + perror("pipe"); + exit(1); + } + pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } + if (!pid) { + env_append("LD_LIBRARY_PATH", ":."); + env_append("LD_PRELOAD", " libfakefile_slave.so"); + safe_setenvf(FAKEFILE_MASTER_OUT_VAR, "%d", m2s[0]); + safe_setenvf(FAKEFILE_MASTER_IN_VAR, "%d", s2m[1]); + closefds(m2s[0], s2m[1], -1); + execv(path, argv); + perror(path); + _exit(1); + } + + (void) close(m2s[0]); + (void) close(s2m[1]); + + ff = alloc_type(struct fakefile); + ff->pid = pid; + ff->peer = fakefile_peer(s2m[0], m2s[1]); + + return ff; +} diff --git a/fakefile/slave.c b/fakefile/slave.c new file mode 100644 index 0000000..a53e4e8 --- /dev/null +++ b/fakefile/slave.c @@ -0,0 +1,305 @@ +/* + * slave.c - Fakefile slave library + * + * 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 +#include +#include +#include +#include + +#define __USE_GNU /* for RTLD_NEXT */ +#include + +#include "util.h" +#include "fakefile.h" +#include "comm.h" + + +#define DUMMY_FILE "/dev/null" +#define master NULL + + +static struct fd_entry { + int fd; + struct fd_entry *next; +} *fake_fds = NULL; + +static int (*libc_close)(int fd); +static int (*libc_dup)(int oldfd); +static int (*libc_dup2)(int oldfd, int newfd); +static int (*libc_open)(const char *pathname, int flags, mode_t mode); +static ssize_t (*libc_read)(int fd, void *buf, size_t count); + +static FILE *(*libc_fopen)(const char *path, const char *mode); + +static void init_self(void) +{ + static int initialized = 0; + + if (initialized) + return; + libc_close = dlsym(RTLD_NEXT, "close"); + libc_dup = dlsym(RTLD_NEXT, "dup"); + libc_dup2 = dlsym(RTLD_NEXT, "dup2"); + libc_open = dlsym(RTLD_NEXT, "open"); + libc_read = dlsym(RTLD_NEXT, "read"); +libc_fopen = dlsym(RTLD_NEXT, "fopen"); + if (!libc_close || !libc_dup || !libc_dup2 || !libc_open || + !libc_read) { + perror("dlsym"); + _exit(1); + } + initialized = 1; +} + + +static int get_fd(void) +{ + int fd; + + fd = libc_open(DUMMY_FILE, O_RDONLY, 0); + if (fd < 0) { + perror(DUMMY_FILE); + exit(1); + } + assert(fd); + return fd; +} + + +static void add_fake_fd(int fd) +{ + struct fd_entry *e; + + e = alloc_type(struct fd_entry); + e->fd = fd; + e->next = fake_fds; + fake_fds =e; +} + + +static int is_fake_fd(int fd) +{ + const struct fd_entry *e; + + for (e = fake_fds; e && e->fd != fd; e = e->next); + return !!e; +} + + +static int del_fake_fd(int fd) +{ + struct fd_entry **e, *next; + + for (e = &fake_fds; *e; e = &(*e)->next) + if ((*e)->fd == fd) + break; + if (!*e) + return 0; + next = (*e)->next; + free(*e); + *e = next; + (void) libc_close(fd); + return 1; +} + + +int open(const char *pathname, int flags, ...) +{ + va_list ap; + struct fakefile_msg *msg; + size_t len; + int mode = 0; + int fd, res; + + init_self(); + if (flags & O_CREAT) { + va_start(ap, flags); + mode = va_arg(ap, int); + va_end(ap); + } + + fd = get_fd(); + len = strlen(pathname); + + msg = fakefile_msg_new(master, len+5*sizeof(int)); + fakefile_msg_add_int(msg, ff_et_open); + fakefile_msg_add_int(msg, flags); + fakefile_msg_add_int(msg, mode); + fakefile_msg_add_int(msg, fd); + fakefile_msg_add_int(msg, len); + fakefile_msg_add(msg, pathname, len); + fakefile_msg_end(msg); + + msg = fakefile_msg_recv(master); + res = fakefile_msg_get_int(msg); + fakefile_msg_end(msg); + +//fprintf(stderr, "res %d\n", (int) res); + if (res <= 0) { + (void) close(fd); + if (res < 0) { + errno = -res; + return -1; + } + } + if (res) + add_fake_fd(fd); + else + res = libc_open(pathname, flags, mode); + return res; +} + + +ssize_t read(int fd, void *buf, size_t count) +{ + struct fakefile_msg *msg; + ssize_t res; + + init_self(); + if (!is_fake_fd(fd)) + return libc_read(fd, buf, count); + msg = fakefile_msg_new(master, 2*sizeof(int)+sizeof(size_t)); + fakefile_msg_add_int(msg, ff_et_read); + fakefile_msg_add_int(msg, fd); + fakefile_msg_add_size_t(msg, count); + fakefile_msg_end(msg); + + msg = fakefile_msg_recv(master); + res = fakefile_msg_get_size_t(msg); + if (res > 0) { + assert((size_t) res <= count); + fakefile_msg_get(msg, buf, res); + } else if (res < 0) { + errno = -res; + res = -1; + } + fakefile_msg_end(msg); +//fprintf(stderr, "READ %d\n", (int) res); + return res; +} + + +int fstat(int fd, struct stat *buf) +{ + struct fakefile_msg *msg; + int res; + + init_self(); + msg = fakefile_msg_new(master, 2*sizeof(int)); + fakefile_msg_add_int(msg, ff_et_fstat); + fakefile_msg_add_int(msg, fd); + fakefile_msg_end(msg); + + msg = fakefile_msg_recv(master); + res = fakefile_msg_get_size_t(msg); + if (res < 0) { + errno = -res; + res = -1; + } else { + fakefile_msg_get(msg, buf, sizeof(struct stat)); + } + fakefile_msg_end(msg); + + return res; +} + + +int stat(const char *path, struct stat *buf) +{ + int fd, res; + + fd = open(path, O_RDONLY); + if (fd < 0) + return fd; + res = fstat(fd, buf); + (void) close(fd); + + return res; +} + + +int lstat(const char *path, struct stat *buf) +{ + return stat(path, buf); +} + + +int close(int fd) +{ + struct fakefile_msg *msg; + int res; + + init_self(); + if (!del_fake_fd(fd)) + return libc_close(fd); + + msg = fakefile_msg_new(master, 2*sizeof(int)); + fakefile_msg_add_int(msg, ff_et_close); + fakefile_msg_add_int(msg, fd); + fakefile_msg_end(msg); + + msg = fakefile_msg_recv(master); + res = fakefile_msg_get_int(msg); + fakefile_msg_end(msg); + + if (res < 0) { + errno = -res; + return -1; + } + + return res; +} + + +FILE *fopen(const char *path, const char *mode) +{ + init_self(); + fprintf(stderr, "fopen \"%s\"\n", path); + return libc_fopen(path, mode); +} + + +int dup(int oldfd) +{ + init_self(); + if (is_fake_fd(oldfd)) { + fprintf(stderr, "not supporting \"dup\" yet\n"); + exit(1); + } + return libc_dup(oldfd); +} + + + +int dup2(int oldfd, int newfd) +{ + int res; + + init_self(); + if (oldfd == newfd) + return 0; + if (is_fake_fd(oldfd)) { + fprintf(stderr, "not supporting \"dup2\" yet\n"); + exit(1); + } + if (is_fake_fd(newfd)) { + res = close(newfd); + if (res < 0) + return res; + } + return libc_dup2(oldfd, newfd); +} diff --git a/fakefile/util.h b/fakefile/util.h new file mode 100644 index 0000000..b53bc6b --- /dev/null +++ b/fakefile/util.h @@ -0,0 +1,26 @@ +/* + * util.h - Utility functions + * + * 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. + */ + +#ifndef UTIL_H +#define UTIL_H + +#include + + +#define alloc_size(s) \ + ({ void *alloc_size_tmp = malloc(s); \ + if (!alloc_size_tmp) \ + abort(); \ + alloc_size_tmp; }) + +#define alloc_type(t) ((t *) alloc_size(sizeof(t))) + +#endif /* !UTIL_H */