fseek.c revision 81822
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. 161573Srgrimes * 3. All advertising materials mentioning features or use of this software 171573Srgrimes * must display the following acknowledgement: 181573Srgrimes * This product includes software developed by the University of 191573Srgrimes * California, Berkeley and its contributors. 201573Srgrimes * 4. Neither the name of the University nor the names of its contributors 211573Srgrimes * may be used to endorse or promote products derived from this software 221573Srgrimes * without specific prior written permission. 231573Srgrimes * 241573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341573Srgrimes * SUCH DAMAGE. 351573Srgrimes */ 361573Srgrimes 371573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 3816586Sjraynard#if 0 391573Srgrimesstatic char sccsid[] = "@(#)fseek.c 8.3 (Berkeley) 1/2/94"; 4016586Sjraynard#endif 4116586Sjraynardstatic const char rcsid[] = 4250476Speter "$FreeBSD: head/lib/libc/stdio/fseek.c 81822 2001-08-17 11:08:56Z ache $"; 431573Srgrimes#endif /* LIBC_SCCS and not lint */ 441573Srgrimes 4571579Sdeischen#include "namespace.h" 461573Srgrimes#include <sys/types.h> 471573Srgrimes#include <sys/stat.h> 4881730Sache#include <errno.h> 491573Srgrimes#include <fcntl.h> 5081730Sache#include <limits.h> 511573Srgrimes#include <stdio.h> 521573Srgrimes#include <stdlib.h> 5371579Sdeischen#include "un-namespace.h" 541573Srgrimes#include "local.h" 5535129Sjb#include "libc_private.h" 561573Srgrimes 571573Srgrimes#define POS_ERR (-(fpos_t)1) 581573Srgrimes 5943782Sdtint 6043782Sdtfseek(fp, offset, whence) 6143782Sdt register FILE *fp; 6243782Sdt long offset; 6343782Sdt int whence; 6443782Sdt{ 6581817Sache int ret; 6681817Sache 6781817Sache /* make sure stdio is set up */ 6881817Sache if (!__sdidinit) 6981817Sache __sinit(); 7081817Sache 7181817Sache FLOCKFILE(fp); 7281817Sache ret = _fseeko(fp, (off_t)offset, whence, 1); 7381817Sache FUNLOCKFILE(fp); 7481817Sache return (ret); 7543782Sdt} 7643782Sdt 7771579Sdeischenint 7871579Sdeischenfseeko(fp, offset, whence) 7971579Sdeischen FILE *fp; 8071579Sdeischen off_t offset; 8171579Sdeischen int whence; 8271579Sdeischen{ 8371579Sdeischen int ret; 8471579Sdeischen 8571579Sdeischen /* make sure stdio is set up */ 8671579Sdeischen if (!__sdidinit) 8771579Sdeischen __sinit(); 8871579Sdeischen 8971579Sdeischen FLOCKFILE(fp); 9081817Sache ret = _fseeko(fp, offset, whence, 0); 9171579Sdeischen FUNLOCKFILE(fp); 9271579Sdeischen return (ret); 9371579Sdeischen} 9471579Sdeischen 951573Srgrimes/* 961573Srgrimes * Seek the given file to the given offset. 971573Srgrimes * `Whence' must be one of the three SEEK_* macros. 981573Srgrimes */ 991573Srgrimesint 10081817Sache_fseeko(fp, offset, whence, ltest) 10171579Sdeischen FILE *fp; 10243782Sdt off_t offset; 1031573Srgrimes int whence; 10481817Sache int ltest; 1051573Srgrimes{ 1061573Srgrimes register fpos_t (*seekfn) __P((void *, fpos_t, int)); 1071573Srgrimes fpos_t target, curoff; 1081573Srgrimes size_t n; 1091573Srgrimes struct stat st; 1101573Srgrimes int havepos; 1111573Srgrimes 1121573Srgrimes /* 1131573Srgrimes * Have to be able to seek. 1141573Srgrimes */ 1151573Srgrimes if ((seekfn = fp->_seek) == NULL) { 11671579Sdeischen errno = ESPIPE; /* historic practice */ 1171573Srgrimes return (EOF); 1181573Srgrimes } 1191573Srgrimes 1201573Srgrimes /* 1211573Srgrimes * Change any SEEK_CUR to SEEK_SET, and check `whence' argument. 1221573Srgrimes * After this, whence is either SEEK_SET or SEEK_END. 1231573Srgrimes */ 1241573Srgrimes switch (whence) { 1251573Srgrimes 1261573Srgrimes case SEEK_CUR: 1271573Srgrimes /* 1281573Srgrimes * In order to seek relative to the current stream offset, 1291573Srgrimes * we have to first find the current stream offset a la 1301573Srgrimes * ftell (see ftell for details). 1311573Srgrimes */ 1321573Srgrimes if (fp->_flags & __SOFF) 1331573Srgrimes curoff = fp->_offset; 1341573Srgrimes else { 1351573Srgrimes curoff = (*seekfn)(fp->_cookie, (fpos_t)0, SEEK_CUR); 13671579Sdeischen if (curoff == -1) 1371573Srgrimes return (EOF); 1381573Srgrimes } 1391573Srgrimes if (fp->_flags & __SRD) { 1401573Srgrimes curoff -= fp->_r; 1411573Srgrimes if (HASUB(fp)) 1421573Srgrimes curoff -= fp->_ur; 1431573Srgrimes } else if (fp->_flags & __SWR && fp->_p != NULL) 1441573Srgrimes curoff += fp->_p - fp->_bf._base; 1451573Srgrimes 14681730Sache /* curoff always >= 0 */ 14781822Sache if (offset > 0 && curoff > OFF_MAX - offset) { 14881666Sache errno = EOVERFLOW; 14981666Sache return (EOF); 15081666Sache } 1511573Srgrimes offset += curoff; 15281666Sache /* Disallow negative seeks per POSIX */ 15381666Sache if (offset < 0) { 15481666Sache errno = EINVAL; 15581666Sache return (EOF); 15681666Sache } 15781819Sache if (ltest && offset > LONG_MAX) { 15881819Sache errno = EOVERFLOW; 15981819Sache return (EOF); 16081819Sache } 1611573Srgrimes whence = SEEK_SET; 1621573Srgrimes havepos = 1; 1631573Srgrimes break; 1641573Srgrimes 1651573Srgrimes case SEEK_SET: 16681666Sache /* Disallow negative seeks per POSIX */ 16781666Sache if (offset < 0) { 16881666Sache errno = EINVAL; 16981666Sache return (EOF); 17081666Sache } 1711573Srgrimes case SEEK_END: 1721573Srgrimes curoff = 0; /* XXX just to keep gcc quiet */ 1731573Srgrimes havepos = 0; 1741573Srgrimes break; 1751573Srgrimes 1761573Srgrimes default: 1771573Srgrimes errno = EINVAL; 1781573Srgrimes return (EOF); 1791573Srgrimes } 1801573Srgrimes 1811573Srgrimes /* 1821573Srgrimes * Can only optimise if: 1831573Srgrimes * reading (and not reading-and-writing); 1841573Srgrimes * not unbuffered; and 1851573Srgrimes * this is a `regular' Unix file (and hence seekfn==__sseek). 1861573Srgrimes * We must check __NBF first, because it is possible to have __NBF 1871573Srgrimes * and __SOPT both set. 1881573Srgrimes */ 1891573Srgrimes if (fp->_bf._base == NULL) 1901573Srgrimes __smakebuf(fp); 1911573Srgrimes if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT)) 1921573Srgrimes goto dumb; 1931573Srgrimes if ((fp->_flags & __SOPT) == 0) { 1941573Srgrimes if (seekfn != __sseek || 19571579Sdeischen fp->_file < 0 || _fstat(fp->_file, &st) || 1961573Srgrimes (st.st_mode & S_IFMT) != S_IFREG) { 1971573Srgrimes fp->_flags |= __SNPT; 1981573Srgrimes goto dumb; 1991573Srgrimes } 2001573Srgrimes fp->_blksize = st.st_blksize; 2011573Srgrimes fp->_flags |= __SOPT; 2021573Srgrimes } 2031573Srgrimes 2041573Srgrimes /* 2051573Srgrimes * We are reading; we can try to optimise. 2061573Srgrimes * Figure out where we are going and where we are now. 2071573Srgrimes */ 2081573Srgrimes if (whence == SEEK_SET) 2091573Srgrimes target = offset; 2101573Srgrimes else { 21171579Sdeischen if (_fstat(fp->_file, &st)) 2121573Srgrimes goto dumb; 21381730Sache /* st.st_size always >= 0 */ 21481822Sache if (offset > 0 && st.st_size > OFF_MAX - offset) { 21581666Sache errno = EOVERFLOW; 21681666Sache return (EOF); 21781666Sache } 2181573Srgrimes target = st.st_size + offset; 21981666Sache /* Disallow negative seeks per POSIX */ 22081666Sache if ((off_t)target < 0) { 22181666Sache errno = EINVAL; 22281666Sache return (EOF); 22381666Sache } 22481819Sache if (ltest && (off_t)target > LONG_MAX) { 22581819Sache errno = EOVERFLOW; 22681819Sache return (EOF); 22781819Sache } 2281573Srgrimes } 2291573Srgrimes 2301573Srgrimes if (!havepos) { 2311573Srgrimes if (fp->_flags & __SOFF) 2321573Srgrimes curoff = fp->_offset; 2331573Srgrimes else { 2341573Srgrimes curoff = (*seekfn)(fp->_cookie, (fpos_t)0, SEEK_CUR); 2351573Srgrimes if (curoff == POS_ERR) 2361573Srgrimes goto dumb; 2371573Srgrimes } 2381573Srgrimes curoff -= fp->_r; 2391573Srgrimes if (HASUB(fp)) 2401573Srgrimes curoff -= fp->_ur; 2411573Srgrimes } 2421573Srgrimes 2431573Srgrimes /* 2441573Srgrimes * Compute the number of bytes in the input buffer (pretending 2451573Srgrimes * that any ungetc() input has been discarded). Adjust current 2461573Srgrimes * offset backwards by this count so that it represents the 2471573Srgrimes * file offset for the first byte in the current input buffer. 2481573Srgrimes */ 2491573Srgrimes if (HASUB(fp)) { 2501573Srgrimes curoff += fp->_r; /* kill off ungetc */ 25172529Simp n = fp->_extra->_up - fp->_bf._base; 2521573Srgrimes curoff -= n; 2531573Srgrimes n += fp->_ur; 2541573Srgrimes } else { 2551573Srgrimes n = fp->_p - fp->_bf._base; 2561573Srgrimes curoff -= n; 2571573Srgrimes n += fp->_r; 2581573Srgrimes } 2591573Srgrimes 2601573Srgrimes /* 2611573Srgrimes * If the target offset is within the current buffer, 2621573Srgrimes * simply adjust the pointers, clear EOF, undo ungetc(), 2631573Srgrimes * and return. (If the buffer was modified, we have to 2641573Srgrimes * skip this; see fgetln.c.) 2651573Srgrimes */ 2661573Srgrimes if ((fp->_flags & __SMOD) == 0 && 2671573Srgrimes target >= curoff && target < curoff + n) { 2681573Srgrimes register int o = target - curoff; 2691573Srgrimes 2701573Srgrimes fp->_p = fp->_bf._base + o; 2711573Srgrimes fp->_r = n - o; 2721573Srgrimes if (HASUB(fp)) 2731573Srgrimes FREEUB(fp); 2741573Srgrimes fp->_flags &= ~__SEOF; 2751573Srgrimes return (0); 2761573Srgrimes } 2771573Srgrimes 2781573Srgrimes /* 2791573Srgrimes * The place we want to get to is not within the current buffer, 2801573Srgrimes * but we can still be kind to the kernel copyout mechanism. 2811573Srgrimes * By aligning the file offset to a block boundary, we can let 2821573Srgrimes * the kernel use the VM hardware to map pages instead of 2831573Srgrimes * copying bytes laboriously. Using a block boundary also 2841573Srgrimes * ensures that we only read one block, rather than two. 2851573Srgrimes */ 2861573Srgrimes curoff = target & ~(fp->_blksize - 1); 2871573Srgrimes if ((*seekfn)(fp->_cookie, curoff, SEEK_SET) == POS_ERR) 2881573Srgrimes goto dumb; 2891573Srgrimes fp->_r = 0; 2904169Snate fp->_p = fp->_bf._base; 2911573Srgrimes if (HASUB(fp)) 2921573Srgrimes FREEUB(fp); 2931573Srgrimes fp->_flags &= ~__SEOF; 2941573Srgrimes n = target - curoff; 2951573Srgrimes if (n) { 2961573Srgrimes if (__srefill(fp) || fp->_r < n) 2971573Srgrimes goto dumb; 2981573Srgrimes fp->_p += n; 2991573Srgrimes fp->_r -= n; 3001573Srgrimes } 3011573Srgrimes return (0); 3021573Srgrimes 3031573Srgrimes /* 3041573Srgrimes * We get here if we cannot optimise the seek ... just 3051573Srgrimes * do it. Allow the seek function to change fp->_bf._base. 3061573Srgrimes */ 3071573Srgrimesdumb: 3081573Srgrimes if (__sflush(fp) || 30971579Sdeischen (*seekfn)(fp->_cookie, (fpos_t)offset, whence) == POS_ERR) 3101573Srgrimes return (EOF); 31181817Sache /* POSIX require long type resulting offset for fseek() */ 31281817Sache if (ltest && fp->_offset != (long)fp->_offset) { 31381817Sache errno = EOVERFLOW; 31481817Sache return (EOF); 31581817Sache } 3161573Srgrimes /* success: clear EOF indicator and discard ungetc() data */ 3171573Srgrimes if (HASUB(fp)) 3181573Srgrimes FREEUB(fp); 3191573Srgrimes fp->_p = fp->_bf._base; 3201573Srgrimes fp->_r = 0; 3211573Srgrimes /* fp->_w = 0; */ /* unnecessary (I think...) */ 3221573Srgrimes fp->_flags &= ~__SEOF; 3231573Srgrimes return (0); 3241573Srgrimes} 325