Reactos
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}