xref: /reactos/sdk/lib/ucrt/stdio/fseek.cpp (revision b09b5584)
1 //
2 // fseek.cpp
3 //
4 //      Copyright (c) Microsoft Corporation.  All rights reserved.
5 //
6 // Defines the fseek() family of functions, which repositions a file pointer to
7 // a new place in a stream.
8 //
9 #include <corecrt_internal_stdio.h>
10 #include <corecrt_internal_ptd_propagation.h>
11 
12 #define ENABLE_INTSAFE_SIGNED_FUNCTIONS
13 #include <intsafe.h>
14 
15 
16 
17 // If a binary mode stream is open for reading and the target of the requested
18 // seek is within the stream buffer, we can simply adjust the buffer pointer to
19 // the new location.  This allows us to avoid flushing the buffer.  If the user
20 // has set a large buffer on the stream and performs frequent seeks that are
21 // likely to result in targets within the buffer, this is a huge performance win.
22 // This function handles the most common cases.
23 static bool __cdecl common_fseek_binary_mode_read_only_fast_track_nolock(
24     __crt_stdio_stream const stream,
25     __int64                  offset,
26     int                      whence
27     ) throw()
28 {
29     // This fast-track path does not handle the seek-from-end case (this is not
30     // nearly as commonly used as seeking from the beginning or from the current
31     // position).
32     if (whence == SEEK_END)
33     {
34         return false;
35     }
36 
37     // This fast-track path is only useful if the stream is buffered.
38     if (!stream.has_any_buffer())
39     {
40         return false;
41     }
42 
43     // This fast-track path requires a stream opened only for reading.  It may
44     // be possible to handle streams opened for writing or update similarly;
45     // further investigation would be required.
46     if (stream.has_any_of(_IOWRITE | _IOUPDATE))
47     {
48         return false;
49     }
50 
51     // The ftell function handles a case where _cnt is negative.  It isn't clear
52     // why _cnt may be negative, so if _cnt is negative, fall back to the
53     // expensive path.  If the _cnt is zero, do not assume that the buffer
54     // contents contain the immediately preceding file content; data may have
55     // been read from the file bypassing the buffer.  Fall back to the expensive
56     // path to be sure.
57     if (stream->_cnt <= 0)
58     {
59         return false;
60     }
61 
62     // This fast-track path requires a binary mode file handle.  When text mode
63     // or UTF transformations are enabled, the contents of the buffer do not
64     // exactly match the contents of the underlying file.
65     int const fh = stream.lowio_handle();
66     if ((_osfile(fh) & FTEXT) != 0 || _textmode(fh) != __crt_lowio_text_mode::ansi)
67     {
68         return false;
69     }
70 
71     // Handle the SEEK_SET case by transforming the SEEK_SET offset into a
72     // SEEK_CUR offset:
73     if (whence == SEEK_SET)
74     {
75         __int64 const lowio_position = _lseeki64_nolock(fh, 0, SEEK_CUR);
76         if (lowio_position < 0)
77         {
78             return false;
79         }
80 
81         __int64 const stdio_position = lowio_position - stream->_cnt;
82         if (FAILED(LongLongSub(offset, stdio_position, &offset)))
83         {
84             return false;
85         }
86         whence = SEEK_CUR;
87     }
88 
89     // Compute the maximum number of bytes that we can seek in each direction
90     // within the buffer and verify that the requested offset is within that
91     // range.  Note that the minimum reverse seek is a negative value.
92     __int64 const minimum_reverse_seek = -(stream->_ptr - stream->_base);
93     __int64 const maximum_forward_seek = stream->_cnt;
94 
95     bool const seek_is_within_buffer = minimum_reverse_seek <= offset && offset <= maximum_forward_seek;
96     if (!seek_is_within_buffer)
97     {
98         return false;
99     }
100 
101     stream->_ptr += offset;
102 
103     // Note that the cast here is safe:  The buffer will never be larger than
104     // INT_MAX bytes in size.  The setvbuf function validates this constraint.
105     stream->_cnt -= static_cast<int>(offset);
106     return true;
107 }
108 
109 
110 
111 static int __cdecl common_fseek_nolock(
112     __crt_stdio_stream const stream,
113     __int64                  offset,
114     int                      whence,
115     __crt_cached_ptd_host&   ptd
116     ) throw()
117 {
118     if (!stream.is_in_use())
119     {
120         ptd.get_errno().set(EINVAL);
121         return -1;
122     }
123 
124     stream.unset_flags(_IOEOF);
125 
126     if (common_fseek_binary_mode_read_only_fast_track_nolock(stream, offset, whence))
127     {
128         return 0;
129     }
130 
131     // If seeking relative to the current location, then convert to a seek
132     // relative to the beginning of the file.  This accounts for buffering,
133     // etc., by letting fseek() tell us where we are:
134     if (whence == SEEK_CUR)
135     {
136         offset += _ftelli64_nolock_internal(stream.public_stream(), ptd);
137         whence = SEEK_SET;
138     }
139 
140     __acrt_stdio_flush_nolock(stream.public_stream(), ptd);
141     // If the stream is opened in update mode and is currently in use for reading,
142     // the buffer must be abandoned to ensure consistency when transitioning from
143     // reading to writing.
144     // __acrt_stdio_flush_nolock will not reset the buffer when _IOWRITE flag
145     // is not set.
146     __acrt_stdio_reset_buffer(stream);
147 
148     // If the file was opened for read/write, clear flags since we don't know
149     // what the user will do next with the file.  If the file was opened for
150     // read only access, decrease the _bufsiz so that the next call to
151     // __acrt_stdio_refill_and_read_{narrow,wide}_nolock won't cost quite so
152     // much:
153     if (stream.has_all_of(_IOUPDATE))
154     {
155         stream.unset_flags(_IOWRITE | _IOREAD);
156     }
157     else if (stream.has_all_of(_IOREAD | _IOBUFFER_CRT) && !stream.has_any_of(_IOBUFFER_SETVBUF))
158     {
159         stream->_bufsiz = _SMALL_BUFSIZ;
160     }
161 
162     if (_lseeki64_nolock_internal(stream.lowio_handle(), offset, whence, ptd) == -1)
163     {
164         return -1;
165     }
166 
167     return 0;
168 }
169 
170 
171 
172 // Repositions the file pointer of a stream to the specified location, relative
173 // to 'whence', which can be SEEK_SET (the beginning of the file), SEEK_CUR (the
174 // current pointer position), or SEEK_END (the end of the file).  The offset may
175 // be negative.  Returns 0 on success; returns -1 and sets errno on failure.
176 static int __cdecl common_fseek(
177     __crt_stdio_stream const stream,
178     __int64            const offset,
179     int                const whence,
180     __crt_cached_ptd_host&   ptd
181     ) throw()
182 {
183     _UCRT_VALIDATE_RETURN(ptd, stream.valid(), EINVAL, -1);
184     _UCRT_VALIDATE_RETURN(ptd, whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END, EINVAL, -1);
185 
186     int return_value = -1;
187 
188     _lock_file(stream.public_stream());
189     __try
190     {
191         return_value = common_fseek_nolock(stream, offset, whence, ptd);
192     }
193     __finally
194     {
195         _unlock_file(stream.public_stream());
196     }
197     __endtry
198 
199     return return_value;
200 }
201 
202 
203 
204 extern "C" int __cdecl fseek(
205     FILE* const public_stream,
206     long  const offset,
207     int   const whence
208     )
209 {
210     __crt_cached_ptd_host ptd;
211     return common_fseek(__crt_stdio_stream(public_stream), offset, whence, ptd);
212 }
213 
214 
215 
216 extern "C" int __cdecl _fseek_nolock(
217     FILE* const public_stream,
218     long  const offset,
219     int   const whence
220     )
221 {
222     __crt_cached_ptd_host ptd;
223     return common_fseek_nolock(__crt_stdio_stream(public_stream), offset, whence, ptd);
224 }
225 
226 
227 
228 extern "C" int __cdecl _fseeki64(
229     FILE*   const public_stream,
230     __int64 const offset,
231     int     const whence
232     )
233 {
234     __crt_cached_ptd_host ptd;
235     return common_fseek(__crt_stdio_stream(public_stream), offset, whence, ptd);
236 }
237 
238 
239 
240 extern "C" int __cdecl _fseeki64_nolock(
241     FILE*   const public_stream,
242     __int64 const offset,
243     int     const whence
244     )
245 {
246     __crt_cached_ptd_host ptd;
247     return common_fseek_nolock(__crt_stdio_stream(public_stream), offset, whence, ptd);
248 }
249