1 /*
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * and/or other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 /*
19 FUNCTION
20 <<fseeko64>>---set file position for large file
21 
22 INDEX
23 	fseeko64
24 INDEX
25 	_fseeko64_r
26 
27 SYNOPSIS
28 	#include <stdio.h>
29 	int fseeko64(FILE *<[fp]>, _off64_t <[offset]>, int <[whence]>);
30 	int _fseeko64_r (struct _reent *<[ptr]>, FILE *<[fp]>,
31                          _off64_t <[offset]>, int <[whence]>);
32 
33 DESCRIPTION
34 Objects of type <<FILE>> can have a ``position'' that records how much
35 of the file your program has already read.  Many of the <<stdio>> functions
36 depend on this position, and many change it as a side effect.
37 
38 You can use <<fseeko64>> to set the position for the file identified by
39 <[fp]> that was opened via <<fopen64>>.  The value of <[offset]> determines
40 the new position, in one of three ways selected by the value of <[whence]>
41 (defined as macros in `<<stdio.h>>'):
42 
43 <<SEEK_SET>>---<[offset]> is the absolute file position (an offset
44 from the beginning of the file) desired.  <[offset]> must be positive.
45 
46 <<SEEK_CUR>>---<[offset]> is relative to the current file position.
47 <[offset]> can meaningfully be either positive or negative.
48 
49 <<SEEK_END>>---<[offset]> is relative to the current end of file.
50 <[offset]> can meaningfully be either positive (to increase the size
51 of the file) or negative.
52 
53 See <<ftello64>> to determine the current file position.
54 
55 RETURNS
56 <<fseeko64>> returns <<0>> when successful.  On failure, the
57 result is <<EOF>>.  The reason for failure is indicated in <<errno>>:
58 either <<ESPIPE>> (the stream identified by <[fp]> doesn't support
59 repositioning or wasn't opened via <<fopen64>>) or <<EINVAL>>
60 (invalid file position).
61 
62 PORTABILITY
63 <<fseeko64>> is a glibc extension.
64 
65 Supporting OS subroutines required: <<close>>, <<fstat64>>, <<isatty>>,
66 <<lseek64>>, <<read>>, <<sbrk>>, <<write>>.
67 */
68 
69 #define _DEFAULT_SOURCE
70 #include <stdio.h>
71 #include <time.h>
72 #include <fcntl.h>
73 #include <stdlib.h>
74 #include <errno.h>
75 #include <sys/types.h>
76 #include <sys/stat.h>
77 #include "../stdio/local.h"
78 
79 #define	POS_ERR	(-(_fpos64_t)1)
80 
81 #ifdef __LARGE64_FILES
82 
83 /*
84  * Seek the given file to the given offset.
85  * `Whence' must be one of the three SEEK_* macros.
86  */
87 
88 _off64_t
fseeko64(register FILE * fp,_off64_t offset,int whence)89 fseeko64 (
90      register FILE *fp,
91      _off64_t offset,
92      int whence)
93 {
94   _fpos64_t (*seekfn) (void *, _fpos64_t, int);
95   _fpos64_t curoff;
96 #ifdef _FSEEK_OPTIMIZATION
97   _fpos64_t target;
98   size_t n;
99 #endif
100 
101   int havepos;
102 
103   /* Only do 64-bit seek on large file.  */
104   if (!(fp->_flags & __SL64))
105     {
106       if ((_off_t) offset != offset)
107 	{
108 	  _REENT_ERRNO(ptr) = EOVERFLOW;
109 	  return EOF;
110 	}
111       return (_off64_t) fseeko (fp, offset, whence);
112     }
113 
114   /* Make sure stdio is set up.  */
115 
116   CHECK_INIT (ptr, fp);
117 
118   _newlib_flockfile_start (fp);
119 
120   curoff = fp->_offset;
121 
122   /* If we've been doing some writing, and we're in append mode
123      then we don't really know where the filepos is.  */
124 
125   if (fp->_flags & __SAPP && fp->_flags & __SWR)
126     {
127       /* So flush the buffer and seek to the end.  */
128       fflush (fp);
129     }
130 
131   /* Have to be able to seek.  */
132 
133   if ((seekfn = fp->_seek64) == NULL)
134     {
135       _REENT_ERRNO(ptr) = ESPIPE;	/* ??? */
136       _newlib_flockfile_exit(fp);
137       return EOF;
138     }
139 
140   /*
141    * Change any SEEK_CUR to SEEK_SET, and check `whence' argument.
142    * After this, whence is either SEEK_SET or SEEK_END.
143    */
144 
145   switch (whence)
146     {
147     case SEEK_CUR:
148       /*
149        * In order to seek relative to the current stream offset,
150        * we have to first find the current stream offset a la
151        * ftell (see ftell for details).
152        */
153       fflush (fp);   /* may adjust seek offset on append stream */
154       if (fp->_flags & __SOFF)
155 	curoff = fp->_offset;
156       else
157 	{
158 	  curoff = seekfn (fp->_cookie, (_fpos64_t) 0, SEEK_CUR);
159 	  if (curoff == -1L)
160 	    {
161 	      _newlib_flockfile_exit(fp);
162 	      return EOF;
163 	    }
164 	}
165       if (fp->_flags & __SRD)
166 	{
167 	  curoff -= fp->_r;
168 	  if (HASUB (fp))
169 	    curoff -= fp->_ur;
170 	}
171       else if (fp->_flags & __SWR && fp->_p != NULL)
172 	curoff += fp->_p - fp->_bf._base;
173 
174       offset += curoff;
175       whence = SEEK_SET;
176       havepos = 1;
177       break;
178 
179     case SEEK_SET:
180     case SEEK_END:
181       havepos = 0;
182       break;
183 
184     default:
185       _REENT_ERRNO(ptr) = EINVAL;
186       _newlib_flockfile_exit(fp);
187       return (EOF);
188     }
189 
190   (void) havepos;
191   /*
192    * Can only optimise if:
193    *	reading (and not reading-and-writing);
194    *	not unbuffered; and
195    *	this is a `regular' Unix file (and hence seekfn==__sseek).
196    * We must check __NBF first, because it is possible to have __NBF
197    * and __SOPT both set.
198    */
199 
200   if (fp->_bf._base == NULL)
201     _smakebuf (fp);
202 
203 #ifdef _FSEEK_OPTIMIZATION
204   if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT))
205     goto dumb;
206   if ((fp->_flags & __SOPT) == 0)
207     {
208       struct stat64 st;
209       if (seekfn != __sseek64
210 	  || fp->_file < 0
211 	  || _fstat64 (fp->_file, &st)
212 	  || (st.st_mode & S_IFMT) != S_IFREG)
213 	{
214 	  fp->_flags |= __SNPT;
215 	  goto dumb;
216 	}
217 #ifdef	HAVE_BLKSIZE
218       fp->_blksize = st.st_blksize;
219 #else
220       fp->_blksize = 1024;
221 #endif
222       fp->_flags |= __SOPT;
223     }
224 
225   /*
226    * We are reading; we can try to optimise.
227    * Figure out where we are going and where we are now.
228    */
229 
230   if (whence == SEEK_SET)
231     target = offset;
232   else
233     {
234       struct stat64 st;
235       if (_fstat64 (fp->_file, &st))
236 	goto dumb;
237       target = st.st_size + offset;
238     }
239 
240   if (!havepos)
241     {
242       if (fp->_flags & __SOFF)
243 	curoff = fp->_offset;
244       else
245 	{
246 	  curoff = seekfn (fp->_cookie, (_fpos64_t)0, SEEK_CUR);
247 	  if (curoff == POS_ERR)
248 	    goto dumb;
249 	}
250       curoff -= fp->_r;
251       if (HASUB (fp))
252 	curoff -= fp->_ur;
253     }
254 
255   /*
256    * Compute the number of bytes in the input buffer (pretending
257    * that any ungetc() input has been discarded).  Adjust current
258    * offset backwards by this count so that it represents the
259    * file offset for the first byte in the current input buffer.
260    */
261 
262   if (HASUB (fp))
263     {
264       curoff += fp->_r;       /* kill off ungetc */
265       n = fp->_up - fp->_bf._base;
266       curoff -= n;
267       n += fp->_ur;
268     }
269   else
270     {
271       n = fp->_p - fp->_bf._base;
272       curoff -= n;
273       n += fp->_r;
274     }
275 
276   /*
277    * If the target offset is within the current buffer,
278    * simply adjust the pointers, clear EOF, undo ungetc(),
279    * and return.
280    */
281 
282   if (target >= curoff && (size_t) target < (size_t) curoff + n)
283     {
284       register int o = target - curoff;
285 
286       fp->_p = fp->_bf._base + o;
287       fp->_r = n - o;
288       if (HASUB (fp))
289 	FREEUB (ptr, fp);
290       fp->_flags &= ~__SEOF;
291       _newlib_flockfile_exit(fp);
292       return 0;
293     }
294 
295   /*
296    * The place we want to get to is not within the current buffer,
297    * but we can still be kind to the kernel copyout mechanism.
298    * By aligning the file offset to a block boundary, we can let
299    * the kernel use the VM hardware to map pages instead of
300    * copying bytes laboriously.  Using a block boundary also
301    * ensures that we only read one block, rather than two.
302    */
303 
304   curoff = target & ~((_fpos64_t)(fp->_blksize - 1));
305   if (seekfn (fp->_cookie, curoff, SEEK_SET) == POS_ERR)
306     goto dumb;
307   fp->_r = 0;
308   fp->_p = fp->_bf._base;
309   if (HASUB (fp))
310     FREEUB (ptr, fp);
311   fp->_flags &= ~__SEOF;
312   n = target - curoff;
313   if (n)
314     {
315       if (_srefill (fp) || fp->_r < (int) n)
316 	goto dumb;
317       fp->_p += n;
318       fp->_r -= n;
319     }
320   _newlib_flockfile_exit(fp);
321   return 0;
322 
323   /*
324    * We get here if we cannot optimise the seek ... just
325    * do it.  Allow the seek function to change fp->_bf._base.
326    */
327 dumb:
328 #endif
329 
330   if (fflush (fp)
331       || seekfn (fp->_cookie, offset, whence) == POS_ERR)
332     {
333       _newlib_flockfile_exit(fp);
334       return EOF;
335     }
336   /* success: clear EOF indicator and discard ungetc() data */
337   if (HASUB (fp))
338     FREEUB (ptr, fp);
339   fp->_p = fp->_bf._base;
340   fp->_r = 0;
341   /* fp->_w = 0; *//* unnecessary (I think...) */
342   fp->_flags &= ~__SEOF;
343   _newlib_flockfile_end (fp);
344   return 0;
345 }
346 
347 #endif /* __LARGE64_FILES */
348