11573Srgrimes/*- 21573Srgrimes * Copyright (c) 1990, 1993 31573Srgrimes * The Regents of the University of California. All rights reserved. 41573Srgrimes * 51573Srgrimes * This code is derived from software contributed to Berkeley by 61573Srgrimes * Chris Torek. 71573Srgrimes * 81573Srgrimes * Redistribution and use in source and binary forms, with or without 91573Srgrimes * modification, are permitted provided that the following conditions 101573Srgrimes * are met: 111573Srgrimes * 1. Redistributions of source code must retain the above copyright 121573Srgrimes * notice, this list of conditions and the following disclaimer. 131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141573Srgrimes * notice, this list of conditions and the following disclaimer in the 151573Srgrimes * documentation and/or other materials provided with the distribution. 16249808Semaste * 3. Neither the name of the University nor the names of its contributors 171573Srgrimes * may be used to endorse or promote products derived from this software 181573Srgrimes * without specific prior written permission. 191573Srgrimes * 201573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301573Srgrimes * SUCH DAMAGE. 311573Srgrimes */ 321573Srgrimes 331573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 341573Srgrimesstatic char sccsid[] = "@(#)fseek.c 8.3 (Berkeley) 1/2/94"; 351573Srgrimes#endif /* LIBC_SCCS and not lint */ 3692986Sobrien#include <sys/cdefs.h> 3792986Sobrien__FBSDID("$FreeBSD: stable/10/lib/libc/stdio/fseek.c 321074 2017-07-17 14:09:34Z kib $"); 381573Srgrimes 3971579Sdeischen#include "namespace.h" 401573Srgrimes#include <sys/types.h> 411573Srgrimes#include <sys/stat.h> 4281730Sache#include <errno.h> 431573Srgrimes#include <fcntl.h> 4481730Sache#include <limits.h> 451573Srgrimes#include <stdio.h> 461573Srgrimes#include <stdlib.h> 4771579Sdeischen#include "un-namespace.h" 481573Srgrimes#include "local.h" 4935129Sjb#include "libc_private.h" 501573Srgrimes 511573Srgrimes#define POS_ERR (-(fpos_t)1) 521573Srgrimes 5343782Sdtint 54249810Semastefseek(FILE *fp, long offset, int whence) 5543782Sdt{ 5681817Sache int ret; 5782743Sache int serrno = errno; 5881817Sache 5981817Sache /* make sure stdio is set up */ 6081817Sache if (!__sdidinit) 6181817Sache __sinit(); 6281817Sache 63321074Skib FLOCKFILE_CANCELSAFE(fp); 6481817Sache ret = _fseeko(fp, (off_t)offset, whence, 1); 65321074Skib FUNLOCKFILE_CANCELSAFE(); 6682743Sache if (ret == 0) 6782743Sache errno = serrno; 6881817Sache return (ret); 6943782Sdt} 7043782Sdt 7171579Sdeischenint 72249810Semastefseeko(FILE *fp, off_t offset, int whence) 7371579Sdeischen{ 7471579Sdeischen int ret; 7582743Sache int serrno = errno; 7671579Sdeischen 7771579Sdeischen /* make sure stdio is set up */ 7871579Sdeischen if (!__sdidinit) 7971579Sdeischen __sinit(); 8071579Sdeischen 81321074Skib FLOCKFILE_CANCELSAFE(fp); 8281817Sache ret = _fseeko(fp, offset, whence, 0); 83321074Skib FUNLOCKFILE_CANCELSAFE(); 8482743Sache if (ret == 0) 8582743Sache errno = serrno; 8671579Sdeischen return (ret); 8771579Sdeischen} 8871579Sdeischen 891573Srgrimes/* 901573Srgrimes * Seek the given file to the given offset. 911573Srgrimes * `Whence' must be one of the three SEEK_* macros. 921573Srgrimes */ 931573Srgrimesint 94249810Semaste_fseeko(FILE *fp, off_t offset, int whence, int ltest) 951573Srgrimes{ 9692905Sobrien fpos_t (*seekfn)(void *, fpos_t, int); 9785513Sache fpos_t target, curoff, ret; 981573Srgrimes size_t n; 991573Srgrimes struct stat st; 1001573Srgrimes int havepos; 1011573Srgrimes 1021573Srgrimes /* 1031573Srgrimes * Have to be able to seek. 1041573Srgrimes */ 1051573Srgrimes if ((seekfn = fp->_seek) == NULL) { 10671579Sdeischen errno = ESPIPE; /* historic practice */ 10782588Sache return (-1); 1081573Srgrimes } 1091573Srgrimes 1101573Srgrimes /* 1111573Srgrimes * Change any SEEK_CUR to SEEK_SET, and check `whence' argument. 1121573Srgrimes * After this, whence is either SEEK_SET or SEEK_END. 1131573Srgrimes */ 1141573Srgrimes switch (whence) { 1151573Srgrimes 1161573Srgrimes case SEEK_CUR: 1171573Srgrimes /* 1181573Srgrimes * In order to seek relative to the current stream offset, 11982668Sache * we have to first find the current stream offset via 1201573Srgrimes * ftell (see ftell for details). 1211573Srgrimes */ 12282709Sache if (_ftello(fp, &curoff)) 12382668Sache return (-1); 12482740Sache if (curoff < 0) { 12582740Sache /* Unspecified position because of ungetc() at 0 */ 12682740Sache errno = ESPIPE; 12782740Sache return (-1); 12882740Sache } 12982740Sache if (offset > 0 && curoff > OFF_MAX - offset) { 13081666Sache errno = EOVERFLOW; 13182588Sache return (-1); 13281666Sache } 1331573Srgrimes offset += curoff; 13481666Sache if (offset < 0) { 13581666Sache errno = EINVAL; 13682588Sache return (-1); 13781666Sache } 13881819Sache if (ltest && offset > LONG_MAX) { 13981819Sache errno = EOVERFLOW; 14082588Sache return (-1); 14181819Sache } 1421573Srgrimes whence = SEEK_SET; 1431573Srgrimes havepos = 1; 1441573Srgrimes break; 1451573Srgrimes 1461573Srgrimes case SEEK_SET: 14781666Sache if (offset < 0) { 14881666Sache errno = EINVAL; 14982588Sache return (-1); 15081666Sache } 1511573Srgrimes case SEEK_END: 1521573Srgrimes curoff = 0; /* XXX just to keep gcc quiet */ 1531573Srgrimes havepos = 0; 1541573Srgrimes break; 1551573Srgrimes 1561573Srgrimes default: 1571573Srgrimes errno = EINVAL; 15882588Sache return (-1); 1591573Srgrimes } 1601573Srgrimes 1611573Srgrimes /* 1621573Srgrimes * Can only optimise if: 1631573Srgrimes * reading (and not reading-and-writing); 1641573Srgrimes * not unbuffered; and 1651573Srgrimes * this is a `regular' Unix file (and hence seekfn==__sseek). 1661573Srgrimes * We must check __NBF first, because it is possible to have __NBF 1671573Srgrimes * and __SOPT both set. 1681573Srgrimes */ 1691573Srgrimes if (fp->_bf._base == NULL) 1701573Srgrimes __smakebuf(fp); 1711573Srgrimes if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT)) 1721573Srgrimes goto dumb; 1731573Srgrimes if ((fp->_flags & __SOPT) == 0) { 1741573Srgrimes if (seekfn != __sseek || 17571579Sdeischen fp->_file < 0 || _fstat(fp->_file, &st) || 1761573Srgrimes (st.st_mode & S_IFMT) != S_IFREG) { 1771573Srgrimes fp->_flags |= __SNPT; 1781573Srgrimes goto dumb; 1791573Srgrimes } 1801573Srgrimes fp->_blksize = st.st_blksize; 1811573Srgrimes fp->_flags |= __SOPT; 1821573Srgrimes } 1831573Srgrimes 1841573Srgrimes /* 1851573Srgrimes * We are reading; we can try to optimise. 1861573Srgrimes * Figure out where we are going and where we are now. 1871573Srgrimes */ 1881573Srgrimes if (whence == SEEK_SET) 1891573Srgrimes target = offset; 1901573Srgrimes else { 19171579Sdeischen if (_fstat(fp->_file, &st)) 1921573Srgrimes goto dumb; 19381822Sache if (offset > 0 && st.st_size > OFF_MAX - offset) { 19481666Sache errno = EOVERFLOW; 19582588Sache return (-1); 19681666Sache } 1971573Srgrimes target = st.st_size + offset; 19881666Sache if ((off_t)target < 0) { 19981666Sache errno = EINVAL; 20082588Sache return (-1); 20181666Sache } 20281819Sache if (ltest && (off_t)target > LONG_MAX) { 20381819Sache errno = EOVERFLOW; 20482588Sache return (-1); 20581819Sache } 2061573Srgrimes } 2071573Srgrimes 20882709Sache if (!havepos && _ftello(fp, &curoff)) 20982668Sache goto dumb; 2101573Srgrimes 2111573Srgrimes /* 21285396Sache * (If the buffer was modified, we have to 21385396Sache * skip this; see fgetln.c.) 21482709Sache */ 21582709Sache if (fp->_flags & __SMOD) 21682709Sache goto abspos; 21782709Sache 21882709Sache /* 2191573Srgrimes * Compute the number of bytes in the input buffer (pretending 2201573Srgrimes * that any ungetc() input has been discarded). Adjust current 2211573Srgrimes * offset backwards by this count so that it represents the 2221573Srgrimes * file offset for the first byte in the current input buffer. 2231573Srgrimes */ 2241573Srgrimes if (HASUB(fp)) { 2251573Srgrimes curoff += fp->_r; /* kill off ungetc */ 226178287Sjhb n = fp->_up - fp->_bf._base; 2271573Srgrimes curoff -= n; 2281573Srgrimes n += fp->_ur; 2291573Srgrimes } else { 2301573Srgrimes n = fp->_p - fp->_bf._base; 2311573Srgrimes curoff -= n; 2321573Srgrimes n += fp->_r; 2331573Srgrimes } 2341573Srgrimes 2351573Srgrimes /* 2361573Srgrimes * If the target offset is within the current buffer, 2371573Srgrimes * simply adjust the pointers, clear EOF, undo ungetc(), 23882709Sache * and return. 2391573Srgrimes */ 24082742Sache if (target >= curoff && target < curoff + n) { 24182588Sache size_t o = target - curoff; 2421573Srgrimes 2431573Srgrimes fp->_p = fp->_bf._base + o; 2441573Srgrimes fp->_r = n - o; 2451573Srgrimes if (HASUB(fp)) 2461573Srgrimes FREEUB(fp); 2471573Srgrimes fp->_flags &= ~__SEOF; 248178287Sjhb memset(&fp->_mbstate, 0, sizeof(mbstate_t)); 2491573Srgrimes return (0); 2501573Srgrimes } 2511573Srgrimes 25282588Sacheabspos: 2531573Srgrimes /* 2541573Srgrimes * The place we want to get to is not within the current buffer, 2551573Srgrimes * but we can still be kind to the kernel copyout mechanism. 2561573Srgrimes * By aligning the file offset to a block boundary, we can let 2571573Srgrimes * the kernel use the VM hardware to map pages instead of 2581573Srgrimes * copying bytes laboriously. Using a block boundary also 2591573Srgrimes * ensures that we only read one block, rather than two. 2601573Srgrimes */ 2611573Srgrimes curoff = target & ~(fp->_blksize - 1); 26282807Sache if (_sseek(fp, curoff, SEEK_SET) == POS_ERR) 2631573Srgrimes goto dumb; 2641573Srgrimes fp->_r = 0; 2654169Snate fp->_p = fp->_bf._base; 2661573Srgrimes if (HASUB(fp)) 2671573Srgrimes FREEUB(fp); 2681573Srgrimes n = target - curoff; 2691573Srgrimes if (n) { 2701573Srgrimes if (__srefill(fp) || fp->_r < n) 2711573Srgrimes goto dumb; 2721573Srgrimes fp->_p += n; 2731573Srgrimes fp->_r -= n; 2741573Srgrimes } 27582668Sache fp->_flags &= ~__SEOF; 276178287Sjhb memset(&fp->_mbstate, 0, sizeof(mbstate_t)); 2771573Srgrimes return (0); 2781573Srgrimes 2791573Srgrimes /* 2801573Srgrimes * We get here if we cannot optimise the seek ... just 2811573Srgrimes * do it. Allow the seek function to change fp->_bf._base. 2821573Srgrimes */ 2831573Srgrimesdumb: 28485513Sache if (__sflush(fp) || 28585513Sache (ret = _sseek(fp, (fpos_t)offset, whence)) == POS_ERR) 28682588Sache return (-1); 287170907Sache if (ltest && ret > LONG_MAX) { 288170907Sache fp->_flags |= __SERR; 289170907Sache errno = EOVERFLOW; 290170907Sache return (-1); 291170907Sache } 2921573Srgrimes /* success: clear EOF indicator and discard ungetc() data */ 2931573Srgrimes if (HASUB(fp)) 2941573Srgrimes FREEUB(fp); 2951573Srgrimes fp->_p = fp->_bf._base; 2961573Srgrimes fp->_r = 0; 2971573Srgrimes /* fp->_w = 0; */ /* unnecessary (I think...) */ 2981573Srgrimes fp->_flags &= ~__SEOF; 299178287Sjhb memset(&fp->_mbstate, 0, sizeof(mbstate_t)); 3001573Srgrimes return (0); 3011573Srgrimes} 302