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