[NTVDM] Improve the PCH situation.
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dos32krnl / handle.c
1 /*
2 * COPYRIGHT: GPLv2+ - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/handle.c
5 * PURPOSE: DOS32 Handles (Job File Table)
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 #include "ntvdm.h"
10
11 #define NDEBUG
12 #include <debug.h>
13
14 /* PRIVATE FUNCTIONS **********************************************************/
15
16 /* Taken from base/shell/cmd/console.c */
17 static BOOL IsConsoleHandle(HANDLE hHandle)
18 {
19 DWORD dwMode;
20
21 /* Check whether the handle may be that of a console... */
22 if ((GetFileType(hHandle) & FILE_TYPE_CHAR) == 0) return FALSE;
23
24 /*
25 * It may be. Perform another test... The idea comes from the
26 * MSDN description of the WriteConsole API:
27 *
28 * "WriteConsole fails if it is used with a standard handle
29 * that is redirected to a file. If an application processes
30 * multilingual output that can be redirected, determine whether
31 * the output handle is a console handle (one method is to call
32 * the GetConsoleMode function and check whether it succeeds).
33 * If the handle is a console handle, call WriteConsole. If the
34 * handle is not a console handle, the output is redirected and
35 * you should call WriteFile to perform the I/O."
36 */
37 return GetConsoleMode(hHandle, &dwMode);
38 }
39
40 /* PUBLIC FUNCTIONS ***********************************************************/
41
42 VOID DosCopyHandleTable(LPBYTE DestinationTable)
43 {
44 UINT i;
45 PDOS_PSP PspBlock;
46 LPBYTE SourceTable;
47 PDOS_FILE_DESCRIPTOR Descriptor;
48
49 /* Clear the table first */
50 for (i = 0; i < DEFAULT_JFT_SIZE; i++) DestinationTable[i] = 0xFF;
51
52 /* Check if this is the initial process */
53 if (Sda->CurrentPsp == SYSTEM_PSP)
54 {
55 BYTE DescriptorId;
56 HANDLE StandardHandles[3];
57
58 /* Get the native standard handles */
59 StandardHandles[0] = GetStdHandle(STD_INPUT_HANDLE);
60 StandardHandles[1] = GetStdHandle(STD_OUTPUT_HANDLE);
61 StandardHandles[2] = GetStdHandle(STD_ERROR_HANDLE);
62
63 for (i = 0; i < 3; i++)
64 {
65 /* Find the corresponding SFT entry */
66 if (IsConsoleHandle(StandardHandles[i]))
67 {
68 DescriptorId = DosFindDeviceDescriptor(SysVars->ActiveCon);
69 }
70 else
71 {
72 DescriptorId = DosFindWin32Descriptor(StandardHandles[i]);
73 }
74
75 if (DescriptorId != 0xFF)
76 {
77 Descriptor = DosGetFileDescriptor(DescriptorId);
78 }
79 else
80 {
81 /* Create a new SFT entry for it */
82 DescriptorId = DosFindFreeDescriptor();
83 if (DescriptorId == 0xFF)
84 {
85 DPRINT1("Cannot create standard handle %d, the SFT is full!\n", i);
86 continue;
87 }
88
89 Descriptor = DosGetFileDescriptor(DescriptorId);
90 ASSERT(Descriptor != NULL);
91 RtlZeroMemory(Descriptor, sizeof(*Descriptor));
92
93 if (IsConsoleHandle(StandardHandles[i]))
94 {
95 PDOS_DEVICE_NODE Node = DosGetDriverNode(SysVars->ActiveCon);
96
97 Descriptor->DeviceInfo = Node->DeviceAttributes | FILE_INFO_DEVICE;
98 Descriptor->DevicePointer = SysVars->ActiveCon;
99 RtlFillMemory(Descriptor->FileName, sizeof(Descriptor->FileName), ' ');
100 RtlCopyMemory(Descriptor->FileName, Node->Name.Buffer, Node->Name.Length);
101
102 /* Call the open routine */
103 if (Node->OpenRoutine) Node->OpenRoutine(Node);
104 }
105 else
106 {
107 Descriptor->Win32Handle = StandardHandles[i];
108 }
109 }
110
111 Descriptor->RefCount++;
112 DestinationTable[i] = DescriptorId;
113 }
114 }
115 else
116 {
117 /* Get the parent PSP block and handle table */
118 PspBlock = SEGMENT_TO_PSP(Sda->CurrentPsp);
119 SourceTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
120
121 /* Copy the first 20 handles into the new table */
122 for (i = 0; i < DEFAULT_JFT_SIZE; i++)
123 {
124 Descriptor = DosGetFileDescriptor(SourceTable[i]);
125 DestinationTable[i] = SourceTable[i];
126
127 /* Increase the reference count */
128 Descriptor->RefCount++;
129 }
130 }
131 }
132
133 BOOLEAN DosResizeHandleTable(WORD NewSize)
134 {
135 PDOS_PSP PspBlock;
136 LPBYTE HandleTable;
137 WORD Segment;
138
139 /* Get the PSP block */
140 PspBlock = SEGMENT_TO_PSP(Sda->CurrentPsp);
141
142 if (NewSize == PspBlock->HandleTableSize)
143 {
144 /* No change */
145 return TRUE;
146 }
147
148 if (PspBlock->HandleTableSize > DEFAULT_JFT_SIZE)
149 {
150 /* Get the segment of the current table */
151 Segment = (LOWORD(PspBlock->HandleTablePtr) >> 4) + HIWORD(PspBlock->HandleTablePtr);
152
153 if (NewSize <= DEFAULT_JFT_SIZE)
154 {
155 /* Get the current handle table */
156 HandleTable = FAR_POINTER(PspBlock->HandleTablePtr);
157
158 /* Copy it to the PSP */
159 RtlCopyMemory(PspBlock->HandleTable, HandleTable, NewSize);
160
161 /* Free the memory */
162 DosFreeMemory(Segment);
163
164 /* Update the handle table pointer and size */
165 PspBlock->HandleTableSize = NewSize;
166 PspBlock->HandleTablePtr = MAKELONG(0x18, Sda->CurrentPsp);
167 }
168 else
169 {
170 /* Resize the memory */
171 if (!DosResizeMemory(Segment, NewSize, NULL))
172 {
173 /* Unable to resize, try allocating it somewhere else */
174 Segment = DosAllocateMemory(NewSize, NULL);
175 if (Segment == 0) return FALSE;
176
177 /* Get the new handle table */
178 HandleTable = SEG_OFF_TO_PTR(Segment, 0);
179
180 /* Copy the handles to the new table */
181 RtlCopyMemory(HandleTable,
182 FAR_POINTER(PspBlock->HandleTablePtr),
183 PspBlock->HandleTableSize);
184
185 /* Update the handle table pointer */
186 PspBlock->HandleTablePtr = MAKELONG(0, Segment);
187 }
188
189 /* Update the handle table size */
190 PspBlock->HandleTableSize = NewSize;
191 }
192 }
193 else if (NewSize > DEFAULT_JFT_SIZE)
194 {
195 Segment = DosAllocateMemory(NewSize, NULL);
196 if (Segment == 0) return FALSE;
197
198 /* Get the new handle table */
199 HandleTable = SEG_OFF_TO_PTR(Segment, 0);
200
201 /* Copy the handles from the PSP to the new table */
202 RtlCopyMemory(HandleTable,
203 FAR_POINTER(PspBlock->HandleTablePtr),
204 PspBlock->HandleTableSize);
205
206 /* Update the handle table pointer and size */
207 PspBlock->HandleTableSize = NewSize;
208 PspBlock->HandleTablePtr = MAKELONG(0, Segment);
209 }
210
211 return TRUE;
212 }
213
214
215 WORD DosOpenHandle(BYTE DescriptorId)
216 {
217 WORD DosHandle;
218 PDOS_PSP PspBlock;
219 LPBYTE HandleTable;
220 PDOS_FILE_DESCRIPTOR Descriptor = DosGetFileDescriptor(DescriptorId);
221
222 DPRINT("DosOpenHandle: DescriptorId 0x%02X\n", DescriptorId);
223
224 /* Make sure the descriptor ID is valid */
225 if (Descriptor == NULL) return INVALID_DOS_HANDLE;
226
227 /* The system PSP has no handle table */
228 if (Sda->CurrentPsp == SYSTEM_PSP) return INVALID_DOS_HANDLE;
229
230 /* Get a pointer to the handle table */
231 PspBlock = SEGMENT_TO_PSP(Sda->CurrentPsp);
232 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
233
234 /* Find a free entry in the JFT */
235 for (DosHandle = 0; DosHandle < PspBlock->HandleTableSize; DosHandle++)
236 {
237 if (HandleTable[DosHandle] == 0xFF) break;
238 }
239
240 /* If there are no free entries, fail */
241 if (DosHandle == PspBlock->HandleTableSize) return INVALID_DOS_HANDLE;
242
243 /* Reference the descriptor */
244 Descriptor->RefCount++;
245
246 /* Set the JFT entry to that descriptor ID */
247 HandleTable[DosHandle] = DescriptorId;
248
249 /* Return the new handle */
250 return DosHandle;
251 }
252
253 BYTE DosQueryHandle(WORD DosHandle)
254 {
255 PDOS_PSP PspBlock;
256 LPBYTE HandleTable;
257
258 DPRINT("DosQueryHandle: DosHandle 0x%04X\n", DosHandle);
259
260 /* The system PSP has no handle table */
261 if (Sda->CurrentPsp == SYSTEM_PSP) return 0xFF;
262
263 /* Get a pointer to the handle table */
264 PspBlock = SEGMENT_TO_PSP(Sda->CurrentPsp);
265 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
266
267 /* Return the descriptor ID */
268 return HandleTable[DosHandle];
269 }
270
271 WORD DosDuplicateHandle(WORD DosHandle)
272 {
273 BYTE DescriptorId = DosQueryHandle(DosHandle);
274
275 if (DescriptorId == 0xFF)
276 {
277 Sda->LastErrorCode = ERROR_INVALID_HANDLE;
278 return INVALID_DOS_HANDLE;
279 }
280
281 return DosOpenHandle(DescriptorId);
282 }
283
284 BOOLEAN DosForceDuplicateHandle(WORD OldHandle, WORD NewHandle)
285 {
286 BYTE DescriptorId;
287 PDOS_PSP PspBlock;
288 LPBYTE HandleTable;
289 PDOS_FILE_DESCRIPTOR Descriptor;
290
291 DPRINT("DosForceDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
292 OldHandle,
293 NewHandle);
294
295 /* The system PSP has no handle table */
296 if (Sda->CurrentPsp == SYSTEM_PSP) return FALSE;
297
298 /* Get a pointer to the handle table */
299 PspBlock = SEGMENT_TO_PSP(Sda->CurrentPsp);
300 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
301
302 /* Make sure the old handle is open */
303 if (HandleTable[OldHandle] == 0xFF) return FALSE;
304
305 /* Check if the new handle is open */
306 if (HandleTable[NewHandle] != 0xFF)
307 {
308 /* Close it */
309 DosCloseHandle(NewHandle);
310 }
311
312 DescriptorId = HandleTable[OldHandle];
313 Descriptor = DosGetFileDescriptor(DescriptorId);
314 if (Descriptor == NULL) return FALSE;
315
316 /* Increment the reference count of the descriptor */
317 Descriptor->RefCount++;
318
319 /* Make the new handle point to that descriptor */
320 HandleTable[NewHandle] = DescriptorId;
321
322 /* Return success */
323 return TRUE;
324 }
325
326 BOOLEAN DosCloseHandle(WORD DosHandle)
327 {
328 PDOS_PSP PspBlock;
329 LPBYTE HandleTable;
330 PDOS_FILE_DESCRIPTOR Descriptor;
331
332 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle);
333
334 /* The system PSP has no handle table */
335 if (Sda->CurrentPsp == SYSTEM_PSP) return FALSE;
336
337 /* Get a pointer to the handle table */
338 PspBlock = SEGMENT_TO_PSP(Sda->CurrentPsp);
339 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
340
341 /* Make sure the handle is open */
342 if (HandleTable[DosHandle] == 0xFF) return FALSE;
343
344 /* Make sure the descriptor is valid */
345 Descriptor = DosGetFileDescriptor(HandleTable[DosHandle]);
346 if (Descriptor == NULL) return FALSE;
347
348 /* Decrement the reference count of the descriptor */
349 Descriptor->RefCount--;
350
351 /* Check if the reference count fell to zero */
352 if (!Descriptor->RefCount)
353 {
354 if (Descriptor->DeviceInfo & FILE_INFO_DEVICE)
355 {
356 PDOS_DEVICE_NODE Node = DosGetDriverNode(Descriptor->DevicePointer);
357
358 /* Call the close routine, if it exists */
359 if (Node->CloseRoutine) Node->CloseRoutine(Node);
360 }
361 else
362 {
363 /* Close the Win32 handle */
364 CloseHandle(Descriptor->Win32Handle);
365 }
366 }
367
368 /* Clear the entry in the JFT */
369 HandleTable[DosHandle] = 0xFF;
370
371 return TRUE;
372 }