1 /*	$OpenBSD: findfp.c,v 1.15 2013/12/17 16:33:27 deraadt Exp $ */
2 /*-
3  * Copyright (c) 1990, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Chris Torek.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include <stdio.h>
35 
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <limits.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/param.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44 
45 #include "local.h"
46 #include "glue.h"
47 #include "private/ErrnoRestorer.h"
48 #include "private/thread_private.h"
49 
50 #define ALIGNBYTES (sizeof(uintptr_t) - 1)
51 #define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES)
52 
53 #define	NDYNAMIC 10		/* add ten more whenever necessary */
54 
55 #define std(flags, file) \
56     {0,0,0,flags,file,{0,0},0,__sF+file,__sclose,__sread,nullptr,__swrite, \
57     {(unsigned char *)(__sFext+file), 0},nullptr,0,{0},{0},{0,0},0,0}
58 
59 _THREAD_PRIVATE_MUTEX(__sfp_mutex);
60 
61 // TODO: when we no longer have to support both clang and GCC, we can simplify all this.
62 #define SBUF_INIT {0,0}
63 #if defined(__LP64__)
64 #define MBSTATE_T_INIT {{0},{0}}
65 #else
66 #define MBSTATE_T_INIT {{0}}
67 #endif
68 #define WCHAR_IO_DATA_INIT {MBSTATE_T_INIT,MBSTATE_T_INIT,{0},0,0}
69 
70 static struct __sfileext __sFext[3] = {
71   { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false, __sseek64 },
72   { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false, __sseek64 },
73   { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false, __sseek64 },
74 };
75 
76 // __sF is exported for backwards compatibility. Until M, we didn't have symbols
77 // for stdin/stdout/stderr; they were macros accessing __sF.
78 FILE __sF[3] = {
79   std(__SRD, STDIN_FILENO),
80   std(__SWR, STDOUT_FILENO),
81   std(__SWR|__SNBF, STDERR_FILENO),
82 };
83 
84 FILE* stdin = &__sF[0];
85 FILE* stdout = &__sF[1];
86 FILE* stderr = &__sF[2];
87 
88 struct glue __sglue = { NULL, 3, __sF };
89 static struct glue* lastglue = &__sglue;
90 
91 class ScopedFileLock {
92  public:
ScopedFileLock(FILE * fp)93   ScopedFileLock(FILE* fp) : fp_(fp) {
94     FLOCKFILE(fp_);
95   }
~ScopedFileLock()96   ~ScopedFileLock() {
97     FUNLOCKFILE(fp_);
98   }
99 
100  private:
101   FILE* fp_;
102 };
103 
moreglue(int n)104 static glue* moreglue(int n) {
105   static FILE empty;
106 
107   char* data = new char[sizeof(glue) + ALIGNBYTES + n * sizeof(FILE) + n * sizeof(__sfileext)];
108   if (data == nullptr) return nullptr;
109 
110   glue* g = reinterpret_cast<glue*>(data);
111   FILE* p = reinterpret_cast<FILE*>(ALIGN(data + sizeof(*g)));
112   __sfileext* pext = reinterpret_cast<__sfileext*>(ALIGN(data + sizeof(*g)) + n * sizeof(FILE));
113   g->next = NULL;
114   g->niobs = n;
115   g->iobs = p;
116   while (--n >= 0) {
117     *p = empty;
118     _FILEEXT_SETUP(p, pext);
119     p++;
120     pext++;
121   }
122   return g;
123 }
124 
125 /*
126  * Find a free FILE for fopen et al.
127  */
__sfp(void)128 FILE* __sfp(void) {
129 	FILE *fp;
130 	int n;
131 	struct glue *g;
132 
133 	_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
134 	for (g = &__sglue; g != NULL; g = g->next) {
135 		for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
136 			if (fp->_flags == 0)
137 				goto found;
138 	}
139 
140 	/* release lock while mallocing */
141 	_THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
142 	if ((g = moreglue(NDYNAMIC)) == NULL)
143 		return (NULL);
144 	_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
145 	lastglue->next = g;
146 	lastglue = g;
147 	fp = g->iobs;
148 found:
149 	fp->_flags = 1;		/* reserve this slot; caller sets real flags */
150 	_THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
151 	fp->_p = NULL;		/* no current pointer */
152 	fp->_w = 0;		/* nothing to read or write */
153 	fp->_r = 0;
154 	fp->_bf._base = NULL;	/* no buffer */
155 	fp->_bf._size = 0;
156 	fp->_lbfsize = 0;	/* not line buffered */
157 	fp->_file = -1;		/* no file */
158 
159 	fp->_lb._base = NULL;	/* no line buffer */
160 	fp->_lb._size = 0;
161 	_FILEEXT_INIT(fp);
162 
163 	// Caller sets cookie, _read/_write etc.
164 	// We explicitly clear _seek and _seek64 to prevent subtle bugs.
165 	fp->_seek = nullptr;
166 	_EXT(fp)->_seek64 = nullptr;
167 
168 	return fp;
169 }
170 
__libc_stdio_cleanup(void)171 extern "C" __LIBC_HIDDEN__ void __libc_stdio_cleanup(void) {
172   // Equivalent to fflush(nullptr), but without all the locking since we're shutting down anyway.
173   _fwalk(__sflush);
174 }
175 
__fopen(int fd,int flags)176 static FILE* __fopen(int fd, int flags) {
177 #if !defined(__LP64__)
178   if (fd > SHRT_MAX) {
179     errno = EMFILE;
180     return nullptr;
181   }
182 #endif
183 
184   FILE* fp = __sfp();
185   if (fp != nullptr) {
186     fp->_file = fd;
187     fp->_flags = flags;
188     fp->_cookie = fp;
189     fp->_read = __sread;
190     fp->_write = __swrite;
191     fp->_close = __sclose;
192     _EXT(fp)->_seek64 = __sseek64;
193   }
194   return fp;
195 }
196 
fopen(const char * file,const char * mode)197 FILE* fopen(const char* file, const char* mode) {
198   int oflags;
199   int flags = __sflags(mode, &oflags);
200   if (flags == 0) return nullptr;
201 
202   int fd = open(file, oflags, DEFFILEMODE);
203   if (fd == -1) {
204     return nullptr;
205   }
206 
207   FILE* fp = __fopen(fd, flags);
208   if (fp == nullptr) {
209     ErrnoRestorer errno_restorer;
210     close(fd);
211     return nullptr;
212   }
213 
214   // When opening in append mode, even though we use O_APPEND,
215   // we need to seek to the end so that ftell() gets the right
216   // answer.  If the user then alters the seek pointer, or
217   // the file extends, this will fail, but there is not much
218   // we can do about this.  (We could set __SAPP and check in
219   // fseek and ftell.)
220   // TODO: check in __sseek instead.
221   if (oflags & O_APPEND) __sseek64(fp, 0, SEEK_END);
222 
223   return fp;
224 }
225 __strong_alias(fopen64, fopen);
226 
fdopen(int fd,const char * mode)227 FILE* fdopen(int fd, const char* mode) {
228   int oflags;
229   int flags = __sflags(mode, &oflags);
230   if (flags == 0) return nullptr;
231 
232   // Make sure the mode the user wants is a subset of the actual mode.
233   int fdflags = fcntl(fd, F_GETFL, 0);
234   if (fdflags < 0) return nullptr;
235   int tmp = fdflags & O_ACCMODE;
236   if (tmp != O_RDWR && (tmp != (oflags & O_ACCMODE))) {
237     errno = EINVAL;
238     return nullptr;
239   }
240 
241   // If opened for appending, but underlying descriptor does not have
242   // O_APPEND bit set, assert __SAPP so that __swrite() will lseek to
243   // end before each write.
244   // TODO: use fcntl(2) to set O_APPEND instead.
245   if ((oflags & O_APPEND) && !(fdflags & O_APPEND)) flags |= __SAPP;
246 
247   // If close-on-exec was requested, then turn it on if not already.
248   if ((oflags & O_CLOEXEC) && !((tmp = fcntl(fd, F_GETFD)) & FD_CLOEXEC)) {
249     fcntl(fd, F_SETFD, tmp | FD_CLOEXEC);
250   }
251 
252   return __fopen(fd, flags);
253 }
254 
255 // Re-direct an existing, open (probably) file to some other file.
256 // ANSI is written such that the original file gets closed if at
257 // all possible, no matter what.
258 // TODO: rewrite this mess completely.
freopen(const char * file,const char * mode,FILE * fp)259 FILE* freopen(const char* file, const char* mode, FILE* fp) {
260   int oflags;
261   int flags = __sflags(mode, &oflags);
262   if (flags == 0) {
263     fclose(fp);
264     return nullptr;
265   }
266 
267   ScopedFileLock sfl(fp);
268 
269   // There are actually programs that depend on being able to "freopen"
270   // descriptors that weren't originally open.  Keep this from breaking.
271   // Remember whether the stream was open to begin with, and which file
272   // descriptor (if any) was associated with it.  If it was attached to
273   // a descriptor, defer closing it; freopen("/dev/stdin", "r", stdin)
274   // should work.  This is unnecessary if it was not a Unix file.
275   int isopen, wantfd;
276   if (fp->_flags == 0) {
277     fp->_flags = __SEOF; // Hold on to it.
278     isopen = 0;
279     wantfd = -1;
280   } else {
281     // Flush the stream; ANSI doesn't require this.
282     if (fp->_flags & __SWR) __sflush(fp);
283 
284     // If close is NULL, closing is a no-op, hence pointless.
285     isopen = fp->_close != NULL;
286     if ((wantfd = fp->_file) < 0 && isopen) {
287         (*fp->_close)(fp->_cookie);
288         isopen = 0;
289     }
290   }
291 
292   // Get a new descriptor to refer to the new file.
293   int fd = open(file, oflags, DEFFILEMODE);
294   if (fd < 0 && isopen) {
295     // If out of fd's close the old one and try again.
296     if (errno == ENFILE || errno == EMFILE) {
297       (*fp->_close)(fp->_cookie);
298       isopen = 0;
299       fd = open(file, oflags, DEFFILEMODE);
300     }
301   }
302 
303   int sverrno = errno;
304 
305   // Finish closing fp.  Even if the open succeeded above, we cannot
306   // keep fp->_base: it may be the wrong size.  This loses the effect
307   // of any setbuffer calls, but stdio has always done this before.
308   if (isopen && fd != wantfd) (*fp->_close)(fp->_cookie);
309   if (fp->_flags & __SMBF) free(fp->_bf._base);
310   fp->_w = 0;
311   fp->_r = 0;
312   fp->_p = NULL;
313   fp->_bf._base = NULL;
314   fp->_bf._size = 0;
315   fp->_lbfsize = 0;
316   if (HASUB(fp)) FREEUB(fp);
317   _UB(fp)._size = 0;
318   WCIO_FREE(fp);
319   if (HASLB(fp)) FREELB(fp);
320   fp->_lb._size = 0;
321 
322   if (fd < 0) { // Did not get it after all.
323     fp->_flags = 0; // Release.
324     errno = sverrno; // Restore errno in case _close clobbered it.
325     return nullptr;
326   }
327 
328   // If reopening something that was open before on a real file, try
329   // to maintain the descriptor.  Various C library routines (perror)
330   // assume stderr is always fd STDERR_FILENO, even if being freopen'd.
331   if (wantfd >= 0 && fd != wantfd) {
332     if (dup3(fd, wantfd, oflags & O_CLOEXEC) >= 0) {
333       close(fd);
334       fd = wantfd;
335     }
336   }
337 
338   // _file is only a short.
339   if (fd > SHRT_MAX) {
340       fp->_flags = 0; // Release.
341       errno = EMFILE;
342       return nullptr;
343   }
344 
345   fp->_flags = flags;
346   fp->_file = fd;
347   fp->_cookie = fp;
348   fp->_read = __sread;
349   fp->_write = __swrite;
350   fp->_close = __sclose;
351   _EXT(fp)->_seek64 = __sseek64;
352 
353   // When opening in append mode, even though we use O_APPEND,
354   // we need to seek to the end so that ftell() gets the right
355   // answer.  If the user then alters the seek pointer, or
356   // the file extends, this will fail, but there is not much
357   // we can do about this.  (We could set __SAPP and check in
358   // fseek and ftell.)
359   if (oflags & O_APPEND) __sseek64(fp, 0, SEEK_END);
360   return fp;
361 }
362 __strong_alias(freopen64, freopen);
363 
fclose(FILE * fp)364 int fclose(FILE* fp) {
365   if (fp->_flags == 0) {
366     // Already freed!
367     errno = EBADF;
368     return EOF;
369   }
370 
371   ScopedFileLock sfl(fp);
372   WCIO_FREE(fp);
373   int r = fp->_flags & __SWR ? __sflush(fp) : 0;
374   if (fp->_close != NULL && (*fp->_close)(fp->_cookie) < 0) {
375     r = EOF;
376   }
377   if (fp->_flags & __SMBF) free(fp->_bf._base);
378   if (HASUB(fp)) FREEUB(fp);
379   if (HASLB(fp)) FREELB(fp);
380 
381   // Poison this FILE so accesses after fclose will be obvious.
382   fp->_file = -1;
383   fp->_r = fp->_w = 0;
384 
385   // Release this FILE for reuse.
386   fp->_flags = 0;
387   return r;
388 }
389 
fileno(FILE * fp)390 int fileno(FILE* fp) {
391   ScopedFileLock sfl(fp);
392   return fileno_unlocked(fp);
393 }
394 
__sread(void * cookie,char * buf,int n)395 int __sread(void* cookie, char* buf, int n) {
396   FILE* fp = reinterpret_cast<FILE*>(cookie);
397   return TEMP_FAILURE_RETRY(read(fp->_file, buf, n));
398 }
399 
__swrite(void * cookie,const char * buf,int n)400 int __swrite(void* cookie, const char* buf, int n) {
401   FILE* fp = reinterpret_cast<FILE*>(cookie);
402   if (fp->_flags & __SAPP) {
403     // The FILE* is in append mode, but the underlying fd doesn't have O_APPEND set.
404     // We need to seek manually.
405     // TODO: use fcntl(2) to set O_APPEND in fdopen(3) instead?
406     TEMP_FAILURE_RETRY(lseek64(fp->_file, 0, SEEK_END));
407   }
408   return TEMP_FAILURE_RETRY(write(fp->_file, buf, n));
409 }
410 
__sseek(void * cookie,fpos_t offset,int whence)411 fpos_t __sseek(void* cookie, fpos_t offset, int whence) {
412   FILE* fp = reinterpret_cast<FILE*>(cookie);
413   return TEMP_FAILURE_RETRY(lseek(fp->_file, offset, whence));
414 }
415 
__sseek64(void * cookie,off64_t offset,int whence)416 off64_t __sseek64(void* cookie, off64_t offset, int whence) {
417   FILE* fp = reinterpret_cast<FILE*>(cookie);
418   return TEMP_FAILURE_RETRY(lseek64(fp->_file, offset, whence));
419 }
420 
__sclose(void * cookie)421 int __sclose(void* cookie) {
422   FILE* fp = reinterpret_cast<FILE*>(cookie);
423   return close(fp->_file);
424 }
425 
__seek_unlocked(FILE * fp,off64_t offset,int whence)426 static off64_t __seek_unlocked(FILE* fp, off64_t offset, int whence) {
427   // Use `_seek64` if set, but fall back to `_seek`.
428   if (_EXT(fp)->_seek64 != nullptr) {
429     return (*_EXT(fp)->_seek64)(fp->_cookie, offset, whence);
430   } else if (fp->_seek != nullptr) {
431     off64_t result = (*fp->_seek)(fp->_cookie, offset, whence);
432 #if !defined(__LP64__)
433     // Avoid sign extension if off64_t is larger than off_t.
434     if (result != -1) result &= 0xffffffff;
435 #endif
436     return result;
437   } else {
438     errno = ESPIPE;
439     return -1;
440   }
441 }
442 
__ftello64_unlocked(FILE * fp)443 static off64_t __ftello64_unlocked(FILE* fp) {
444   // Find offset of underlying I/O object, then adjust for buffered bytes.
445   __sflush(fp);  // May adjust seek offset on append stream.
446   off64_t result = __seek_unlocked(fp, 0, SEEK_CUR);
447   if (result == -1) {
448     return -1;
449   }
450 
451   if (fp->_flags & __SRD) {
452     // Reading.  Any unread characters (including
453     // those from ungetc) cause the position to be
454     // smaller than that in the underlying object.
455     result -= fp->_r;
456     if (HASUB(fp)) result -= fp->_ur;
457   } else if (fp->_flags & __SWR && fp->_p != NULL) {
458     // Writing.  Any buffered characters cause the
459     // position to be greater than that in the
460     // underlying object.
461     result += fp->_p - fp->_bf._base;
462   }
463   return result;
464 }
465 
__fseeko64(FILE * fp,off64_t offset,int whence,int off_t_bits)466 int __fseeko64(FILE* fp, off64_t offset, int whence, int off_t_bits) {
467   ScopedFileLock sfl(fp);
468 
469   // Change any SEEK_CUR to SEEK_SET, and check `whence` argument.
470   // After this, whence is either SEEK_SET or SEEK_END.
471   if (whence == SEEK_CUR) {
472     fpos64_t current_offset = __ftello64_unlocked(fp);
473     if (current_offset == -1) {
474       return -1;
475     }
476     offset += current_offset;
477     whence = SEEK_SET;
478   } else if (whence != SEEK_SET && whence != SEEK_END) {
479     errno = EINVAL;
480     return -1;
481   }
482 
483   // If our caller has a 32-bit interface, refuse to go past a 32-bit file offset.
484   if (off_t_bits == 32 && offset > LONG_MAX) {
485     errno = EOVERFLOW;
486     return -1;
487   }
488 
489   if (fp->_bf._base == NULL) __smakebuf(fp);
490 
491   // Flush unwritten data and attempt the seek.
492   if (__sflush(fp) || __seek_unlocked(fp, offset, whence) == -1) {
493     return -1;
494   }
495 
496   // Success: clear EOF indicator and discard ungetc() data.
497   if (HASUB(fp)) FREEUB(fp);
498   fp->_p = fp->_bf._base;
499   fp->_r = 0;
500   /* fp->_w = 0; */	/* unnecessary (I think...) */
501   fp->_flags &= ~__SEOF;
502   return 0;
503 }
504 
fseeko(FILE * fp,off_t offset,int whence)505 int fseeko(FILE* fp, off_t offset, int whence) {
506   static_assert(sizeof(off_t) == sizeof(long), "sizeof(off_t) != sizeof(long)");
507   return __fseeko64(fp, offset, whence, 8*sizeof(off_t));
508 }
509 __strong_alias(fseek, fseeko);
510 
fseeko64(FILE * fp,off64_t offset,int whence)511 int fseeko64(FILE* fp, off64_t offset, int whence) {
512   return __fseeko64(fp, offset, whence, 8*sizeof(off_t));
513 }
514 
fsetpos(FILE * fp,const fpos_t * pos)515 int fsetpos(FILE* fp, const fpos_t* pos) {
516   return fseeko(fp, *pos, SEEK_SET);
517 }
518 
fsetpos64(FILE * fp,const fpos64_t * pos)519 int fsetpos64(FILE* fp, const fpos64_t* pos) {
520   return fseeko64(fp, *pos, SEEK_SET);
521 }
522 
ftello(FILE * fp)523 off_t ftello(FILE* fp) {
524   static_assert(sizeof(off_t) == sizeof(long), "sizeof(off_t) != sizeof(long)");
525   off64_t result = ftello64(fp);
526   if (result > LONG_MAX) {
527     errno = EOVERFLOW;
528     return -1;
529   }
530   return result;
531 }
532 __strong_alias(ftell, ftello);
533 
ftello64(FILE * fp)534 off64_t ftello64(FILE* fp) {
535   ScopedFileLock sfl(fp);
536   return __ftello64_unlocked(fp);
537 }
538 
fgetpos(FILE * fp,fpos_t * pos)539 int fgetpos(FILE* fp, fpos_t* pos) {
540   *pos = ftello(fp);
541   return (*pos == -1) ? -1 : 0;
542 }
543 
fgetpos64(FILE * fp,fpos64_t * pos)544 int fgetpos64(FILE* fp, fpos64_t* pos) {
545   *pos = ftello64(fp);
546   return (*pos == -1) ? -1 : 0;
547 }
548 
__funopen(const void * cookie,int (* read_fn)(void *,char *,int),int (* write_fn)(void *,const char *,int),int (* close_fn)(void *))549 static FILE* __funopen(const void* cookie,
550                        int (*read_fn)(void*, char*, int),
551                        int (*write_fn)(void*, const char*, int),
552                        int (*close_fn)(void*)) {
553   if (read_fn == nullptr && write_fn == nullptr) {
554     errno = EINVAL;
555     return nullptr;
556   }
557 
558   FILE* fp = __sfp();
559   if (fp == nullptr) return nullptr;
560 
561   if (read_fn != nullptr && write_fn != nullptr) {
562     fp->_flags = __SRW;
563   } else if (read_fn != nullptr) {
564     fp->_flags = __SRD;
565   } else if (write_fn != nullptr) {
566     fp->_flags = __SWR;
567   }
568 
569   fp->_file = -1;
570   fp->_cookie = const_cast<void*>(cookie); // The funopen(3) API is incoherent.
571   fp->_read = read_fn;
572   fp->_write = write_fn;
573   fp->_close = close_fn;
574 
575   return fp;
576 }
577 
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 *))578 FILE* funopen(const void* cookie,
579               int (*read_fn)(void*, char*, int),
580               int (*write_fn)(void*, const char*, int),
581               fpos_t (*seek_fn)(void*, fpos_t, int),
582               int (*close_fn)(void*)) {
583   FILE* fp = __funopen(cookie, read_fn, write_fn, close_fn);
584   if (fp != nullptr) {
585     fp->_seek = seek_fn;
586   }
587   return fp;
588 }
589 
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 *))590 FILE* funopen64(const void* cookie,
591                 int (*read_fn)(void*, char*, int),
592                 int (*write_fn)(void*, const char*, int),
593                 fpos64_t (*seek_fn)(void*, fpos64_t, int),
594                 int (*close_fn)(void*)) {
595   FILE* fp = __funopen(cookie, read_fn, write_fn, close_fn);
596   if (fp != nullptr) {
597     _EXT(fp)->_seek64 = seek_fn;
598   }
599   return fp;
600 }
601