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 39 static char sccsid[] = "@(#)fseek.c 8.3 (Berkeley) 1/2/94"; 40 #endif 41 static const char rcsid[] = 42 "$FreeBSD$"; 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 59 int 60 fseek(fp, offset, whence) 61 register FILE *fp; 62 long offset; 63 int whence; 64 { 65 int ret; 66 int serrno = errno; 67 68 /* make sure stdio is set up */ 69 if (!__sdidinit) 70 __sinit(); 71 72 FLOCKFILE(fp); 73 ret = _fseeko(fp, (off_t)offset, whence, 1); 74 FUNLOCKFILE(fp); 75 if (ret == 0) 76 errno = serrno; 77 return (ret); 78 } 79 80 int 81 fseeko(fp, offset, whence) 82 FILE *fp; 83 off_t offset; 84 int whence; 85 { 86 int ret; 87 int serrno = errno; 88 89 /* make sure stdio is set up */ 90 if (!__sdidinit) 91 __sinit(); 92 93 FLOCKFILE(fp); 94 ret = _fseeko(fp, offset, whence, 0); 95 FUNLOCKFILE(fp); 96 if (ret == 0) 97 errno = serrno; 98 return (ret); 99 } 100 101 /* 102 * Seek the given file to the given offset. 103 * `Whence' must be one of the three SEEK_* macros. 104 */ 105 int 106 _fseeko(fp, offset, whence, ltest) 107 FILE *fp; 108 off_t offset; 109 int whence; 110 int ltest; 111 { 112 register fpos_t (*seekfn) __P((void *, fpos_t, int)); 113 fpos_t target, curoff, ret; 114 size_t n; 115 struct stat st; 116 int havepos; 117 118 /* 119 * Have to be able to seek. 120 */ 121 if ((seekfn = fp->_seek) == NULL) { 122 errno = ESPIPE; /* historic practice */ 123 return (-1); 124 } 125 126 /* 127 * Change any SEEK_CUR to SEEK_SET, and check `whence' argument. 128 * After this, whence is either SEEK_SET or SEEK_END. 129 */ 130 switch (whence) { 131 132 case SEEK_CUR: 133 /* 134 * In order to seek relative to the current stream offset, 135 * we have to first find the current stream offset via 136 * ftell (see ftell for details). 137 */ 138 if (_ftello(fp, &curoff)) 139 return (-1); 140 if (curoff < 0) { 141 /* Unspecified position because of ungetc() at 0 */ 142 errno = ESPIPE; 143 return (-1); 144 } 145 if (offset > 0 && curoff > OFF_MAX - offset) { 146 errno = EOVERFLOW; 147 return (-1); 148 } 149 offset += curoff; 150 if (offset < 0) { 151 errno = EINVAL; 152 return (-1); 153 } 154 if (ltest && offset > LONG_MAX) { 155 errno = EOVERFLOW; 156 return (-1); 157 } 158 whence = SEEK_SET; 159 havepos = 1; 160 break; 161 162 case SEEK_SET: 163 if (offset < 0) { 164 errno = EINVAL; 165 return (-1); 166 } 167 case SEEK_END: 168 curoff = 0; /* XXX just to keep gcc quiet */ 169 havepos = 0; 170 break; 171 172 default: 173 errno = EINVAL; 174 return (-1); 175 } 176 177 /* 178 * Can only optimise if: 179 * reading (and not reading-and-writing); 180 * not unbuffered; and 181 * this is a `regular' Unix file (and hence seekfn==__sseek). 182 * We must check __NBF first, because it is possible to have __NBF 183 * and __SOPT both set. 184 */ 185 if (fp->_bf._base == NULL) 186 __smakebuf(fp); 187 if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT)) 188 goto dumb; 189 if ((fp->_flags & __SOPT) == 0) { 190 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 267 abspos: 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 */ 297 dumb: 298 if (__sflush(fp) || 299 (ret = _sseek(fp, (fpos_t)offset, whence)) == POS_ERR) 300 return (-1); 301 /* success: clear EOF indicator and discard ungetc() data */ 302 if (HASUB(fp)) 303 FREEUB(fp); 304 fp->_p = fp->_bf._base; 305 fp->_r = 0; 306 /* fp->_w = 0; */ /* unnecessary (I think...) */ 307 fp->_flags &= ~__SEOF; 308 if (ltest && ret > LONG_MAX) { 309 fp->_flags |= __SERR; 310 errno = EOVERFLOW; 311 return (-1); 312 } 313 return (0); 314 } 315