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