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