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