fseek.c revision 82659
1/*- 2 * Copyright (c) 1990, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Chris Torek. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#if defined(LIBC_SCCS) && !defined(lint) 38#if 0 39static char sccsid[] = "@(#)fseek.c 8.3 (Berkeley) 1/2/94"; 40#endif 41static const char rcsid[] = 42 "$FreeBSD: head/lib/libc/stdio/fseek.c 82659 2001-08-31 14:11:14Z ache $"; 43#endif /* LIBC_SCCS and not lint */ 44 45#include "namespace.h" 46#include <sys/types.h> 47#include <sys/stat.h> 48#include <errno.h> 49#include <fcntl.h> 50#include <limits.h> 51#include <stdio.h> 52#include <stdlib.h> 53#include "un-namespace.h" 54#include "local.h" 55#include "libc_private.h" 56 57#define POS_ERR (-(fpos_t)1) 58 59int 60fseek(fp, offset, whence) 61 register FILE *fp; 62 long offset; 63 int whence; 64{ 65 int ret; 66 67 /* make sure stdio is set up */ 68 if (!__sdidinit) 69 __sinit(); 70 71 FLOCKFILE(fp); 72 ret = _fseeko(fp, (off_t)offset, whence, 1); 73 FUNLOCKFILE(fp); 74 return (ret); 75} 76 77int 78fseeko(fp, offset, whence) 79 FILE *fp; 80 off_t offset; 81 int whence; 82{ 83 int ret; 84 85 /* make sure stdio is set up */ 86 if (!__sdidinit) 87 __sinit(); 88 89 FLOCKFILE(fp); 90 ret = _fseeko(fp, offset, whence, 0); 91 FUNLOCKFILE(fp); 92 return (ret); 93} 94 95/* 96 * Seek the given file to the given offset. 97 * `Whence' must be one of the three SEEK_* macros. 98 */ 99int 100_fseeko(fp, offset, whence, ltest) 101 FILE *fp; 102 off_t offset; 103 int whence; 104 int ltest; 105{ 106 register fpos_t (*seekfn) __P((void *, fpos_t, int)); 107 fpos_t target, curoff, spos; 108 size_t n; 109 struct stat st; 110 int havepos; 111 112 /* 113 * Have to be able to seek. 114 */ 115 if ((seekfn = fp->_seek) == NULL) { 116 errno = ESPIPE; /* historic practice */ 117 return (-1); 118 } 119 120 /* 121 * Change any SEEK_CUR to SEEK_SET, and check `whence' argument. 122 * After this, whence is either SEEK_SET or SEEK_END. 123 */ 124 switch (whence) { 125 126 case SEEK_CUR: 127 /* 128 * In order to seek relative to the current stream offset, 129 * we have to first find the current stream offset a la 130 * ftell (see ftell for details). 131 */ 132 if (fp->_flags & __SOFF) 133 curoff = fp->_offset; 134 else { 135 curoff = (*seekfn)(fp->_cookie, (fpos_t)0, SEEK_CUR); 136 if (curoff == -1) 137 return (-1); 138 } 139 if (fp->_flags & __SRD) { 140 spos = curoff; 141 curoff -= fp->_r; 142 if (curoff < 0) { 143 if (HASUB(fp)) { 144 fp->_p -= curoff; 145 fp->_r += curoff; 146 curoff = 0; 147 } else { 148 fp->_p = fp->_bf._base; 149 fp->_r = 0; 150 curoff = spos; 151 } 152 } 153 if (HASUB(fp)) { 154 curoff -= fp->_ur; 155 if (curoff < 0) { 156 if (-curoff <= fp->_r) { 157 fp->_p -= curoff; 158 fp->_r += curoff; 159 curoff = 0; 160 } else { 161 fp->_p = fp->_bf._base; 162 fp->_r = 0; 163 FREEUB(fp); 164 curoff = spos; 165 } 166 } 167 } 168 } else if ((fp->_flags & __SWR) && fp->_p != NULL) { 169 n = fp->_p - fp->_bf._base; 170 if (curoff > OFF_MAX - n) { 171 errno = EOVERFLOW; 172 return (-1); 173 } 174 curoff += n; 175 } 176 if (offset > 0 && curoff > OFF_MAX - offset) { 177 errno = EOVERFLOW; 178 return (-1); 179 } 180 offset += curoff; 181 if (offset < 0) { 182 errno = EINVAL; 183 return (-1); 184 } 185 if (ltest && offset > LONG_MAX) { 186 errno = EOVERFLOW; 187 return (-1); 188 } 189 whence = SEEK_SET; 190 havepos = 1; 191 break; 192 193 case SEEK_SET: 194 if (offset < 0) { 195 errno = EINVAL; 196 return (-1); 197 } 198 case SEEK_END: 199 curoff = 0; /* XXX just to keep gcc quiet */ 200 havepos = 0; 201 break; 202 203 default: 204 errno = EINVAL; 205 return (-1); 206 } 207 208 /* 209 * Can only optimise if: 210 * reading (and not reading-and-writing); 211 * not unbuffered; and 212 * this is a `regular' Unix file (and hence seekfn==__sseek). 213 * We must check __NBF first, because it is possible to have __NBF 214 * and __SOPT both set. 215 */ 216 if (fp->_bf._base == NULL) 217 __smakebuf(fp); 218 if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT)) 219 goto dumb; 220 if ((fp->_flags & __SOPT) == 0) { 221 if (seekfn != __sseek || 222 fp->_file < 0 || _fstat(fp->_file, &st) || 223 (st.st_mode & S_IFMT) != S_IFREG) { 224 fp->_flags |= __SNPT; 225 goto dumb; 226 } 227 fp->_blksize = st.st_blksize; 228 fp->_flags |= __SOPT; 229 } 230 231 /* 232 * We are reading; we can try to optimise. 233 * Figure out where we are going and where we are now. 234 */ 235 if (whence == SEEK_SET) 236 target = offset; 237 else { 238 if (_fstat(fp->_file, &st)) 239 goto dumb; 240 if (offset > 0 && st.st_size > OFF_MAX - offset) { 241 errno = EOVERFLOW; 242 return (-1); 243 } 244 target = st.st_size + offset; 245 if ((off_t)target < 0) { 246 errno = EINVAL; 247 return (-1); 248 } 249 if (ltest && (off_t)target > LONG_MAX) { 250 errno = EOVERFLOW; 251 return (-1); 252 } 253 } 254 255 if (!havepos) { 256 if (fp->_flags & __SOFF) 257 curoff = fp->_offset; 258 else { 259 curoff = (*seekfn)(fp->_cookie, (fpos_t)0, SEEK_CUR); 260 if (curoff == POS_ERR) 261 goto dumb; 262 } 263 spos = curoff; 264 curoff -= fp->_r; 265 if (curoff < 0) { 266 if (HASUB(fp)) { 267 fp->_p -= curoff; 268 fp->_r += curoff; 269 curoff = 0; 270 } else { 271 fp->_p = fp->_bf._base; 272 fp->_r = 0; 273 curoff = spos; 274 } 275 } 276 if (HASUB(fp)) { 277 curoff -= fp->_ur; 278 if (curoff < 0) { 279 if (-curoff <= fp->_r) { 280 fp->_p -= curoff; 281 fp->_r += curoff; 282 curoff = 0; 283 } else { 284 fp->_p = fp->_bf._base; 285 fp->_r = 0; 286 FREEUB(fp); 287 curoff = spos; 288 } 289 } 290 } 291 } 292 293 /* 294 * Compute the number of bytes in the input buffer (pretending 295 * that any ungetc() input has been discarded). Adjust current 296 * offset backwards by this count so that it represents the 297 * file offset for the first byte in the current input buffer. 298 */ 299 if (HASUB(fp)) { 300 if (curoff > OFF_MAX - fp->_r) 301 goto abspos; 302 curoff += fp->_r; /* kill off ungetc */ 303 n = fp->_extra->_up - fp->_bf._base; 304 curoff -= n; 305 n += fp->_ur; 306 } else { 307 n = fp->_p - fp->_bf._base; 308 curoff -= n; 309 n += fp->_r; 310 } 311 /* curoff can be negative at this point. */ 312 313 /* 314 * If the target offset is within the current buffer, 315 * simply adjust the pointers, clear EOF, undo ungetc(), 316 * and return. (If the buffer was modified, we have to 317 * skip this; see fgetln.c.) 318 */ 319 if ((fp->_flags & __SMOD) == 0 && 320 target >= curoff && 321 (curoff <= 0 || curoff <= OFF_MAX - n) && 322 target < curoff + n) { 323 size_t o = target - curoff; 324 325 fp->_p = fp->_bf._base + o; 326 fp->_r = n - o; 327 if (HASUB(fp)) 328 FREEUB(fp); 329 fp->_flags &= ~__SEOF; 330 return (0); 331 } 332 333abspos: 334 /* 335 * The place we want to get to is not within the current buffer, 336 * but we can still be kind to the kernel copyout mechanism. 337 * By aligning the file offset to a block boundary, we can let 338 * the kernel use the VM hardware to map pages instead of 339 * copying bytes laboriously. Using a block boundary also 340 * ensures that we only read one block, rather than two. 341 */ 342 curoff = target & ~(fp->_blksize - 1); 343 if ((*seekfn)(fp->_cookie, curoff, SEEK_SET) == POS_ERR) 344 goto dumb; 345 fp->_r = 0; 346 fp->_p = fp->_bf._base; 347 if (HASUB(fp)) 348 FREEUB(fp); 349 fp->_flags &= ~__SEOF; 350 n = target - curoff; 351 if (n) { 352 if (__srefill(fp) || fp->_r < n) 353 goto dumb; 354 fp->_p += n; 355 fp->_r -= n; 356 } 357 return (0); 358 359 /* 360 * We get here if we cannot optimise the seek ... just 361 * do it. Allow the seek function to change fp->_bf._base. 362 */ 363dumb: 364 if (__sflush(fp) || 365 (*seekfn)(fp->_cookie, (fpos_t)offset, whence) == POS_ERR) 366 return (-1); 367 if (ltest && fp->_offset > LONG_MAX) { 368 errno = EOVERFLOW; 369 return (-1); 370 } 371 /* success: clear EOF indicator and discard ungetc() data */ 372 if (HASUB(fp)) 373 FREEUB(fp); 374 fp->_p = fp->_bf._base; 375 fp->_r = 0; 376 /* fp->_w = 0; */ /* unnecessary (I think...) */ 377 fp->_flags &= ~__SEOF; 378 return (0); 379} 380