From 2704bd13409a77237147f861c43796148326b2e3 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 20 Jan 2016 17:14:53 -0800 Subject: [PATCH] Simplify fseek/ftell. Another step towards _FILE_OFFSET_BITS=64 support. Bug: http://b/24807045 Change-Id: I00b83c81a7b108176c4d9437bc32611f73b7e967 --- libc/Android.mk | 2 - libc/include/stdio.h | 6 +- libc/stdio/local.h | 5 +- libc/stdio/stdio.cpp | 232 +++++++++++------ libc/upstream-openbsd/lib/libc/stdio/fseek.c | 251 ------------------- libc/upstream-openbsd/lib/libc/stdio/ftell.c | 96 ------- tests/stdio_test.cpp | 25 ++ 7 files changed, 192 insertions(+), 425 deletions(-) delete mode 100644 libc/upstream-openbsd/lib/libc/stdio/fseek.c delete mode 100644 libc/upstream-openbsd/lib/libc/stdio/ftell.c diff --git a/libc/Android.mk b/libc/Android.mk index 260964451..85a58e753 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -464,9 +464,7 @@ libc_upstream_openbsd_ndk_src_files := \ upstream-openbsd/lib/libc/stdio/fputws.c \ upstream-openbsd/lib/libc/stdio/freopen.c \ upstream-openbsd/lib/libc/stdio/fscanf.c \ - upstream-openbsd/lib/libc/stdio/fseek.c \ upstream-openbsd/lib/libc/stdio/fsetpos.c \ - upstream-openbsd/lib/libc/stdio/ftell.c \ upstream-openbsd/lib/libc/stdio/funopen.c \ upstream-openbsd/lib/libc/stdio/fvwrite.c \ upstream-openbsd/lib/libc/stdio/fwalk.c \ diff --git a/libc/include/stdio.h b/libc/include/stdio.h index fd653d9f4..268db78d5 100644 --- a/libc/include/stdio.h +++ b/libc/include/stdio.h @@ -73,9 +73,9 @@ extern FILE* stderr; #define __SMBF 0x0080 /* _buf is from malloc */ #define __SAPP 0x0100 /* fdopen()ed in append mode */ #define __SSTR 0x0200 /* this is an sprintf/snprintf string */ -#define __SOPT 0x0400 /* do fseek() optimization */ -#define __SNPT 0x0800 /* do not do fseek() optimization */ -#define __SOFF 0x1000 /* set iff _offset is in fact correct */ +/* #define __SOPT 0x0400 --- historical (do fseek() optimization). */ +/* #define __SNPT 0x0800 --- historical (do not do fseek() optimization). */ +/* #define __SOFF 0x1000 --- historical (set iff _offset is in fact correct). */ #define __SMOD 0x2000 /* true => fgetln modified _p text */ #define __SALC 0x4000 /* allocate string space dynamically */ #define __SIGN 0x8000 /* ignore this file in _fwalk */ diff --git a/libc/stdio/local.h b/libc/stdio/local.h index a15bddf9a..7188c7422 100644 --- a/libc/stdio/local.h +++ b/libc/stdio/local.h @@ -92,7 +92,6 @@ struct __sFILE { /* Unix stdio files get aligned to block boundaries on fseek() */ int _blksize; /* stat.st_blksize (may be != _bf._size) */ - fpos_t _offset; /* current lseek offset */ }; /* @@ -112,6 +111,10 @@ struct __sfileext { bool _caller_handles_locking; }; +// TODO: remove remaining references to these obsolete flags. +#define __SNPT 0 +#define __SOPT 0 + #if defined(__cplusplus) #define _EXT(fp) reinterpret_cast<__sfileext*>((fp)->_ext._base) #else diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp index 1ea3ef574..1066d7fc9 100644 --- a/libc/stdio/stdio.cpp +++ b/libc/stdio/stdio.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -51,7 +52,7 @@ #define std(flags, file) \ {0,0,0,flags,file,{0,0},0,__sF+file,__sclose,__sread,__sseek,__swrite, \ - {(unsigned char *)(__sFext+file), 0},NULL,0,{0},{0},{0,0},0,0} + {(unsigned char *)(__sFext+file), 0},NULL,0,{0},{0},{0,0},0} _THREAD_PRIVATE_MUTEX(__sfp_mutex); @@ -85,25 +86,38 @@ FILE* stderr = &__sF[2]; struct glue __sglue = { NULL, 3, __sF }; static struct glue* lastglue = &__sglue; +class ScopedFileLock { + public: + ScopedFileLock(FILE* fp) : fp_(fp) { + FLOCKFILE(fp_); + } + ~ScopedFileLock() { + FUNLOCKFILE(fp_); + } + + private: + FILE* fp_; +}; + static glue* moreglue(int n) { - static FILE empty; + static FILE empty; - char* data = new char[sizeof(glue) + ALIGNBYTES + n * sizeof(FILE) + n * sizeof(__sfileext)]; - if (data == nullptr) return nullptr; + char* data = new char[sizeof(glue) + ALIGNBYTES + n * sizeof(FILE) + n * sizeof(__sfileext)]; + if (data == nullptr) return nullptr; - glue* g = reinterpret_cast(data); - FILE* p = reinterpret_cast(ALIGN(data + sizeof(*g))); - __sfileext* pext = reinterpret_cast<__sfileext*>(ALIGN(data + sizeof(*g)) + n * sizeof(FILE)); - g->next = NULL; - g->niobs = n; - g->iobs = p; - while (--n >= 0) { - *p = empty; - _FILEEXT_SETUP(p, pext); - p++; - pext++; - } - return g; + glue* g = reinterpret_cast(data); + FILE* p = reinterpret_cast(ALIGN(data + sizeof(*g))); + __sfileext* pext = reinterpret_cast<__sfileext*>(ALIGN(data + sizeof(*g)) + n * sizeof(FILE)); + g->next = NULL; + g->niobs = n; + g->iobs = p; + while (--n >= 0) { + *p = empty; + _FILEEXT_SETUP(p, pext); + p++; + pext++; + } + return g; } /* @@ -147,81 +161,155 @@ found: } extern "C" __LIBC_HIDDEN__ void __libc_stdio_cleanup(void) { - /* (void) _fwalk(fclose); */ - (void) _fwalk(__sflush); /* `cheating' */ + // Equivalent to fflush(nullptr), but without all the locking since we're shutting down anyway. + _fwalk(__sflush); } int fclose(FILE* fp) { - if (fp->_flags == 0) { - // Already freed! - errno = EBADF; - return EOF; - } + if (fp->_flags == 0) { + // Already freed! + errno = EBADF; + return EOF; + } - FLOCKFILE(fp); - WCIO_FREE(fp); - int r = fp->_flags & __SWR ? __sflush(fp) : 0; - if (fp->_close != NULL && (*fp->_close)(fp->_cookie) < 0) { - r = EOF; - } - if (fp->_flags & __SMBF) free(fp->_bf._base); - if (HASUB(fp)) FREEUB(fp); - if (HASLB(fp)) FREELB(fp); + ScopedFileLock sfl(fp); + WCIO_FREE(fp); + int r = fp->_flags & __SWR ? __sflush(fp) : 0; + if (fp->_close != NULL && (*fp->_close)(fp->_cookie) < 0) { + r = EOF; + } + if (fp->_flags & __SMBF) free(fp->_bf._base); + if (HASUB(fp)) FREEUB(fp); + if (HASLB(fp)) FREELB(fp); - // Poison this FILE so accesses after fclose will be obvious. - fp->_file = -1; - fp->_r = fp->_w = 0; + // Poison this FILE so accesses after fclose will be obvious. + fp->_file = -1; + fp->_r = fp->_w = 0; - // Release this FILE for reuse. - fp->_flags = 0; - FUNLOCKFILE(fp); - return (r); + // Release this FILE for reuse. + fp->_flags = 0; + return r; } int fileno(FILE* fp) { - FLOCKFILE(fp); - int result = fileno_unlocked(fp); - FUNLOCKFILE(fp); - return result; + ScopedFileLock sfl(fp); + return fileno_unlocked(fp); } -// Small standard I/O/seek/close functions. -// These maintain the `known seek offset' for seek optimisation. int __sread(void* cookie, char* buf, int n) { - FILE* fp = reinterpret_cast(cookie); - int ret = TEMP_FAILURE_RETRY(read(fp->_file, buf, n)); - // If the read succeeded, update the current offset. - if (ret >= 0) { - fp->_offset += ret; - } else { - fp->_flags &= ~__SOFF; // Paranoia. - } - return ret; + FILE* fp = reinterpret_cast(cookie); + return TEMP_FAILURE_RETRY(read(fp->_file, buf, n)); } int __swrite(void* cookie, const char* buf, int n) { - FILE* fp = reinterpret_cast(cookie); - if (fp->_flags & __SAPP) { - (void) TEMP_FAILURE_RETRY(lseek(fp->_file, 0, SEEK_END)); - } - fp->_flags &= ~__SOFF; // In case FAPPEND mode is set. - return TEMP_FAILURE_RETRY(write(fp->_file, buf, n)); + FILE* fp = reinterpret_cast(cookie); + if (fp->_flags & __SAPP) { + // The FILE* is in append mode, but the underlying fd doesn't have O_APPEND set. + // We need to seek manually. + // TODO: can we use fcntl(2) to set O_APPEND in fdopen(3) instead? + TEMP_FAILURE_RETRY(lseek64(fp->_file, 0, SEEK_END)); + } + return TEMP_FAILURE_RETRY(write(fp->_file, buf, n)); } // TODO: _FILE_OFFSET_BITS=64. fpos_t __sseek(void* cookie, fpos_t offset, int whence) { - FILE* fp = reinterpret_cast(cookie); - off_t ret = TEMP_FAILURE_RETRY(lseek(fp->_file, offset, whence)); - if (ret == -1) { - fp->_flags &= ~__SOFF; - } else { - fp->_flags |= __SOFF; - fp->_offset = ret; - } - return ret; + FILE* fp = reinterpret_cast(cookie); + return TEMP_FAILURE_RETRY(lseek(fp->_file, offset, whence)); } int __sclose(void* cookie) { - FILE* fp = reinterpret_cast(cookie); - return close(fp->_file); + FILE* fp = reinterpret_cast(cookie); + return close(fp->_file); +} + +static bool __file_is_seekable(FILE* fp) { + if (fp->_seek == nullptr) { + errno = ESPIPE; // Historic practice. + return false; + } + return true; +} + +// TODO: _FILE_OFFSET_BITS=64. +static off_t __ftello_unlocked(FILE* fp) { + if (!__file_is_seekable(fp)) return -1; + + // Find offset of underlying I/O object, then + // adjust for buffered bytes. + __sflush(fp); // May adjust seek offset on append stream. + fpos_t result = (*fp->_seek)(fp->_cookie, 0, SEEK_CUR); + if (result == -1) { + return -1; + } + + if (fp->_flags & __SRD) { + // Reading. Any unread characters (including + // those from ungetc) cause the position to be + // smaller than that in the underlying object. + result -= fp->_r; + if (HASUB(fp)) result -= fp->_ur; + } else if (fp->_flags & __SWR && fp->_p != NULL) { + // Writing. Any buffered characters cause the + // position to be greater than that in the + // underlying object. + result += fp->_p - fp->_bf._base; + } + return result; +} + +int fseeko(FILE* fp, off_t offset, int whence) { + ScopedFileLock sfl(fp); + + if (!__file_is_seekable(fp)) return -1; + + // Change any SEEK_CUR to SEEK_SET, and check `whence' argument. + // After this, whence is either SEEK_SET or SEEK_END. + if (whence == SEEK_CUR) { + fpos_t current_offset = __ftello_unlocked(fp); + if (current_offset == -1) { + return EOF; + } + offset += current_offset; + whence = SEEK_SET; + } else if (whence != SEEK_SET && whence != SEEK_END) { + errno = EINVAL; + return EOF; + } + + if (fp->_bf._base == NULL) __smakebuf(fp); + + // Flush unwritten data and attempt the seek. + if (__sflush(fp) || (*fp->_seek)(fp->_cookie, offset, whence) == -1) { + return EOF; + } + + // Success: clear EOF indicator and discard ungetc() data. + if (HASUB(fp)) FREEUB(fp); + fp->_p = fp->_bf._base; + fp->_r = 0; + /* fp->_w = 0; */ /* unnecessary (I think...) */ + fp->_flags &= ~__SEOF; + return 0; +} + +// TODO: _FILE_OFFSET_BITS=64. +int fseek(FILE* fp, long offset, int whence) { + return fseeko(fp, offset, whence); +} + +// TODO: _FILE_OFFSET_BITS=64. +off_t ftello(FILE* fp) { + ScopedFileLock sfl(fp); + return __ftello_unlocked(fp); +} + +// TODO: _FILE_OFFSET_BITS=64 +long ftell(FILE* fp) { + off_t offset = ftello(fp); + if (offset > LONG_MAX) { + errno = EOVERFLOW; + return -1; + } + return offset; } diff --git a/libc/upstream-openbsd/lib/libc/stdio/fseek.c b/libc/upstream-openbsd/lib/libc/stdio/fseek.c deleted file mode 100644 index cdd40b62f..000000000 --- a/libc/upstream-openbsd/lib/libc/stdio/fseek.c +++ /dev/null @@ -1,251 +0,0 @@ -/* $OpenBSD: fseek.c,v 1.11 2012/05/21 22:24:19 matthew Exp $ */ -/*- - * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Chris Torek. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include "local.h" - -#define POS_ERR (-(fpos_t)1) - -/* - * Seek the given file to the given offset. - * `Whence' must be one of the three SEEK_* macros. - */ -int -fseeko(FILE *fp, off_t offset, int whence) -{ - fpos_t (*seekfn)(void *, fpos_t, int); - fpos_t target, curoff; - size_t n; - struct stat st; - int havepos; - - /* make sure stdio is set up */ - if (!__sdidinit) - __sinit(); - - /* - * Have to be able to seek. - */ - if ((seekfn = fp->_seek) == NULL) { - errno = ESPIPE; /* historic practice */ - return (EOF); - } - - /* - * Change any SEEK_CUR to SEEK_SET, and check `whence' argument. - * After this, whence is either SEEK_SET or SEEK_END. - */ - FLOCKFILE(fp); - switch (whence) { - - case SEEK_CUR: - /* - * In order to seek relative to the current stream offset, - * we have to first find the current stream offset a la - * ftell (see ftell for details). - */ - __sflush(fp); /* may adjust seek offset on append stream */ - if (fp->_flags & __SOFF) - curoff = fp->_offset; - else { - curoff = (*seekfn)(fp->_cookie, (fpos_t)0, SEEK_CUR); - if (curoff == (fpos_t)-1) { - FUNLOCKFILE(fp); - return (EOF); - } - } - if (fp->_flags & __SRD) { - curoff -= fp->_r; - if (HASUB(fp)) - curoff -= fp->_ur; - } else if (fp->_flags & __SWR && fp->_p != NULL) - curoff += fp->_p - fp->_bf._base; - - offset += curoff; - whence = SEEK_SET; - havepos = 1; - break; - - case SEEK_SET: - case SEEK_END: - curoff = 0; /* XXX just to keep gcc quiet */ - havepos = 0; - break; - - default: - FUNLOCKFILE(fp); - errno = EINVAL; - return (EOF); - } - - /* - * Can only optimise if: - * reading (and not reading-and-writing); - * not unbuffered; and - * this is a `regular' Unix file (and hence seekfn==__sseek). - * We must check __NBF first, because it is possible to have __NBF - * and __SOPT both set. - */ - if (fp->_bf._base == NULL) - __smakebuf(fp); - if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT)) - goto dumb; - if ((fp->_flags & __SOPT) == 0) { - if (seekfn != __sseek || - fp->_file < 0 || fstat(fp->_file, &st) || - (st.st_mode & S_IFMT) != S_IFREG) { - fp->_flags |= __SNPT; - goto dumb; - } - fp->_blksize = st.st_blksize; - fp->_flags |= __SOPT; - } - - /* - * We are reading; we can try to optimise. - * Figure out where we are going and where we are now. - */ - if (whence == SEEK_SET) - target = offset; - else { - if (fstat(fp->_file, &st)) - goto dumb; - target = st.st_size + offset; - } - - if (!havepos) { - if (fp->_flags & __SOFF) - curoff = fp->_offset; - else { - curoff = (*seekfn)(fp->_cookie, (fpos_t)0, SEEK_CUR); - if (curoff == POS_ERR) - goto dumb; - } - curoff -= fp->_r; - if (HASUB(fp)) - curoff -= fp->_ur; - } - - /* - * Compute the number of bytes in the input buffer (pretending - * that any ungetc() input has been discarded). Adjust current - * offset backwards by this count so that it represents the - * file offset for the first byte in the current input buffer. - */ - if (HASUB(fp)) { - curoff += fp->_r; /* kill off ungetc */ - n = fp->_up - fp->_bf._base; - curoff -= n; - n += fp->_ur; - } else { - n = fp->_p - fp->_bf._base; - curoff -= n; - n += fp->_r; - } - - /* - * If the target offset is within the current buffer, - * simply adjust the pointers, clear EOF, undo ungetc(), - * and return. (If the buffer was modified, we have to - * skip this; see fgetln.c.) - */ - if ((fp->_flags & __SMOD) == 0 && - target >= curoff && target < curoff + n) { - int o = target - curoff; - - fp->_p = fp->_bf._base + o; - fp->_r = n - o; - if (HASUB(fp)) - FREEUB(fp); - fp->_flags &= ~__SEOF; - FUNLOCKFILE(fp); - return (0); - } - - /* - * The place we want to get to is not within the current buffer, - * but we can still be kind to the kernel copyout mechanism. - * By aligning the file offset to a block boundary, we can let - * the kernel use the VM hardware to map pages instead of - * copying bytes laboriously. Using a block boundary also - * ensures that we only read one block, rather than two. - */ - curoff = target & ~(fp->_blksize - 1); - if ((*seekfn)(fp->_cookie, curoff, SEEK_SET) == POS_ERR) - goto dumb; - fp->_r = 0; - fp->_p = fp->_bf._base; - if (HASUB(fp)) - FREEUB(fp); - fp->_flags &= ~__SEOF; - n = target - curoff; - if (n) { - if (__srefill(fp) || fp->_r < n) - goto dumb; - fp->_p += n; - fp->_r -= n; - } - FUNLOCKFILE(fp); - return (0); - - /* - * We get here if we cannot optimise the seek ... just - * do it. Allow the seek function to change fp->_bf._base. - */ -dumb: - if (__sflush(fp) || - (*seekfn)(fp->_cookie, (fpos_t)offset, whence) == POS_ERR) { - FUNLOCKFILE(fp); - return (EOF); - } - /* success: clear EOF indicator and discard ungetc() data */ - if (HASUB(fp)) - FREEUB(fp); - fp->_p = fp->_bf._base; - fp->_r = 0; - /* fp->_w = 0; */ /* unnecessary (I think...) */ - fp->_flags &= ~__SEOF; - FUNLOCKFILE(fp); - return (0); -} - -int -fseek(FILE *fp, long offset, int whence) -{ - return (fseeko(fp, offset, whence)); -} diff --git a/libc/upstream-openbsd/lib/libc/stdio/ftell.c b/libc/upstream-openbsd/lib/libc/stdio/ftell.c deleted file mode 100644 index 0a2016ce1..000000000 --- a/libc/upstream-openbsd/lib/libc/stdio/ftell.c +++ /dev/null @@ -1,96 +0,0 @@ -/* $OpenBSD: ftell.c,v 1.10 2012/05/21 22:24:19 matthew Exp $ */ -/*- - * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Chris Torek. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include -#include "local.h" - -/* - * ftello: return current offset. - */ -off_t -ftello(FILE *fp) -{ - fpos_t pos; - - if (fp->_seek == NULL) { - errno = ESPIPE; /* historic practice */ - pos = -1; - goto out; - } - - /* - * Find offset of underlying I/O object, then - * adjust for buffered bytes. - */ - FLOCKFILE(fp); - __sflush(fp); /* may adjust seek offset on append stream */ - if (fp->_flags & __SOFF) - pos = fp->_offset; - else { - pos = (*fp->_seek)(fp->_cookie, (fpos_t)0, SEEK_CUR); - if (pos == -1) - goto out; - } - if (fp->_flags & __SRD) { - /* - * Reading. Any unread characters (including - * those from ungetc) cause the position to be - * smaller than that in the underlying object. - */ - pos -= fp->_r; - if (HASUB(fp)) - pos -= fp->_ur; - } else if (fp->_flags & __SWR && fp->_p != NULL) { - /* - * Writing. Any buffered characters cause the - * position to be greater than that in the - * underlying object. - */ - pos += fp->_p - fp->_bf._base; - } -out: FUNLOCKFILE(fp); - return (pos); -} - -long -ftell(FILE *fp) -{ - off_t offset = ftello(fp); - if (offset > LONG_MAX) { - errno = EOVERFLOW; - return (-1); - } - return ((long)offset); -} diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp index d054bf59d..31acfec90 100644 --- a/tests/stdio_test.cpp +++ b/tests/stdio_test.cpp @@ -1068,3 +1068,28 @@ TEST(STDIO_TEST, fclose_invalidates_fd) { ASSERT_EQ(-1, fileno(stdin)); ASSERT_EQ(EBADF, errno); } + +TEST(STDIO_TEST, fseek_ftell_unseekable) { +#if defined(__BIONIC__) // glibc has fopencookie instead. + auto read_fn = [](void*, char*, int) { return -1; }; + FILE* fp = funopen(nullptr, read_fn, nullptr, nullptr, nullptr); + ASSERT_TRUE(fp != nullptr); + + // Check that ftell balks on an unseekable FILE*. + errno = 0; + ASSERT_EQ(-1, ftell(fp)); + ASSERT_EQ(ESPIPE, errno); + + // SEEK_CUR is rewritten as SEEK_SET internally... + errno = 0; + ASSERT_EQ(-1, fseek(fp, 0, SEEK_CUR)); + ASSERT_EQ(ESPIPE, errno); + + // ...so it's worth testing the direct seek path too. + errno = 0; + ASSERT_EQ(-1, fseek(fp, 0, SEEK_SET)); + ASSERT_EQ(ESPIPE, errno); + + fclose(fp); +#endif +}