From 47c6f1b4332a9e4935c48cca826786a6b8fe6f59 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Neal=20Gompa=20=28=E3=83=8B=E3=83=BC=E3=83=AB=E3=83=BB?=
 =?UTF-8?q?=E3=82=B3=E3=82=99=E3=83=B3=E3=83=8F=E3=82=9A=29?=
 <ngompa13@gmail.com>
Date: Wed, 11 Nov 2015 20:32:17 -0500
Subject: [PATCH 1/2] Add fallback fopencookie() implementation

In environments where neither fopencookie() nor funopen()
are implemented, we need to provide a suitable implementation
of fopencookie() that we can use.

Alex Kanavin: rebased CMakeLists.txt change to apply to latest upstream code.

Upstream-Status: Denied [https://github.com/openSUSE/libsolv/pull/112]
Signed-off-by: Alexander Kanavin <alex.kanavin@gmail.com>

---
 ext/CMakeLists.txt                     |   7 ++
 ext/solv_xfopen.c                      |  10 +--
 ext/solv_xfopen_fallback_fopencookie.c | 123 +++++++++++++++++++++++++++++++++
 ext/solv_xfopen_fallback_fopencookie.h |  28 ++++++++
 4 files changed, 164 insertions(+), 4 deletions(-)
 create mode 100644 ext/solv_xfopen_fallback_fopencookie.c
 create mode 100644 ext/solv_xfopen_fallback_fopencookie.h

diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt
index b8917a2..fac6c32 100644
--- a/ext/CMakeLists.txt
+++ b/ext/CMakeLists.txt
@@ -4,6 +4,13 @@ SET (libsolvext_SRCS
 SET (libsolvext_HEADERS
     tools_util.h solv_xfopen.h testcase.h)
 
+IF (NOT HAVE_FOPENCOOKIE AND NOT HAVE_FUNOPEN)
+    SET (libsolvext_SRCS ${libsolvext_SRCS}
+        solv_xfopen_fallback_fopencookie.c)
+    SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+        solv_xfopen_fallback_fopencookie.h)
+ENDIF (NOT HAVE_FOPENCOOKIE AND NOT HAVE_FUNOPEN)
+
 IF (ENABLE_RPMDB OR ENABLE_RPMPKG)
     SET (libsolvext_SRCS ${libsolvext_SRCS}
         pool_fileconflicts.c repo_rpmdb.c)
diff --git a/ext/solv_xfopen.c b/ext/solv_xfopen.c
index 2c64bb6..eb3a3ad 100644
--- a/ext/solv_xfopen.c
+++ b/ext/solv_xfopen.c
@@ -12,6 +12,10 @@
 #include <string.h>
 #include <fcntl.h>
 
+#if !defined(HAVE_FUNOPEN) && !defined(HAVE_FOPENCOOKIE)
+#include "solv_xfopen_fallback_fopencookie.h"
+#endif
+
 #include "solv_xfopen.h"
 #include "util.h"
 
@@ -21,7 +25,7 @@ static FILE *cookieopen(void *cookie, const char *mode,
 	ssize_t (*cwrite)(void *, const char *, size_t),
 	int (*cclose)(void *))
 {
-#ifdef HAVE_FUNOPEN
+#if defined(HAVE_FUNOPEN) && !defined(HAVE_FOPENCOOKIE)
   if (!cookie)
     return 0;
   return funopen(cookie,
@@ -30,7 +34,7 @@ static FILE *cookieopen(void *cookie, const char *mode,
       (fpos_t (*)(void *, fpos_t, int))NULL,					/* seekfn */
       cclose
       );
-#elif defined(HAVE_FOPENCOOKIE)
+#else
   cookie_io_functions_t cio;
 
   if (!cookie)
@@ -42,8 +46,6 @@ static FILE *cookieopen(void *cookie, const char *mode,
     cio.write = cwrite;
   cio.close = cclose;
   return  fopencookie(cookie, *mode == 'w' ? "w" : "r", cio);
-#else
-# error Need to implement custom I/O
 #endif
 }
 
diff --git a/ext/solv_xfopen_fallback_fopencookie.c b/ext/solv_xfopen_fallback_fopencookie.c
new file mode 100644
index 0000000..0ce2571
--- /dev/null
+++ b/ext/solv_xfopen_fallback_fopencookie.c
@@ -0,0 +1,123 @@
+/* 
+ *	Provides a very limited fopencookie() for environments with a libc
+ *	that lacks it.
+ *	
+ *	Author: zhasha
+ *	Modified for libsolv by Neal Gompa
+ *	
+ *	This program is licensed under the BSD license, read LICENSE.BSD
+ *	for further information.
+ *
+ */
+
+#define _LARGEFILE64_SOURCE 1
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <errno.h>
+#include "solv_xfopen_fallback_fopencookie.h"
+
+extern int pipe2(int[2], int);
+
+struct ctx {
+    int fd;
+    void *cookie;
+    struct cookie_io_functions_t io;
+    char buf[1024];
+};
+
+static void *proxy(void *arg)
+{
+    struct ctx *ctx = arg;
+    ssize_t r;
+    size_t n;
+
+    pthread_detach(pthread_self());
+
+    while (1) {
+        r = ctx->io.read ?
+            (ctx->io.read)(ctx->cookie, ctx->buf, sizeof(ctx->buf)) :
+            read(ctx->fd, ctx->buf, sizeof(ctx->buf));
+        if (r < 0) {
+            if (errno != EINTR) { break; }
+            continue;
+        }
+        if (r == 0) { break; }
+
+        while (n > 0) {
+            r = ctx->io.write ?
+                (ctx->io.write)(ctx->cookie, ctx->buf + ((size_t)r - n), n) :
+                write(ctx->fd, ctx->buf + ((size_t)r - n), n);
+            if (r < 0) {
+                if (errno != EINTR) { break; }
+                continue;
+            }
+            if (r == 0) { break; }
+
+            n -= (size_t)r;
+        }
+        if (n > 0) { break; }
+    }
+
+    if (ctx->io.close) { (ctx->io.close)(ctx->cookie); }
+    close(ctx->fd);
+    return NULL;
+}
+
+FILE *fopencookie(void *cookie, const char *mode, struct cookie_io_functions_t io)
+{
+    struct ctx *ctx = NULL;
+    int rd = 0, wr = 0;
+    int p[2] = { -1, -1 };
+    FILE *f = NULL;
+    pthread_t dummy;
+
+    switch (mode[0]) {
+        case 'a':
+        case 'r': rd = 1; break;
+        case 'w': wr = 1; break;
+        default:
+            errno = EINVAL;
+            return NULL;
+    }
+    switch (mode[1]) {
+        case '\0': break;
+        case '+':
+            if (mode[2] == '\0') {
+                errno = ENOTSUP;
+                return NULL;
+            }
+        default:
+            errno = EINVAL;
+            return NULL;
+    }
+    if (io.seek) {
+        errno = ENOTSUP;
+        return NULL;
+    }
+
+    ctx = malloc(sizeof(*ctx));
+    if (!ctx) { return NULL; }
+    if (pipe2(p, O_CLOEXEC) != 0) { goto err; }
+    if ((f = fdopen(p[wr], mode)) == NULL) { goto err; }
+    p[wr] = -1;
+    ctx->fd = p[rd];
+    ctx->cookie = cookie;
+    ctx->io.read = rd ? io.read : NULL;
+    ctx->io.write = wr ? io.write : NULL;
+    ctx->io.seek = NULL;
+    ctx->io.close = io.close;
+    if (pthread_create(&dummy, NULL, proxy, ctx) != 0) { goto err; }
+
+    return f;
+
+err:
+    if (p[0] >= 0) { close(p[0]); }
+    if (p[1] >= 0) { close(p[1]); }
+    if (f) { fclose(f); }
+    free(ctx);
+    return NULL;
+}
diff --git a/ext/solv_xfopen_fallback_fopencookie.h b/ext/solv_xfopen_fallback_fopencookie.h
new file mode 100644
index 0000000..6a7bfee
--- /dev/null
+++ b/ext/solv_xfopen_fallback_fopencookie.h
@@ -0,0 +1,28 @@
+/* 
+ *	Provides a very limited fopencookie() for environments with a libc
+ *	that lacks it.
+ *	
+ *	Author: zhasha
+ *	Modified for libsolv by Neal Gompa
+ *	
+ *	This program is licensed under the BSD license, read LICENSE.BSD
+ *	for further information.
+ *	
+ */
+
+#ifndef SOLV_XFOPEN_FALLBACK_FOPENCOOKIE_H
+#define SOLV_XFOPEN_FALLBACK_FOPENCOOKIE_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+typedef struct cookie_io_functions_t {
+    ssize_t (*read)(void *, char *, size_t);
+    ssize_t (*write)(void *, const char *, size_t);
+    int (*seek)(void *, off64_t, int);
+    int (*close)(void *);
+} cookie_io_functions_t;
+
+FILE *fopencookie(void *cookie, const char *mode, struct cookie_io_functions_t io);
+
+#endif
-- 
2.4.0

