69965661178eed8036d108cfd22d38bd5830a38b
[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 (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 | (1 << 7);
107 Descriptor->DevicePointer = SysVars->ActiveCon;
108
109 /* Call the open routine */
110 if (Node->OpenRoutine) Node->OpenRoutine(Node);
111 }
112 else
113 {
114 Descriptor->Win32Handle = StandardHandles[i];
115 }
116 }
117
118 Descriptor->RefCount++;
119 DestinationTable[i] = DescriptorId;
120 }
121 }
122 else
123 {
124 /* Get the parent PSP block and handle table */
125 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
126 SourceTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
127
128 /* Copy the first 20 handles into the new table */
129 for (i = 0; i < DEFAULT_JFT_SIZE; i++)
130 {
131 Descriptor = DosGetFileDescriptor(SourceTable[i]);
132 DestinationTable[i] = SourceTable[i];
133
134 /* Increase the reference count */
135 Descriptor->RefCount++;
136 }
137 }
138 }
139
140 BOOLEAN DosResizeHandleTable(WORD NewSize)
141 {
142 PDOS_PSP PspBlock;
143 LPBYTE HandleTable;
144 WORD Segment;
145
146 /* Get the PSP block */
147 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
148
149 if (NewSize == PspBlock->HandleTableSize)
150 {
151 /* No change */
152 return TRUE;
153 }
154
155 if (PspBlock->HandleTableSize > DEFAULT_JFT_SIZE)
156 {
157 /* Get the segment of the current table */
158 Segment = (LOWORD(PspBlock->HandleTablePtr) >> 4) + HIWORD(PspBlock->HandleTablePtr);
159
160 if (NewSize <= DEFAULT_JFT_SIZE)
161 {
162 /* Get the current handle table */
163 HandleTable = FAR_POINTER(PspBlock->HandleTablePtr);
164
165 /* Copy it to the PSP */
166 RtlCopyMemory(PspBlock->HandleTable, HandleTable, NewSize);
167
168 /* Free the memory */
169 DosFreeMemory(Segment);
170
171 /* Update the handle table pointer and size */
172 PspBlock->HandleTableSize = NewSize;
173 PspBlock->HandleTablePtr = MAKELONG(0x18, CurrentPsp);
174 }
175 else
176 {
177 /* Resize the memory */
178 if (!DosResizeMemory(Segment, NewSize, NULL))
179 {
180 /* Unable to resize, try allocating it somewhere else */
181 Segment = DosAllocateMemory(NewSize, NULL);
182 if (Segment == 0) return FALSE;
183
184 /* Get the new handle table */
185 HandleTable = SEG_OFF_TO_PTR(Segment, 0);
186
187 /* Copy the handles to the new table */
188 RtlCopyMemory(HandleTable,
189 FAR_POINTER(PspBlock->HandleTablePtr),
190 PspBlock->HandleTableSize);
191
192 /* Update the handle table pointer */
193 PspBlock->HandleTablePtr = MAKELONG(0, Segment);
194 }
195
196 /* Update the handle table size */
197 PspBlock->HandleTableSize = NewSize;
198 }
199 }
200 else if (NewSize > DEFAULT_JFT_SIZE)
201 {
202 Segment = DosAllocateMemory(NewSize, NULL);
203 if (Segment == 0) return FALSE;
204
205 /* Get the new handle table */
206 HandleTable = SEG_OFF_TO_PTR(Segment, 0);
207
208 /* Copy the handles from the PSP to the new table */
209 RtlCopyMemory(HandleTable,
210 FAR_POINTER(PspBlock->HandleTablePtr),
211 PspBlock->HandleTableSize);
212
213 /* Update the handle table pointer and size */
214 PspBlock->HandleTableSize = NewSize;
215 PspBlock->HandleTablePtr = MAKELONG(0, Segment);
216 }
217
218 return TRUE;
219 }
220
221
222 WORD DosOpenHandle(BYTE DescriptorId)
223 {
224 WORD DosHandle;
225 PDOS_PSP PspBlock;
226 LPBYTE HandleTable;
227 PDOS_FILE_DESCRIPTOR Descriptor = DosGetFileDescriptor(DescriptorId);
228
229 DPRINT("DosOpenHandle: DescriptorId 0x%02X\n", DescriptorId);
230
231 /* Make sure the descriptor ID is valid */
232 if (Descriptor == NULL) return INVALID_DOS_HANDLE;
233
234 /* The system PSP has no handle table */
235 if (CurrentPsp == SYSTEM_PSP) return INVALID_DOS_HANDLE;
236
237 /* Get a pointer to the handle table */
238 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
239 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
240
241 /* Find a free entry in the JFT */
242 for (DosHandle = 0; DosHandle < PspBlock->HandleTableSize; DosHandle++)
243 {
244 if (HandleTable[DosHandle] == 0xFF) break;
245 }
246
247 /* If there are no free entries, fail */
248 if (DosHandle == PspBlock->HandleTableSize) return INVALID_DOS_HANDLE;
249
250 /* Reference the descriptor */
251 Descriptor->RefCount++;
252
253 /* Set the JFT entry to that descriptor ID */
254 HandleTable[DosHandle] = DescriptorId;
255
256 /* Return the new handle */
257 return DosHandle;
258 }
259
260 BYTE DosQueryHandle(WORD DosHandle)
261 {
262 PDOS_PSP PspBlock;
263 LPBYTE HandleTable;
264
265 DPRINT("DosQueryHandle: DosHandle 0x%04X\n", DosHandle);
266
267 /* The system PSP has no handle table */
268 if (CurrentPsp == SYSTEM_PSP) return 0xFF;
269
270 /* Get a pointer to the handle table */
271 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
272 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
273
274 /* Return the descriptor ID */
275 return HandleTable[DosHandle];
276 }
277
278 WORD DosDuplicateHandle(WORD DosHandle)
279 {
280 BYTE DescriptorId = DosQueryHandle(DosHandle);
281
282 if (DescriptorId == 0xFF)
283 {
284 DosLastError = ERROR_INVALID_HANDLE;
285 return INVALID_DOS_HANDLE;
286 }
287
288 return DosOpenHandle(DescriptorId);
289 }
290
291 BOOLEAN DosForceDuplicateHandle(WORD OldHandle, WORD NewHandle)
292 {
293 BYTE DescriptorId;
294 PDOS_PSP PspBlock;
295 LPBYTE HandleTable;
296 PDOS_FILE_DESCRIPTOR Descriptor;
297
298 DPRINT("DosForceDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
299 OldHandle,
300 NewHandle);
301
302 /* The system PSP has no handle table */
303 if (CurrentPsp == SYSTEM_PSP) return FALSE;
304
305 /* Get a pointer to the handle table */
306 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
307 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
308
309 /* Make sure the old handle is open */
310 if (HandleTable[OldHandle] == 0xFF) return FALSE;
311
312 /* Check if the new handle is open */
313 if (HandleTable[NewHandle] != 0xFF)
314 {
315 /* Close it */
316 DosCloseHandle(NewHandle);
317 }
318
319 DescriptorId = HandleTable[OldHandle];
320 Descriptor = DosGetFileDescriptor(DescriptorId);
321 if (Descriptor == NULL) return FALSE;
322
323 /* Increment the reference count of the descriptor */
324 Descriptor->RefCount++;
325
326 /* Make the new handle point to that descriptor */
327 HandleTable[NewHandle] = DescriptorId;
328
329 /* Return success */
330 return TRUE;
331 }
332
333 BOOLEAN DosCloseHandle(WORD DosHandle)
334 {
335 PDOS_PSP PspBlock;
336 LPBYTE HandleTable;
337 PDOS_FILE_DESCRIPTOR Descriptor;
338
339 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle);
340
341 /* The system PSP has no handle table */
342 if (CurrentPsp == SYSTEM_PSP) return FALSE;
343
344 /* Get a pointer to the handle table */
345 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
346 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
347
348 /* Make sure the handle is open */
349 if (HandleTable[DosHandle] == 0xFF) return FALSE;
350
351 /* Make sure the descriptor is valid */
352 Descriptor = DosGetFileDescriptor(HandleTable[DosHandle]);
353 if (Descriptor == NULL) return FALSE;
354
355 /* Decrement the reference count of the descriptor */
356 Descriptor->RefCount--;
357
358 /* Check if the reference count fell to zero */
359 if (!Descriptor->RefCount)
360 {
361 if (Descriptor->DeviceInfo & (1 << 7))
362 {
363 PDOS_DEVICE_NODE Node = DosGetDriverNode(Descriptor->DevicePointer);
364
365 /* Call the close routine, if it exists */
366 if (Node->CloseRoutine) Node->CloseRoutine(Node);
367 }
368 else
369 {
370 /* Close the win32 handle */
371 CloseHandle(Descriptor->Win32Handle);
372 }
373 }
374
375 /* Clear the entry in the JFT */
376 HandleTable[DosHandle] = 0xFF;
377
378 return TRUE;
379 }