/* * 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); }