Implement funopen64.

Bug: http://b/24807045
Change-Id: I161920978161389be34b707cc6ce8e05f760d552
diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp
index 23b6971..2139621 100644
--- a/libc/stdio/stdio.cpp
+++ b/libc/stdio/stdio.cpp
@@ -538,3 +538,56 @@
   *pos = ftello64(fp);
   return (*pos == -1);
 }
+
+static FILE* __funopen(const void* cookie,
+                       int (*read_fn)(void*, char*, int),
+                       int (*write_fn)(void*, const char*, int),
+                       int (*close_fn)(void*)) {
+  if (read_fn == nullptr && write_fn == nullptr) {
+    errno = EINVAL;
+    return nullptr;
+  }
+
+  FILE* fp = __sfp();
+  if (fp == nullptr) return nullptr;
+
+  if (read_fn != nullptr && write_fn != nullptr) {
+    fp->_flags = __SRW;
+  } else if (read_fn != nullptr) {
+    fp->_flags = __SRD;
+  } else if (write_fn != nullptr) {
+    fp->_flags = __SWR;
+  }
+
+  fp->_file = -1;
+  fp->_cookie = const_cast<void*>(cookie); // The funopen(3) API is incoherent.
+  fp->_read = read_fn;
+  fp->_write = write_fn;
+  fp->_close = close_fn;
+
+  return fp;
+}
+
+FILE* funopen(const void* cookie,
+              int (*read_fn)(void*, char*, int),
+              int (*write_fn)(void*, const char*, int),
+              fpos_t (*seek_fn)(void*, fpos_t, int),
+              int (*close_fn)(void*)) {
+  FILE* fp = __funopen(cookie, read_fn, write_fn, close_fn);
+  if (fp != nullptr) {
+    fp->_seek = seek_fn;
+  }
+  return fp;
+}
+
+FILE* funopen64(const void* cookie,
+                int (*read_fn)(void*, char*, int),
+                int (*write_fn)(void*, const char*, int),
+                fpos64_t (*seek_fn)(void*, fpos64_t, int),
+                int (*close_fn)(void*)) {
+  FILE* fp = __funopen(cookie, read_fn, write_fn, close_fn);
+  if (fp != nullptr) {
+    _EXT(fp)->_seek64 = seek_fn;
+  }
+  return fp;
+}