|
Message-Id: <20171011045254.15544-1-nenolod@dereferenced.org> Date: Wed, 11 Oct 2017 04:52:54 +0000 From: William Pitcock <nenolod@...eferenced.org> To: musl@...ts.openwall.com Cc: William Pitcock <nenolod@...eferenced.org> Subject: [PATCH v5] stdio: implement fopencookie(3) The fopencookie(3) function allows the programmer to create a custom stdio implementation, using four hook functions which operate on a "cookie" data type. Changelog: v5: - implement stdio readahead buffering support v4: - remove parameter names from header function declarations v3: - remove spurious `struct winsize` - make f->lock unconditionally 0 v2: - properly implement stdio buffering v1: - initial proof of concept --- include/stdio.h | 9 ++++ src/stdio/fopencookie.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 src/stdio/fopencookie.c diff --git a/include/stdio.h b/include/stdio.h index 884d2e6a..da0563f6 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -182,6 +182,15 @@ int vasprintf(char **, const char *, __isoc_va_list); #ifdef _GNU_SOURCE char *fgets_unlocked(char *, int, FILE *); int fputs_unlocked(const char *, FILE *); + +typedef struct { + ssize_t (*read)(void *, char *, size_t); + ssize_t (*write)(void *, const char *, size_t); + int (*seek)(void *, off_t *, int); + int (*close)(void *); +} cookie_io_functions_t; + +FILE *fopencookie(void *, const char *, cookie_io_functions_t); #endif #if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE) diff --git a/src/stdio/fopencookie.c b/src/stdio/fopencookie.c new file mode 100644 index 00000000..fa15be00 --- /dev/null +++ b/src/stdio/fopencookie.c @@ -0,0 +1,115 @@ +#define _GNU_SOURCE +#include "stdio_impl.h" +#include <stdlib.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +struct fcookie { + void *cookie; + cookie_io_functions_t iofuncs; +}; + +static size_t cookieread(FILE *f, unsigned char *buf, size_t len) +{ + struct fcookie *fc = f->cookie; + size_t ret, remain = len, readlen = 0; + + if (fc->iofuncs.read == NULL) return 0; + + ret = fc->iofuncs.read(fc->cookie, (char *) buf, len - 1); + if (ret == 0) goto bail_eof; + + readlen += ret; + remain -= ret; + + f->rpos = f->buf; + ret = fc->iofuncs.read(fc->cookie, (char *) f->rpos, f->buf_size); + if (ret == 0) goto bail_eof; + f->rend = f->rpos + ret; + + if (remain > f->buf_size) remain = f->buf_size; + memcpy(buf + readlen, f->rpos, remain); + readlen += remain; + f->rpos += remain; + + return readlen; + +bail_eof: + f->flags |= F_EOF; + f->rpos = f->rend = f->buf; + return readlen; +} + +static size_t cookiewrite(FILE *f, const unsigned char *buf, size_t len) +{ + struct fcookie *fc = f->cookie; + size_t ret; + size_t len2 = f->wpos - f->wbase; + if (fc->iofuncs.write == NULL) return 0; + if (len2) { + f->wpos = f->wbase; + if (cookiewrite(f, f->wpos, len2) < len2) return 0; + } + return fc->iofuncs.write(fc->cookie, (const char *) buf, len); +} + +static off_t cookieseek(FILE *f, off_t off, int whence) +{ + struct fcookie *fc = f->cookie; + if (fc->iofuncs.seek) return fc->iofuncs.seek(fc->cookie, &off, whence); + return -1; +} + +static int cookieclose(FILE *f) +{ + struct fcookie *fc = f->cookie; + if (fc->iofuncs.close) return fc->iofuncs.close(fc->cookie); + return 0; +} + +FILE *fopencookie(void *cookie, const char *mode, cookie_io_functions_t iofuncs) +{ + FILE *f; + struct fcookie *fc; + + /* Check for valid initial mode character */ + if (!strchr("rwa", *mode)) { + errno = EINVAL; + return 0; + } + + /* Allocate FILE+fcookie+buffer or fail */ + if (!(f=malloc(sizeof *f + sizeof *fc + UNGET + BUFSIZ))) return 0; + + /* Zero-fill only the struct, not the buffer */ + memset(f, 0, sizeof *f); + + /* Impose mode restrictions */ + if (!strchr(mode, '+')) f->flags = (*mode == 'r') ? F_NOWR : F_NORD; + + /* Set up our fcookie */ + fc = (void *)(f + 1); + fc->cookie = cookie; + fc->iofuncs.read = iofuncs.read; + fc->iofuncs.write = iofuncs.write; + fc->iofuncs.seek = iofuncs.seek; + fc->iofuncs.close = iofuncs.close; + + f->fd = -1; + f->cookie = fc; + f->buf = (unsigned char *)f + sizeof *f + sizeof *fc + UNGET; + f->buf_size = BUFSIZ; + f->lbf = EOF; + f->lock = 0; + + /* Initialize op ptrs. No problem if some are unneeded. */ + f->read = cookieread; + f->write = cookiewrite; + f->seek = cookieseek; + f->close = cookieclose; + + /* Add new FILE to open file list */ + return __ofl_add(f); +} -- 2.13.3
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.