1 // 2 // _flsbuf.cpp 3 // 4 // Copyright (c) Microsoft Corporation. All rights reserved. 5 // 6 // Functions that flush a stdio stream buffer and write a character. 7 // 8 #include <corecrt_internal_stdio.h> 9 #include <corecrt_internal_ptd_propagation.h> 10 11 static bool __cdecl stream_is_at_end_of_file_nolock( 12 __crt_stdio_stream const stream 13 ) throw() 14 { 15 if (stream.has_any_of(_IOEOF)) 16 { 17 return true; 18 } 19 20 // If there is any data in the buffer, then we are not at the end of the file. 21 if (stream.has_big_buffer() && (stream->_ptr == stream->_base)) 22 { 23 return false; 24 } 25 26 HANDLE const os_handle = reinterpret_cast<HANDLE>(_get_osfhandle(stream.lowio_handle())); 27 28 // If we fail at querying for the file size, proceed as though we cannot 29 // gather that information. For example, this will fail with pipes. 30 if (os_handle == INVALID_HANDLE_VALUE) 31 { 32 return false; 33 } 34 35 // Both SetFilePointerEx and GetFileSizeEx are valid ways to determine the 36 // length of a file. We can use that equivalence to check for end-of-file. 37 38 // This is a racy condition to check - a write or read from another process 39 // could interfere with the size reported from GetFileSizeEx. 40 // In that case, the write function looking to switch from read to write mode 41 // will fail in the usual manner when the write was not possible. 42 LARGE_INTEGER current_position; 43 if (!SetFilePointerEx(os_handle, {}, ¤t_position, FILE_CURRENT)) 44 { 45 return false; 46 } 47 48 LARGE_INTEGER eof_position; 49 if (!GetFileSizeEx(os_handle, &eof_position)) 50 { 51 return false; 52 } 53 54 return current_position.QuadPart == eof_position.QuadPart; 55 } 56 57 template <typename Character> 58 static bool __cdecl write_buffer_nolock( 59 Character const c, 60 __crt_stdio_stream const stream, 61 __crt_cached_ptd_host& ptd 62 ) throw() 63 { 64 typedef __acrt_stdio_char_traits<Character> stdio_traits; 65 66 int const fh = _fileno(stream.public_stream()); 67 68 // If the stream is buffered, write the buffer if there are characters to 69 // be written, then push the character 'c' into the buffer: 70 if (stream.has_big_buffer()) 71 { 72 _ASSERTE(("inconsistent IOB fields", stream->_ptr - stream->_base >= 0)); 73 74 int const chars_to_write = static_cast<int>(stream->_ptr - stream->_base); 75 stream->_ptr = stream->_base + sizeof(Character); 76 stream->_cnt = stream->_bufsiz - static_cast<int>(sizeof(Character)); 77 78 int chars_written = 0; 79 if (chars_to_write > 0) 80 { 81 chars_written = _write_internal(fh, stream->_base, chars_to_write, ptd); 82 } 83 else 84 { 85 if (_osfile_safe(fh) & FAPPEND) 86 { 87 if (_lseeki64(fh, 0, SEEK_END) == -1) 88 { 89 stream.set_flags(_IOERROR); 90 return stdio_traits::eof; 91 } 92 } 93 } 94 95 *reinterpret_cast<Character*>(stream->_base) = c; 96 return chars_written == chars_to_write; 97 } 98 // Otherwise, perform a single character write (if we get here, either 99 // _IONBF is set or there is no buffering): 100 else 101 { 102 return _write_internal(fh, reinterpret_cast<char const*>(&c), sizeof(c), ptd) == sizeof(Character); 103 } 104 } 105 106 // Flushes the buffer of the given stream and writes the character. If the stream 107 // does not have a buffer, one is obtained for it. The character 'c' is written 108 // into the buffer after the buffer is flushed. This function is only intended 109 // to be called from within the library. Returns (W)EOF on failure; otherwise 110 // returns the character 'c' that was written to the file (note that 'c' may be 111 // truncated if the int value cannot be represented by the Character type). 112 template <typename Character> 113 static int __cdecl common_flush_and_write_nolock( 114 int const c, 115 __crt_stdio_stream const stream, 116 __crt_cached_ptd_host& ptd 117 ) throw() 118 { 119 typedef __acrt_stdio_char_traits<Character> stdio_traits; 120 121 unsigned const character_mask = (1 << (CHAR_BIT * sizeof(Character))) - 1; 122 123 _ASSERTE(stream.valid()); 124 125 int const fh = _fileno(stream.public_stream()); 126 127 if (!stream.has_any_of(_IOWRITE | _IOUPDATE)) 128 { 129 ptd.get_errno().set(EBADF); 130 stream.set_flags(_IOERROR); 131 return stdio_traits::eof; 132 } 133 else if (stream.is_string_backed()) 134 { 135 ptd.get_errno().set(ERANGE); 136 stream.set_flags(_IOERROR); 137 return stdio_traits::eof; 138 } 139 140 // Check that _IOREAD is not set or, if it is, then so is _IOEOF. Note 141 // that _IOREAD and IOEOF both being set implies switching from read to 142 // write at end-of-file, which is allowed by ANSI. Note that resetting 143 // the _cnt and _ptr fields amounts to doing an fflush() on the stream 144 // in this case. Note also that the _cnt field has to be reset to 0 for 145 // the error path as well (i.e., _IOREAD set but _IOEOF not set) as 146 // well as the non-error path. 147 148 if (stream.has_any_of(_IOREAD)) 149 { 150 bool const switch_to_write_mode = stream_is_at_end_of_file_nolock(stream); 151 stream->_cnt = 0; // in either case, flush buffer 152 153 if (switch_to_write_mode) 154 { 155 stream->_ptr = stream->_base; 156 stream.unset_flags(_IOREAD); 157 } 158 else 159 { 160 stream.set_flags(_IOERROR); 161 return stdio_traits::eof; 162 } 163 } 164 165 stream.set_flags(_IOWRITE); 166 stream.unset_flags(_IOEOF); 167 168 stream->_cnt = 0; 169 170 // Get a buffer for this stream, if one is necessary: 171 if (!stream.has_any_buffer()) 172 { 173 // If the stream uses temporary buffering, we do not set up a single character buffer; 174 // this is so that later temporary buffering will not be thwarted by 175 // the _IONBF flag being set. (See _stbuf() and _ftbuf() for more 176 //information on stdout and stderr buffering.) 177 if (!__acrt_should_use_temporary_buffer(stream.public_stream())) 178 { 179 __acrt_stdio_allocate_buffer_nolock(stream.public_stream()); 180 } 181 } 182 183 // Write the character; return (W)EOF if it fails: 184 if (!write_buffer_nolock(static_cast<Character>(c & character_mask), stream, ptd)) 185 { 186 stream.set_flags(_IOERROR); 187 return stdio_traits::eof; 188 } 189 190 return c & character_mask; 191 } 192 193 194 195 extern "C" int __cdecl __acrt_stdio_flush_and_write_narrow_nolock( 196 int const c, 197 FILE* const stream, 198 __crt_cached_ptd_host& ptd 199 ) 200 { 201 return common_flush_and_write_nolock<char>(c, __crt_stdio_stream(stream), ptd); 202 } 203 204 205 206 extern "C" int __cdecl __acrt_stdio_flush_and_write_wide_nolock( 207 int const c, 208 FILE* const stream, 209 __crt_cached_ptd_host& ptd 210 ) 211 { 212 return common_flush_and_write_nolock<wchar_t>(c, __crt_stdio_stream(stream), ptd); 213 } 214