Reactos
at master 266 lines 9.1 kB view raw
1// 2// ioinit.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// Defines initialization and termination routines for the lowio library, along 7// with global data shared by most of the lowio library. 8// 9#include <corecrt_internal_lowio.h> 10#include <corecrt_internal_stdio.h> 11 12 13 14// This is a special static lowio file object referenced only by the safe access 15// functionality in the internal headers. It is used in certain stdio-level 16// functions to more gracefully handle a FILE with -1 as its lowio file id. 17extern "C" { __crt_lowio_handle_data __badioinfo = 18{ 19 { }, // lock 20 static_cast<intptr_t>(-1), // osfhnd 21 0, // startpos 22 FTEXT, // osfile 23 __crt_lowio_text_mode::ansi, // textmode 24 { LF, LF, LF }, // _pipe_lookahead 25}; } 26 27 28 29// This is the number of lowio file objects that have been allocated. This 30// includes both in-use and unused elements, since not all allocated files 31// are necessarily in use at any given time. 32// 33// This number is in the range of [IOINFO_ARRAY_ELTS, _NHANDLE_] 34extern "C" { int _nhandle = 0; } 35 36 37 38// This is the global array of file object arrays: 39extern "C" { __crt_lowio_handle_data* __pioinfo[IOINFO_ARRAYS] = { 0 }; } 40 41 42 43static DWORD __cdecl get_std_handle_id(int const fh) throw() 44{ 45 // Convert the CRT file handle to the OS file handle for the three 46 // standard streams: 47 switch (fh) 48 { 49 case 0: return STD_INPUT_HANDLE; 50 case 1: return STD_OUTPUT_HANDLE; 51 case 2: return STD_ERROR_HANDLE; 52 } 53 54 return STD_ERROR_HANDLE; // Unreachable, but the compiler can't know. 55} 56 57 58 59static void __cdecl initialize_inherited_file_handles_nolock() throw() 60{ 61 STARTUPINFOW startup_info; 62 GetStartupInfoW(&startup_info); 63 64 // First check and see if we inherited any file handles. If we didn't, then 65 // we don't have anything to initialize: 66 if (startup_info.cbReserved2 == 0 || startup_info.lpReserved2 == nullptr) 67 return; 68 69 // Get the number of inherited handles: 70 int const handle_count = *reinterpret_cast<UNALIGNED int*>(startup_info.lpReserved2); 71 72 // Compute the start of the passed file info and OS HANDLEs: 73 unsigned char* const first_file = 74 reinterpret_cast<unsigned char*>(startup_info.lpReserved2) + sizeof(int); 75 76 UNALIGNED intptr_t* const first_handle = 77 reinterpret_cast<UNALIGNED intptr_t*>(first_file + handle_count); 78 79 // Do not attempt to inherit more than the maximum number of supported handles: 80 int handles_to_inherit = handle_count < _NHANDLE_ 81 ? handle_count 82 : _NHANDLE_; 83 84 // Attempt to allocate the required number of handles. If we fail for any 85 // reason, we'll inherit as many handles as we can: 86 __acrt_lowio_ensure_fh_exists(handles_to_inherit); 87 if (handles_to_inherit > _nhandle) 88 handles_to_inherit = _nhandle; 89 90 // Validate and copy the provided file information: 91 unsigned char* it_file = first_file; 92 UNALIGNED intptr_t* it_handle = first_handle; 93 94 for (int fh = 0; fh != handles_to_inherit; ++fh, ++it_file, ++it_handle) 95 { 96 HANDLE const real_handle = reinterpret_cast<HANDLE>(*it_handle); 97 98 // If the provided information does not appear to describe an open, 99 // valid file or device, skip it: 100 if (real_handle == INVALID_HANDLE_VALUE) 101 continue; 102 103 if (*it_handle == _NO_CONSOLE_FILENO) 104 continue; 105 106 if ((*it_file & FOPEN) == 0) 107 continue; 108 109 // GetFileType cannot be called for pipe handles since it may "hang" if 110 // there is a blocked read pending on the pipe in the parent. 111 if ((*it_file & FPIPE) == 0 && GetFileType(real_handle) == FILE_TYPE_UNKNOWN) 112 continue; 113 114 // Okay, the file looks valid: 115 __crt_lowio_handle_data* const pio = _pioinfo(fh); 116 pio->osfhnd = *it_handle; 117 pio->osfile = *it_file; 118 } 119} 120 121 122 123static void initialize_stdio_handles_nolock() throw() 124{ 125 for (int fh = 0; fh != STDIO_HANDLES_COUNT; ++fh) 126 { 127 __crt_lowio_handle_data* const pio = _pioinfo(fh); 128 129 // If this handle was inherited from the parent process and initialized 130 // already, make sure it has the FTEXT flag and continue: 131 if (pio->osfhnd != reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE) && 132 pio->osfhnd != _NO_CONSOLE_FILENO) 133 { 134 pio->osfile |= FTEXT; 135 continue; 136 } 137 138 // Regardless what happens next, the file will be treated as if it is 139 // open in text mode: 140 pio->osfile = FOPEN | FTEXT; 141 142 // This handle has not yet been initialized, so let's see if we can get 143 // the handle from the OS: 144 intptr_t const os_handle = reinterpret_cast<intptr_t>(GetStdHandle(get_std_handle_id(fh))); 145 146 bool const is_valid_handle = 147 os_handle != reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE) && 148 os_handle != reinterpret_cast<intptr_t>(nullptr); 149 150 DWORD const handle_type = is_valid_handle 151 ? GetFileType(reinterpret_cast<HANDLE>(os_handle)) 152 : FILE_TYPE_UNKNOWN; 153 154 155 if (handle_type != FILE_TYPE_UNKNOWN) 156 { 157 // The file type is known, so we obtained a valid handle from the 158 // OS. Finish initializing the lowio file object for this handle, 159 // including the flag specifying whether this is a character device 160 // or a pipe: 161 pio->osfhnd = os_handle; 162 163 if ((handle_type & 0xff) == FILE_TYPE_CHAR) 164 pio->osfile |= FDEV; 165 166 else if ((handle_type & 0xff) == FILE_TYPE_PIPE) 167 pio->osfile |= FPIPE; 168 } 169 else 170 { 171 // We were unable to get the handles from the OS. For stdin, stdout, 172 // and stderr, if there is no valid OS handle, treat the CRT handle 173 // as being open in text mode on a device with _NO_CONSOLE_FILENO 174 // underlying it. We use this value instead of INVALID_HANDLE_VALUE 175 // to distinguish between a failure in opening a file and a program 176 // run without a console: 177 pio->osfile |= FDEV; 178 pio->osfhnd = _NO_CONSOLE_FILENO; 179 180 // Also update the corresponding stdio stream, unless stdio was 181 // already terminated: 182 if (__piob) 183 __piob[fh]->_file = _NO_CONSOLE_FILENO; 184 } 185 } 186} 187 188 189 190// Initializes the lowio library. This initialization comprises several steps: 191// 192// [1] An initial array of __crt_lowio_handle_data structures is allocated. 193// 194// [2] Inherited file handles are initialized. To do this, sthe startup info 195// is obtained from the OS, via the lpReserved2 member. The format of the 196// information is as follows: 197// 198// [Bytes 0 - 3] Integer value N, which is the number of handles that 199// are provided by the parent process. 200// 201// [Bytes 4 - N+3] The N osfile values. 202// 203// [Bytes N+4 - 5*N+3] The N OS HANDLE values, as DWORDs 204// 205// [3] Next, the first three lowio files (corresponding to stdin, stdout, and 206// stderr) are initialized as follows: If the value in osfhnd is 207// INVALID_HANDLE_VALUE, then we try to obtain a HANDLE from the OS. These 208// handles are forced to text mode, as standard input, output, and error 209// always start out in text mode. 210// 211// Notes: 212// 213// [1] In general, not all of the pased info from the parent process will 214// describe open handles. If, for example, only C handle 1 (stdout) and 215// C handle 6 are open in the parent, info for C handles 0 through 6 are 216// passed to the child. 0, 2, 3, 4, and 5 will not describe open handles. 217// 218// [2] Care is taken not to "overflow" the arrays of lowio file objects. 219// 220// [3] See the dospawn logic for the encoding of the file handle info to be 221// passed to a child process. 222// 223// This funtion returns 0 on success; -1 on failure. 224extern "C" bool __cdecl __acrt_initialize_lowio() 225{ 226 __acrt_lock(__acrt_lowio_index_lock); 227 bool result = false; 228 __try 229 { 230 // First, allocate and initialize the initial array of lowio files: 231 if (__acrt_lowio_ensure_fh_exists(0) != 0) 232 __leave; 233 234 // Next, process and initialize all inherited file handles: 235 initialize_inherited_file_handles_nolock(); 236 237 // Finally, initialize the stdio handles, if they were not inherited: 238 initialize_stdio_handles_nolock(); 239 result = true; 240 } 241 __finally 242 { 243 __acrt_unlock(__acrt_lowio_index_lock); 244 } 245 __endtry 246 247 return result; 248} 249 250 251 252// Shuts down the lowio library, freeing all allocated memory and destroying all 253// created synchronization objects. 254extern "C" bool __cdecl __acrt_uninitialize_lowio(bool const /* terminating */) 255{ 256 for (size_t i = 0; i < IOINFO_ARRAYS; ++i) 257 { 258 if (!__pioinfo[i]) 259 continue; 260 261 __acrt_lowio_destroy_handle_array(__pioinfo[i]); 262 __pioinfo[i] = nullptr; 263 } 264 265 return true; 266}