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