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.
common_fseek_binary_mode_read_only_fast_track_nolock(__crt_stdio_stream const stream,__int64 offset,int whence)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
common_fseek_nolock(__crt_stdio_stream const stream,__int64 offset,int whence,__crt_cached_ptd_host & ptd)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.
common_fseek(__crt_stdio_stream const stream,__int64 const offset,int const whence,__crt_cached_ptd_host & ptd)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
fseek(FILE * const public_stream,long const offset,int const whence)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
_fseek_nolock(FILE * const public_stream,long const offset,int const whence)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
_fseeki64(FILE * const public_stream,__int64 const offset,int const whence)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
_fseeki64_nolock(FILE * const public_stream,__int64 const offset,int const whence)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