Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
From 061843340fbf2493bb615e20e66f60c5d1ef0455 Mon Sep 17 00:00:00 2001
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
From: William Pitcock <nenolod@dereferenced.org>
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
Date: Tue, 5 Dec 2017 16:04:43 -0500
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
Subject: implement the fopencookie extension to stdio
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
notes added by maintainer:
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
this function is a GNU extension. it was chosen over the similar BSD
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
function funopen because the latter depends on fpos_t being an
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
arithmetic type as part of its public API, conflicting with our
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
definition of fpos_t and with the intent that it be an opaque type. it
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
was accepted for inclusion because, despite not being widely used, it
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
is usually very difficult to extricate software using it from the
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
dependency on it.
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
calling pattern for the read and write callbacks is not likely to
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
match glibc or other implementations, but should work with any
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
reasonable callbacks. in particular the read function is never called
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
without at least one byte being needed to satisfy its caller, so that
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
spurious blocking is not introduced.
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
contracts for what callbacks called from inside libc/stdio can do are
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
always complicated, and at some point still need to be specified
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
explicitly. at the very least, the callbacks must return or block
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
indefinitely (they cannot perform nonlocal exits) and they should not
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
make calls to stdio using their own FILE as an argument.
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
---
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
 include/stdio.h         |  14 +++++
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
 src/stdio/fopencookie.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
 2 files changed, 152 insertions(+)
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
 create mode 100644 src/stdio/fopencookie.c
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
diff --git a/include/stdio.h b/include/stdio.h
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
index 884d2e6..2932c76 100644
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
--- a/include/stdio.h
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+++ b/include/stdio.h
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
@@ -182,6 +182,20 @@ int vasprintf(char **, const char *, __isoc_va_list);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
 #ifdef _GNU_SOURCE
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
 char *fgets_unlocked(char *, int, FILE *);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
 int fputs_unlocked(const char *, FILE *);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+typedef ssize_t (cookie_read_function_t)(void *, char *, size_t);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+typedef ssize_t (cookie_write_function_t)(void *, const char *, size_t);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+typedef int (cookie_seek_function_t)(void *, off_t *, int);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+typedef int (cookie_close_function_t)(void *);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+typedef struct {
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	cookie_read_function_t *read;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	cookie_write_function_t *write;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	cookie_seek_function_t *seek;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	cookie_close_function_t *close;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+} cookie_io_functions_t;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+FILE *fopencookie(void *, const char *, cookie_io_functions_t);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
 #endif
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
 
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
 #if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE)
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
diff --git a/src/stdio/fopencookie.c b/src/stdio/fopencookie.c
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
new file mode 100644
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
index 0000000..2f46dd5
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
--- /dev/null
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+++ b/src/stdio/fopencookie.c
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
@@ -0,0 +1,138 @@
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+#define _GNU_SOURCE
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+#include "stdio_impl.h"
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+#include <stdlib.h>
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+#include <sys/ioctl.h>
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+#include <fcntl.h>
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+#include <errno.h>
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+#include <string.h>
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+struct fcookie {
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	void *cookie;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	cookie_io_functions_t iofuncs;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+};
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+struct cookie_FILE {
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	FILE f;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	struct fcookie fc;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	unsigned char buf[UNGET+BUFSIZ];
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+};
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+static size_t cookieread(FILE *f, unsigned char *buf, size_t len)
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+{
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	struct fcookie *fc = f->cookie;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	ssize_t ret = -1;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	size_t remain = len, readlen = 0;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	size_t len2 = len - !!f->buf_size;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	if (!fc->iofuncs.read) goto bail;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	if (len2) {
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		ret = fc->iofuncs.read(fc->cookie, (char *) buf, len2);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		if (ret <= 0) goto bail;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		readlen += ret;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		remain -= ret;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	}
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	if (!f->buf_size || remain > !!f->buf_size) return readlen;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->rpos = f->buf;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	ret = fc->iofuncs.read(fc->cookie, (char *) f->rpos, f->buf_size);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	if (ret <= 0) goto bail;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->rend = f->rpos + ret;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	buf[readlen++] = *f->rpos++;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	return readlen;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+bail:
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->flags |= ret == 0 ? F_EOF : F_ERR;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->rpos = f->rend = f->buf;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	return readlen;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+}
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+static size_t cookiewrite(FILE *f, const unsigned char *buf, size_t len)
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+{
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	struct fcookie *fc = f->cookie;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	ssize_t ret;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	size_t len2 = f->wpos - f->wbase;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	if (!fc->iofuncs.write) return len;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	if (len2) {
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		f->wpos = f->wbase;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		if (cookiewrite(f, f->wpos, len2) < len2) return 0;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	}
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	ret = fc->iofuncs.write(fc->cookie, (const char *) buf, len);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	if (ret < 0) {
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		f->wpos = f->wbase = f->wend = 0;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		f->flags |= F_ERR;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		return 0;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	}
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	return ret;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+}
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+static off_t cookieseek(FILE *f, off_t off, int whence)
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+{
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	struct fcookie *fc = f->cookie;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	int res;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	if (whence > 2U) {
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		errno = EINVAL;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		return -1;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	}
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	if (!fc->iofuncs.seek) {
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		errno = ENOTSUP;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		return -1;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	}
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	res = fc->iofuncs.seek(fc->cookie, &off, whence);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	if (res < 0)
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		return res;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	return off;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+}
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+static int cookieclose(FILE *f)
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+{
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	struct fcookie *fc = f->cookie;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	if (fc->iofuncs.close) return fc->iofuncs.close(fc->cookie);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	return 0;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+}
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+FILE *fopencookie(void *cookie, const char *mode, cookie_io_functions_t iofuncs)
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+{
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	struct cookie_FILE *f;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	/* Check for valid initial mode character */
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	if (!strchr("rwa", *mode)) {
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		errno = EINVAL;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+		return 0;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	}
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	/* Allocate FILE+fcookie+buffer or fail */
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	if (!(f=malloc(sizeof *f))) return 0;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	/* Zero-fill only the struct, not the buffer */
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	memset(&f->f, 0, sizeof f->f);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	/* Impose mode restrictions */
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	if (!strchr(mode, '+')) f->f.flags = (*mode == 'r') ? F_NOWR : F_NORD;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	/* Set up our fcookie */
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->fc.cookie = cookie;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->fc.iofuncs.read = iofuncs.read;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->fc.iofuncs.write = iofuncs.write;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->fc.iofuncs.seek = iofuncs.seek;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->fc.iofuncs.close = iofuncs.close;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->f.fd = -1;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->f.cookie = &f->fc;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->f.buf = f->buf + UNGET;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->f.buf_size = BUFSIZ;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->f.lbf = EOF;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	/* Initialize op ptrs. No problem if some are unneeded. */
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->f.read = cookieread;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->f.write = cookiewrite;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->f.seek = cookieseek;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	f->f.close = cookieclose;
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	/* Add new FILE to open file list */
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+	return __ofl_add(&f->f);
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
+}
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
-- 
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5
cgit v0.11.2
Lucio Andrés Illanes Albornoz (arab, vxp) ff26b5