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