fseek.c revision 85418
142660Smarkm/*- 242660Smarkm * Copyright (c) 1990, 1993 321495Sjmacd * The Regents of the University of California. All rights reserved. 442660Smarkm * 521495Sjmacd * This code is derived from software contributed to Berkeley by 621495Sjmacd * Chris Torek. 721495Sjmacd * 821495Sjmacd * Redistribution and use in source and binary forms, with or without 921495Sjmacd * modification, are permitted provided that the following conditions 1021495Sjmacd * are met: 1121495Sjmacd * 1. Redistributions of source code must retain the above copyright 1221495Sjmacd * notice, this list of conditions and the following disclaimer. 1321495Sjmacd * 2. Redistributions in binary form must reproduce the above copyright 1421495Sjmacd * notice, this list of conditions and the following disclaimer in the 1521495Sjmacd * documentation and/or other materials provided with the distribution. 1621495Sjmacd * 3. All advertising materials mentioning features or use of this software 1721495Sjmacd * must display the following acknowledgement: 1821495Sjmacd * This product includes software developed by the University of 1921495Sjmacd * California, Berkeley and its contributors. 2021495Sjmacd * 4. Neither the name of the University nor the names of its contributors 2121495Sjmacd * may be used to endorse or promote products derived from this software 2221495Sjmacd * without specific prior written permission. 2321495Sjmacd * 2421495Sjmacd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2542660Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2642660Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2742660Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2821495Sjmacd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2921495Sjmacd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3021495Sjmacd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3121495Sjmacd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3221495Sjmacd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3321495Sjmacd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3421495Sjmacd * SUCH DAMAGE. 3521495Sjmacd */ 3621495Sjmacd 3721495Sjmacd#if defined(LIBC_SCCS) && !defined(lint) 3821495Sjmacd#if 0 3921495Sjmacdstatic char sccsid[] = "@(#)fseek.c 8.3 (Berkeley) 1/2/94"; 4021495Sjmacd#endif 4121495Sjmacdstatic const char rcsid[] = 4221495Sjmacd "$FreeBSD: head/lib/libc/stdio/fseek.c 85418 2001-10-24 17:25:49Z ache $"; 4321495Sjmacd#endif /* LIBC_SCCS and not lint */ 4421495Sjmacd 4521495Sjmacd#include "namespace.h" 4621495Sjmacd#include <sys/types.h> 4721495Sjmacd#include <sys/stat.h> 4821495Sjmacd#include <errno.h> 4921495Sjmacd#include <fcntl.h> 5042660Smarkm#include <limits.h> 5121495Sjmacd#include <stdio.h> 5221495Sjmacd#include <stdlib.h> 5321495Sjmacd#include "un-namespace.h" 5421495Sjmacd#include "local.h" 5542660Smarkm#include "libc_private.h" 5621495Sjmacd 5721495Sjmacd#define POS_ERR (-(fpos_t)1) 5821495Sjmacd 5921495Sjmacdint 6021495Sjmacdfseek(fp, offset, whence) 6121495Sjmacd register FILE *fp; 6221495Sjmacd long offset; 6321495Sjmacd int whence; 6421495Sjmacd{ 6521495Sjmacd int ret; 6621495Sjmacd int serrno = errno; 6721495Sjmacd 6821495Sjmacd /* make sure stdio is set up */ 6942660Smarkm if (!__sdidinit) 7021495Sjmacd __sinit(); 7121495Sjmacd 7221495Sjmacd FLOCKFILE(fp); 7342660Smarkm ret = _fseeko(fp, (off_t)offset, whence, 1); 7421495Sjmacd FUNLOCKFILE(fp); 7521495Sjmacd if (ret == 0) 7621495Sjmacd errno = serrno; 7721495Sjmacd return (ret); 7821495Sjmacd} 7921495Sjmacd 8021495Sjmacdint 8121495Sjmacdfseeko(fp, offset, whence) 8221495Sjmacd FILE *fp; 8321495Sjmacd off_t offset; 8442660Smarkm int whence; 8521495Sjmacd{ 8621495Sjmacd int ret; 8742660Smarkm int serrno = errno; 8821495Sjmacd 8921495Sjmacd /* make sure stdio is set up */ 9042660Smarkm if (!__sdidinit) 9121495Sjmacd __sinit(); 9221495Sjmacd 9321495Sjmacd FLOCKFILE(fp); 9421495Sjmacd ret = _fseeko(fp, offset, whence, 0); 9521495Sjmacd FUNLOCKFILE(fp); 9642660Smarkm if (ret == 0) 9721495Sjmacd errno = serrno; 9821495Sjmacd return (ret); 9921495Sjmacd} 10021495Sjmacd 10121495Sjmacd/* 10221495Sjmacd * Seek the given file to the given offset. 10321495Sjmacd * `Whence' must be one of the three SEEK_* macros. 10421495Sjmacd */ 10521495Sjmacdint 10621495Sjmacd_fseeko(fp, offset, whence, ltest) 10721495Sjmacd FILE *fp; 10821495Sjmacd off_t offset; 10921495Sjmacd int whence; 11021495Sjmacd int ltest; 11121495Sjmacd{ 11221495Sjmacd register fpos_t (*seekfn) __P((void *, fpos_t, int)); 11321495Sjmacd fpos_t target, curoff; 11421495Sjmacd size_t n; 11521495Sjmacd struct stat st; 11621495Sjmacd int havepos; 11721495Sjmacd 11821495Sjmacd /* 11921495Sjmacd * Have to be able to seek. 12021495Sjmacd */ 12121495Sjmacd if ((seekfn = fp->_seek) == NULL) { 12221495Sjmacd errno = ESPIPE; /* historic practice */ 12321495Sjmacd return (-1); 12421495Sjmacd } 12521495Sjmacd 12621495Sjmacd /* 12721495Sjmacd * Change any SEEK_CUR to SEEK_SET, and check `whence' argument. 12821495Sjmacd * After this, whence is either SEEK_SET or SEEK_END. 12921495Sjmacd */ 13021495Sjmacd switch (whence) { 13142660Smarkm 13221495Sjmacd case SEEK_CUR: 13342660Smarkm /* 13442660Smarkm * In order to seek relative to the current stream offset, 13542660Smarkm * we have to first find the current stream offset via 13621495Sjmacd * ftell (see ftell for details). 13721495Sjmacd */ 13821495Sjmacd if (_ftello(fp, &curoff)) 13921495Sjmacd return (-1); 14021495Sjmacd if (curoff < 0) { 14121495Sjmacd /* Unspecified position because of ungetc() at 0 */ 14221495Sjmacd errno = ESPIPE; 14321495Sjmacd return (-1); 14421495Sjmacd } 14521495Sjmacd if (offset > 0 && curoff > OFF_MAX - offset) { 14621495Sjmacd errno = EOVERFLOW; 14721495Sjmacd return (-1); 14821495Sjmacd } 14921495Sjmacd offset += curoff; 15042660Smarkm if (offset < 0) { 15121495Sjmacd errno = EINVAL; 15221495Sjmacd return (-1); 15321495Sjmacd } 15421495Sjmacd if (ltest && offset > LONG_MAX) { 15521495Sjmacd errno = EOVERFLOW; 15621495Sjmacd return (-1); 15721495Sjmacd } 15821495Sjmacd whence = SEEK_SET; 15921495Sjmacd havepos = 1; 16021495Sjmacd break; 16121495Sjmacd 16221495Sjmacd case SEEK_SET: 16342660Smarkm if (offset < 0) { 16421495Sjmacd errno = EINVAL; 16521495Sjmacd return (-1); 16621495Sjmacd } 16721495Sjmacd case SEEK_END: 16821495Sjmacd curoff = 0; /* XXX just to keep gcc quiet */ 16942660Smarkm havepos = 0; 17042660Smarkm break; 17142660Smarkm 17242660Smarkm default: 17321495Sjmacd errno = EINVAL; 17421495Sjmacd return (-1); 17521495Sjmacd } 17642660Smarkm 17721495Sjmacd /* 17821495Sjmacd * Can only optimise if: 17921495Sjmacd * reading (and not reading-and-writing); 18042660Smarkm * not unbuffered; and 18121495Sjmacd * this is a `regular' Unix file (and hence seekfn==__sseek). 18221495Sjmacd * We must check __NBF first, because it is possible to have __NBF 18321495Sjmacd * and __SOPT both set. 18421495Sjmacd */ 18521495Sjmacd if (fp->_bf._base == NULL) 18621495Sjmacd __smakebuf(fp); 18721495Sjmacd if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT)) 18821495Sjmacd goto dumb; 18921495Sjmacd if ((fp->_flags & __SOPT) == 0) { 19021495Sjmacd if (seekfn != __sseek || 191 fp->_file < 0 || _fstat(fp->_file, &st) || 192 (st.st_mode & S_IFMT) != S_IFREG) { 193 fp->_flags |= __SNPT; 194 goto dumb; 195 } 196 fp->_blksize = st.st_blksize; 197 fp->_flags |= __SOPT; 198 } 199 200 /* 201 * We are reading; we can try to optimise. 202 * Figure out where we are going and where we are now. 203 */ 204 if (whence == SEEK_SET) 205 target = offset; 206 else { 207 if (_fstat(fp->_file, &st)) 208 goto dumb; 209 if (offset > 0 && st.st_size > OFF_MAX - offset) { 210 errno = EOVERFLOW; 211 return (-1); 212 } 213 target = st.st_size + offset; 214 if ((off_t)target < 0) { 215 errno = EINVAL; 216 return (-1); 217 } 218 if (ltest && (off_t)target > LONG_MAX) { 219 errno = EOVERFLOW; 220 return (-1); 221 } 222 } 223 224 if (!havepos && _ftello(fp, &curoff)) 225 goto dumb; 226 227 /* 228 * (If the buffer was modified, we have to 229 * skip this; see fgetln.c.) 230 */ 231 if (fp->_flags & __SMOD) 232 goto abspos; 233 234 /* 235 * Compute the number of bytes in the input buffer (pretending 236 * that any ungetc() input has been discarded). Adjust current 237 * offset backwards by this count so that it represents the 238 * file offset for the first byte in the current input buffer. 239 */ 240 if (HASUB(fp)) { 241 curoff += fp->_r; /* kill off ungetc */ 242 n = fp->_extra->_up - fp->_bf._base; 243 curoff -= n; 244 n += fp->_ur; 245 } else { 246 n = fp->_p - fp->_bf._base; 247 curoff -= n; 248 n += fp->_r; 249 } 250 251 /* 252 * If the target offset is within the current buffer, 253 * simply adjust the pointers, clear EOF, undo ungetc(), 254 * and return. 255 */ 256 if (target >= curoff && target < curoff + n) { 257 size_t o = target - curoff; 258 259 fp->_p = fp->_bf._base + o; 260 fp->_r = n - o; 261 if (HASUB(fp)) 262 FREEUB(fp); 263 fp->_flags &= ~__SEOF; 264 return (0); 265 } 266 267abspos: 268 /* 269 * The place we want to get to is not within the current buffer, 270 * but we can still be kind to the kernel copyout mechanism. 271 * By aligning the file offset to a block boundary, we can let 272 * the kernel use the VM hardware to map pages instead of 273 * copying bytes laboriously. Using a block boundary also 274 * ensures that we only read one block, rather than two. 275 */ 276 curoff = target & ~(fp->_blksize - 1); 277 if (_sseek(fp, curoff, SEEK_SET) == POS_ERR) 278 goto dumb; 279 fp->_r = 0; 280 fp->_p = fp->_bf._base; 281 if (HASUB(fp)) 282 FREEUB(fp); 283 n = target - curoff; 284 if (n) { 285 if (__srefill(fp) || fp->_r < n) 286 goto dumb; 287 fp->_p += n; 288 fp->_r -= n; 289 } 290 fp->_flags &= ~__SEOF; 291 return (0); 292 293 /* 294 * We get here if we cannot optimise the seek ... just 295 * do it. Allow the seek function to change fp->_bf._base. 296 */ 297dumb: 298 if (__sflush(fp) || _sseek(fp, (fpos_t)offset, whence) == POS_ERR) 299 return (-1); 300 /* success: clear EOF indicator and discard ungetc() data */ 301 if (HASUB(fp)) 302 FREEUB(fp); 303 fp->_p = fp->_bf._base; 304 fp->_r = 0; 305 /* fp->_w = 0; */ /* unnecessary (I think...) */ 306 fp->_flags &= ~__SEOF; 307 if (ltest && fp->_offset > LONG_MAX) { 308 fp->_flags |= __SERR; 309 errno = EOVERFLOW; 310 return (-1); 311 } 312 return (0); 313} 314