xref: /reactos/win32ss/user/winsrv/consrv/handle.c (revision 1a83762c)
1 /*
2  * LICENSE:         GPL - See COPYING in the top level directory
3  * PROJECT:         ReactOS Console Server DLL
4  * FILE:            win32ss/user/winsrv/consrv/handle.c
5  * PURPOSE:         Console I/O Handles functions
6  * PROGRAMMERS:     David Welch
7  *                  Jeffrey Morlan
8  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
9  */
10 
11 /* INCLUDES *******************************************************************/
12 
13 #include "consrv.h"
14 
15 #include <win/console.h>
16 
17 #define NDEBUG
18 #include <debug.h>
19 
20 /* GLOBALS ********************************************************************/
21 
22 /* Console handle */
23 typedef struct _CONSOLE_IO_HANDLE
24 {
25     PCONSOLE_IO_OBJECT Object;   /* The object on which the handle points to */
26     ULONG   Access;
27     ULONG   ShareMode;
28     BOOLEAN Inheritable;
29 } CONSOLE_IO_HANDLE, *PCONSOLE_IO_HANDLE;
30 
31 
32 /* PRIVATE FUNCTIONS **********************************************************/
33 
34 static LONG
35 AdjustHandleCounts(IN PCONSOLE_IO_HANDLE Handle,
36                    IN LONG Change)
37 {
38     PCONSOLE_IO_OBJECT Object = Handle->Object;
39 
40     DPRINT("AdjustHandleCounts(0x%p, %d), Object = 0x%p\n",
41            Handle, Change, Object);
42     DPRINT("\tAdjustHandleCounts(0x%p, %d), Object = 0x%p, Object->ReferenceCount = %d, Object->Type = %lu\n",
43            Handle, Change, Object, Object->ReferenceCount, Object->Type);
44 
45     if (Handle->Access & GENERIC_READ)           Object->AccessRead += Change;
46     if (Handle->Access & GENERIC_WRITE)          Object->AccessWrite += Change;
47     if (!(Handle->ShareMode & FILE_SHARE_READ))  Object->ExclusiveRead += Change;
48     if (!(Handle->ShareMode & FILE_SHARE_WRITE)) Object->ExclusiveWrite += Change;
49 
50     Object->ReferenceCount += Change;
51 
52     return Object->ReferenceCount;
53 }
54 
55 static VOID
56 ConSrvCloseHandle(IN PCONSOLE_IO_HANDLE Handle)
57 {
58     PCONSOLE_IO_OBJECT Object = Handle->Object;
59     if (Object != NULL)
60     {
61         /*
62          * If this is a input handle, notify and dereference
63          * all the waits related to this handle.
64          */
65         if (Object->Type == INPUT_BUFFER)
66         {
67             // PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
68             PCONSOLE Console = Object->Console;
69 
70             /*
71              * Wake up all the writing waiters related to this handle for this
72              * input buffer, if any, then dereference them and purge them all
73              * from the list.
74              * To select them amongst all the waiters for this input buffer,
75              * pass the handle pointer to the waiters, then they will check
76              * whether or not they are related to this handle and if so, they
77              * return.
78              */
79             CsrNotifyWait(&Console->ReadWaitQueue,
80                           TRUE,
81                           NULL,
82                           (PVOID)Handle);
83             if (!IsListEmpty(&Console->ReadWaitQueue))
84             {
85                 CsrDereferenceWait(&Console->ReadWaitQueue);
86             }
87         }
88 
89         /* If the last handle to a screen buffer is closed, delete it... */
90         if (AdjustHandleCounts(Handle, -1) == 0)
91         {
92             if (Object->Type == TEXTMODE_BUFFER || Object->Type == GRAPHICS_BUFFER)
93             {
94                 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
95                 /* ...unless it's the only buffer left. Windows allows deletion
96                  * even of the last buffer, but having to deal with a lack of
97                  * any active buffer might be error-prone. */
98                 if (Buffer->ListEntry.Flink != Buffer->ListEntry.Blink)
99                     ConDrvDeleteScreenBuffer(Buffer);
100             }
101             else if (Object->Type == INPUT_BUFFER)
102             {
103                 DPRINT("Closing the input buffer\n");
104             }
105             else
106             {
107                 DPRINT1("Invalid object type %d\n", Object->Type);
108             }
109         }
110 
111         /* Invalidate (zero-out) this handle entry */
112         // Handle->Object = NULL;
113         // RtlZeroMemory(Handle, sizeof(*Handle));
114     }
115     RtlZeroMemory(Handle, sizeof(*Handle)); // Be sure the whole entry is invalidated.
116 }
117 
118 
119 
120 
121 
122 
123 /* Forward declaration, used in ConSrvInitHandlesTable */
124 static VOID ConSrvFreeHandlesTable(PCONSOLE_PROCESS_DATA ProcessData);
125 
126 static NTSTATUS
127 ConSrvInitHandlesTable(IN OUT PCONSOLE_PROCESS_DATA ProcessData,
128                        IN PCONSOLE Console,
129                        OUT PHANDLE pInputHandle,
130                        OUT PHANDLE pOutputHandle,
131                        OUT PHANDLE pErrorHandle)
132 {
133     NTSTATUS Status;
134     HANDLE InputHandle  = INVALID_HANDLE_VALUE,
135            OutputHandle = INVALID_HANDLE_VALUE,
136            ErrorHandle  = INVALID_HANDLE_VALUE;
137 
138     /*
139      * Initialize the handles table. Use temporary variables to store
140      * the handles values in such a way that, if we fail, we don't
141      * return to the caller invalid handle values.
142      *
143      * Insert the IO handles.
144      */
145 
146     RtlEnterCriticalSection(&ProcessData->HandleTableLock);
147 
148     /* Insert the Input handle */
149     Status = ConSrvInsertObject(ProcessData,
150                                 &InputHandle,
151                                 &Console->InputBuffer.Header,
152                                 GENERIC_READ | GENERIC_WRITE,
153                                 TRUE,
154                                 FILE_SHARE_READ | FILE_SHARE_WRITE);
155     if (!NT_SUCCESS(Status))
156     {
157         DPRINT1("Failed to insert the input handle\n");
158         RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
159         ConSrvFreeHandlesTable(ProcessData);
160         return Status;
161     }
162 
163     /* Insert the Output handle */
164     Status = ConSrvInsertObject(ProcessData,
165                                 &OutputHandle,
166                                 &Console->ActiveBuffer->Header,
167                                 GENERIC_READ | GENERIC_WRITE,
168                                 TRUE,
169                                 FILE_SHARE_READ | FILE_SHARE_WRITE);
170     if (!NT_SUCCESS(Status))
171     {
172         DPRINT1("Failed to insert the output handle\n");
173         RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
174         ConSrvFreeHandlesTable(ProcessData);
175         return Status;
176     }
177 
178     /* Insert the Error handle */
179     Status = ConSrvInsertObject(ProcessData,
180                                 &ErrorHandle,
181                                 &Console->ActiveBuffer->Header,
182                                 GENERIC_READ | GENERIC_WRITE,
183                                 TRUE,
184                                 FILE_SHARE_READ | FILE_SHARE_WRITE);
185     if (!NT_SUCCESS(Status))
186     {
187         DPRINT1("Failed to insert the error handle\n");
188         RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
189         ConSrvFreeHandlesTable(ProcessData);
190         return Status;
191     }
192 
193     /* Return the newly created handles */
194     *pInputHandle  = InputHandle;
195     *pOutputHandle = OutputHandle;
196     *pErrorHandle  = ErrorHandle;
197 
198     RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
199     return STATUS_SUCCESS;
200 }
201 
202 NTSTATUS
203 ConSrvInheritHandlesTable(IN PCONSOLE_PROCESS_DATA SourceProcessData,
204                           IN PCONSOLE_PROCESS_DATA TargetProcessData)
205 {
206     NTSTATUS Status = STATUS_SUCCESS;
207     ULONG i, j;
208 
209     RtlEnterCriticalSection(&SourceProcessData->HandleTableLock);
210 
211     /* Inherit a handles table only if there is no already */
212     if (TargetProcessData->HandleTable != NULL /* || TargetProcessData->HandleTableSize != 0 */)
213     {
214         Status = STATUS_UNSUCCESSFUL;
215         goto Quit;
216     }
217 
218     /* Allocate a new handle table for the child process */
219     TargetProcessData->HandleTable = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
220                                                       SourceProcessData->HandleTableSize
221                                                         * sizeof(CONSOLE_IO_HANDLE));
222     if (TargetProcessData->HandleTable == NULL)
223     {
224         Status = STATUS_NO_MEMORY;
225         goto Quit;
226     }
227 
228     TargetProcessData->HandleTableSize = SourceProcessData->HandleTableSize;
229 
230     /*
231      * Parse the parent process' handles table and, for each handle,
232      * do a copy of it and reference it, if the handle is inheritable.
233      */
234     for (i = 0, j = 0; i < SourceProcessData->HandleTableSize; i++)
235     {
236         if (SourceProcessData->HandleTable[i].Object != NULL &&
237             SourceProcessData->HandleTable[i].Inheritable)
238         {
239             /*
240              * Copy the handle data and increment the reference count of the
241              * pointed object (via the call to ConSrvCreateHandleEntry == AdjustHandleCounts).
242              */
243             TargetProcessData->HandleTable[j] = SourceProcessData->HandleTable[i];
244             AdjustHandleCounts(&TargetProcessData->HandleTable[j], +1);
245             ++j;
246         }
247     }
248 
249 Quit:
250     RtlLeaveCriticalSection(&SourceProcessData->HandleTableLock);
251     return Status;
252 }
253 
254 static VOID
255 ConSrvFreeHandlesTable(IN PCONSOLE_PROCESS_DATA ProcessData)
256 {
257     RtlEnterCriticalSection(&ProcessData->HandleTableLock);
258 
259     if (ProcessData->HandleTable != NULL)
260     {
261         ULONG i;
262 
263         /*
264          * ProcessData->ConsoleHandle is NULL (and the assertion fails) when
265          * ConSrvFreeHandlesTable is called in ConSrvConnect during the
266          * allocation of a new console.
267          */
268         // ASSERT(ProcessData->ConsoleHandle);
269         if (ProcessData->ConsoleHandle != NULL)
270         {
271             /* Close all the console handles */
272             for (i = 0; i < ProcessData->HandleTableSize; i++)
273             {
274                 ConSrvCloseHandle(&ProcessData->HandleTable[i]);
275             }
276         }
277         /* Free the handles table memory */
278         ConsoleFreeHeap(ProcessData->HandleTable);
279         ProcessData->HandleTable = NULL;
280     }
281 
282     ProcessData->HandleTableSize = 0;
283 
284     RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
285 }
286 
287 
288 
289 
290 
291 
292 // ConSrvCreateObject
293 VOID
294 ConSrvInitObject(IN OUT PCONSOLE_IO_OBJECT Object,
295                  IN CONSOLE_IO_OBJECT_TYPE Type,
296                  IN PCONSOLE Console)
297 {
298     ASSERT(Object);
299     // if (!Object) return;
300 
301     Object->Type    = Type;
302     Object->Console = Console;
303     Object->ReferenceCount = 0;
304 
305     Object->AccessRead    = Object->AccessWrite    = 0;
306     Object->ExclusiveRead = Object->ExclusiveWrite = 0;
307 }
308 
309 NTSTATUS
310 ConSrvInsertObject(IN PCONSOLE_PROCESS_DATA ProcessData,
311                    OUT PHANDLE Handle,
312                    IN PCONSOLE_IO_OBJECT Object,
313                    IN ULONG Access,
314                    IN BOOLEAN Inheritable,
315                    IN ULONG ShareMode)
316 {
317 #define IO_HANDLES_INCREMENT    2 * 3
318 
319     ULONG i = 0;
320     PCONSOLE_IO_HANDLE Block;
321 
322     // NOTE: Commented out because calling code always lock HandleTableLock before.
323     // RtlEnterCriticalSection(&ProcessData->HandleTableLock);
324 
325     ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
326             (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
327 
328     if (ProcessData->HandleTable)
329     {
330         for (i = 0; i < ProcessData->HandleTableSize; i++)
331         {
332             if (ProcessData->HandleTable[i].Object == NULL)
333                 break;
334         }
335     }
336 
337     if (i >= ProcessData->HandleTableSize)
338     {
339         /* Allocate a new handles table */
340         Block = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
341                                  (ProcessData->HandleTableSize +
342                                     IO_HANDLES_INCREMENT) * sizeof(CONSOLE_IO_HANDLE));
343         if (Block == NULL)
344         {
345             // RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
346             return STATUS_UNSUCCESSFUL;
347         }
348 
349         /* If we previously had a handles table, free it and use the new one */
350         if (ProcessData->HandleTable)
351         {
352             /* Copy the handles from the old table to the new one */
353             RtlCopyMemory(Block,
354                           ProcessData->HandleTable,
355                           ProcessData->HandleTableSize * sizeof(CONSOLE_IO_HANDLE));
356             ConsoleFreeHeap(ProcessData->HandleTable);
357         }
358         ProcessData->HandleTable = Block;
359         ProcessData->HandleTableSize += IO_HANDLES_INCREMENT;
360     }
361 
362     ProcessData->HandleTable[i].Object      = Object;
363     ProcessData->HandleTable[i].Access      = Access;
364     ProcessData->HandleTable[i].Inheritable = Inheritable;
365     ProcessData->HandleTable[i].ShareMode   = ShareMode;
366     AdjustHandleCounts(&ProcessData->HandleTable[i], +1);
367     *Handle = ULongToHandle((i << 2) | 0x3);
368 
369     // RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
370 
371     return STATUS_SUCCESS;
372 }
373 
374 NTSTATUS
375 ConSrvRemoveObject(IN PCONSOLE_PROCESS_DATA ProcessData,
376                    IN HANDLE Handle)
377 {
378     ULONG Index = HandleToULong(Handle) >> 2;
379 
380     RtlEnterCriticalSection(&ProcessData->HandleTableLock);
381 
382     ASSERT(ProcessData->HandleTable);
383     // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
384     //         (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
385 
386     if (Index >= ProcessData->HandleTableSize ||
387         ProcessData->HandleTable[Index].Object == NULL)
388     {
389         RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
390         return STATUS_INVALID_HANDLE;
391     }
392 
393     ASSERT(ProcessData->ConsoleHandle);
394     ConSrvCloseHandle(&ProcessData->HandleTable[Index]);
395 
396     RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
397     return STATUS_SUCCESS;
398 }
399 
400 NTSTATUS
401 ConSrvGetObject(IN PCONSOLE_PROCESS_DATA ProcessData,
402                 IN HANDLE Handle,
403                 OUT PCONSOLE_IO_OBJECT* Object,
404                 OUT PVOID* Entry OPTIONAL,
405                 IN ULONG Access,
406                 IN BOOLEAN LockConsole,
407                 IN CONSOLE_IO_OBJECT_TYPE Type)
408 {
409     // NTSTATUS Status;
410     ULONG Index = HandleToULong(Handle) >> 2;
411     PCONSOLE_IO_HANDLE HandleEntry = NULL;
412     PCONSOLE_IO_OBJECT ObjectEntry = NULL;
413     // PCONSOLE ObjectConsole;
414 
415     ASSERT(Object);
416     if (Entry) *Entry = NULL;
417 
418     DPRINT("ConSrvGetObject -- Object: 0x%x, Handle: 0x%x\n", Object, Handle);
419 
420     RtlEnterCriticalSection(&ProcessData->HandleTableLock);
421 
422     if ( IsConsoleHandle(Handle) &&
423          Index < ProcessData->HandleTableSize )
424     {
425         HandleEntry = &ProcessData->HandleTable[Index];
426         ObjectEntry = HandleEntry->Object;
427     }
428 
429     if ( HandleEntry == NULL ||
430          ObjectEntry == NULL ||
431          (HandleEntry->Access & Access) == 0 ||
432          /*(Type != 0 && ObjectEntry->Type != Type)*/
433          (Type != 0 && (ObjectEntry->Type & Type) == 0) )
434     {
435         DPRINT("ConSrvGetObject -- Invalid handle 0x%x of type %lu with access %lu ; retrieved object 0x%x (handle 0x%x) of type %lu with access %lu\n",
436                Handle, Type, Access, ObjectEntry, HandleEntry, (ObjectEntry ? ObjectEntry->Type : 0), (HandleEntry ? HandleEntry->Access : 0));
437 
438         RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
439         return STATUS_INVALID_HANDLE;
440     }
441 
442     RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
443 
444     // Status = ConSrvGetConsole(ProcessData, &ObjectConsole, LockConsole);
445     // if (NT_SUCCESS(Status))
446     if (ConDrvValidateConsoleUnsafe(ObjectEntry->Console, CONSOLE_RUNNING, LockConsole))
447     {
448         _InterlockedIncrement(&ObjectEntry->Console->ReferenceCount);
449 
450         /* Return the objects to the caller */
451         *Object = ObjectEntry;
452         if (Entry) *Entry = HandleEntry;
453 
454         // RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
455         return STATUS_SUCCESS;
456     }
457     else
458     {
459         // RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
460         return STATUS_INVALID_HANDLE;
461     }
462 }
463 
464 VOID
465 ConSrvReleaseObject(IN PCONSOLE_IO_OBJECT Object,
466                     IN BOOLEAN IsConsoleLocked)
467 {
468     ConSrvReleaseConsole(Object->Console, IsConsoleLocked);
469 }
470 
471 
472 
473 NTSTATUS
474 ConSrvAllocateConsole(PCONSOLE_PROCESS_DATA ProcessData,
475                       PHANDLE pInputHandle,
476                       PHANDLE pOutputHandle,
477                       PHANDLE pErrorHandle,
478                       PCONSOLE_INIT_INFO ConsoleInitInfo)
479 {
480     NTSTATUS Status = STATUS_SUCCESS;
481     HANDLE ConsoleHandle;
482     PCONSRV_CONSOLE Console;
483 
484     /*
485      * We are about to create a new console. However when ConSrvNewProcess
486      * was called, we didn't know that we wanted to create a new console and
487      * therefore, we by default inherited the handles table from our parent
488      * process. It's only now that we notice that in fact we do not need
489      * them, because we've created a new console and thus we must use it.
490      *
491      * Therefore, free the handles table so that we can recreate
492      * a new one later on.
493      */
494     ConSrvFreeHandlesTable(ProcessData);
495 
496     /* Initialize a new Console owned by this process */
497     DPRINT("Initialization of console '%S' for process '%S' on desktop '%S'\n",
498            ConsoleInitInfo->ConsoleTitle ? ConsoleInitInfo->ConsoleTitle : L"n/a",
499            ConsoleInitInfo->AppName ? ConsoleInitInfo->AppName : L"n/a",
500            ConsoleInitInfo->Desktop ? ConsoleInitInfo->Desktop : L"n/a");
501     Status = ConSrvInitConsole(&ConsoleHandle,
502                                &Console,
503                                ConsoleInitInfo,
504                                ProcessData->Process);
505     if (!NT_SUCCESS(Status))
506     {
507         DPRINT1("Console initialization failed\n");
508         return Status;
509     }
510 
511     /* Assign the new console handle */
512     ProcessData->ConsoleHandle = ConsoleHandle;
513 
514     /* Initialize the handles table */
515     Status = ConSrvInitHandlesTable(ProcessData,
516                                     Console,
517                                     pInputHandle,
518                                     pOutputHandle,
519                                     pErrorHandle);
520     if (!NT_SUCCESS(Status))
521     {
522         DPRINT1("Failed to initialize the handles table\n");
523         ConSrvDeleteConsole(Console);
524         ProcessData->ConsoleHandle = NULL;
525         return Status;
526     }
527 
528     /* Duplicate the Initialization Events */
529     Status = NtDuplicateObject(NtCurrentProcess(),
530                                Console->InitEvents[INIT_SUCCESS],
531                                ProcessData->Process->ProcessHandle,
532                                &ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS],
533                                EVENT_ALL_ACCESS, 0, 0);
534     if (!NT_SUCCESS(Status))
535     {
536         DPRINT1("NtDuplicateObject(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status);
537         ConSrvFreeHandlesTable(ProcessData);
538         ConSrvDeleteConsole(Console);
539         ProcessData->ConsoleHandle = NULL;
540         return Status;
541     }
542 
543     Status = NtDuplicateObject(NtCurrentProcess(),
544                                Console->InitEvents[INIT_FAILURE],
545                                ProcessData->Process->ProcessHandle,
546                                &ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_FAILURE],
547                                EVENT_ALL_ACCESS, 0, 0);
548     if (!NT_SUCCESS(Status))
549     {
550         DPRINT1("NtDuplicateObject(InitEvents[INIT_FAILURE]) failed: %lu\n", Status);
551         NtDuplicateObject(ProcessData->Process->ProcessHandle,
552                           ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS],
553                           NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
554         ConSrvFreeHandlesTable(ProcessData);
555         ConSrvDeleteConsole(Console);
556         ProcessData->ConsoleHandle = NULL;
557         return Status;
558     }
559 
560     /* Duplicate the Input Event */
561     Status = NtDuplicateObject(NtCurrentProcess(),
562                                Console->InputBuffer.ActiveEvent,
563                                ProcessData->Process->ProcessHandle,
564                                &ConsoleInitInfo->ConsoleStartInfo->InputWaitHandle,
565                                EVENT_ALL_ACCESS, 0, 0);
566     if (!NT_SUCCESS(Status))
567     {
568         DPRINT1("NtDuplicateObject(InputWaitHandle) failed: %lu\n", Status);
569         NtDuplicateObject(ProcessData->Process->ProcessHandle,
570                           ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_FAILURE],
571                           NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
572         NtDuplicateObject(ProcessData->Process->ProcessHandle,
573                           ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS],
574                           NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
575         ConSrvFreeHandlesTable(ProcessData);
576         ConSrvDeleteConsole(Console);
577         ProcessData->ConsoleHandle = NULL;
578         return Status;
579     }
580 
581     /* Mark the process as having a console */
582     ProcessData->ConsoleApp = TRUE;
583     ProcessData->Process->Flags |= CsrProcessIsConsoleApp;
584 
585     /* Return the console handle to the caller */
586     ConsoleInitInfo->ConsoleStartInfo->ConsoleHandle = ProcessData->ConsoleHandle;
587 
588     /*
589      * Insert the process into the processes list of the console,
590      * and set its foreground priority.
591      */
592     InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink);
593     ConSrvSetProcessFocus(ProcessData->Process, Console->HasFocus);
594 
595     /* Add a reference count because the process is tied to the console */
596     _InterlockedIncrement(&Console->ReferenceCount);
597 
598     /* Update the internal info of the terminal */
599     TermRefreshInternalInfo(Console);
600 
601     return STATUS_SUCCESS;
602 }
603 
604 NTSTATUS
605 ConSrvInheritConsole(PCONSOLE_PROCESS_DATA ProcessData,
606                      HANDLE ConsoleHandle,
607                      BOOLEAN CreateNewHandlesTable,
608                      PHANDLE pInputHandle,
609                      PHANDLE pOutputHandle,
610                      PHANDLE pErrorHandle,
611                      PCONSOLE_START_INFO ConsoleStartInfo)
612 {
613     NTSTATUS Status = STATUS_SUCCESS;
614     PCONSOLE Console;
615 
616     /* Validate and lock the console */
617     if (!ConSrvValidateConsole(&Console,
618                                ConsoleHandle,
619                                CONSOLE_RUNNING, TRUE))
620     {
621         // FIXME: Find another status code
622         return STATUS_UNSUCCESSFUL;
623     }
624 
625     /* Inherit the console */
626     ProcessData->ConsoleHandle = ConsoleHandle;
627 
628     if (CreateNewHandlesTable)
629     {
630         /*
631          * We are about to create a new console. However when ConSrvNewProcess
632          * was called, we didn't know that we wanted to create a new console and
633          * therefore, we by default inherited the handles table from our parent
634          * process. It's only now that we notice that in fact we do not need
635          * them, because we've created a new console and thus we must use it.
636          *
637          * Therefore, free the handles table so that we can recreate
638          * a new one later on.
639          */
640         ConSrvFreeHandlesTable(ProcessData);
641 
642         /* Initialize the handles table */
643         Status = ConSrvInitHandlesTable(ProcessData,
644                                         Console,
645                                         pInputHandle,
646                                         pOutputHandle,
647                                         pErrorHandle);
648         if (!NT_SUCCESS(Status))
649         {
650             DPRINT1("Failed to initialize the handles table\n");
651             ProcessData->ConsoleHandle = NULL;
652             goto Quit;
653         }
654     }
655 
656     /* Duplicate the Initialization Events */
657     Status = NtDuplicateObject(NtCurrentProcess(),
658                                Console->InitEvents[INIT_SUCCESS],
659                                ProcessData->Process->ProcessHandle,
660                                &ConsoleStartInfo->InitEvents[INIT_SUCCESS],
661                                EVENT_ALL_ACCESS, 0, 0);
662     if (!NT_SUCCESS(Status))
663     {
664         DPRINT1("NtDuplicateObject(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status);
665         ConSrvFreeHandlesTable(ProcessData);
666         ProcessData->ConsoleHandle = NULL;
667         goto Quit;
668     }
669 
670     Status = NtDuplicateObject(NtCurrentProcess(),
671                                Console->InitEvents[INIT_FAILURE],
672                                ProcessData->Process->ProcessHandle,
673                                &ConsoleStartInfo->InitEvents[INIT_FAILURE],
674                                EVENT_ALL_ACCESS, 0, 0);
675     if (!NT_SUCCESS(Status))
676     {
677         DPRINT1("NtDuplicateObject(InitEvents[INIT_FAILURE]) failed: %lu\n", Status);
678         NtDuplicateObject(ProcessData->Process->ProcessHandle,
679                           ConsoleStartInfo->InitEvents[INIT_SUCCESS],
680                           NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
681         ConSrvFreeHandlesTable(ProcessData);
682         ProcessData->ConsoleHandle = NULL;
683         goto Quit;
684     }
685 
686     /* Duplicate the Input Event */
687     Status = NtDuplicateObject(NtCurrentProcess(),
688                                Console->InputBuffer.ActiveEvent,
689                                ProcessData->Process->ProcessHandle,
690                                &ConsoleStartInfo->InputWaitHandle,
691                                EVENT_ALL_ACCESS, 0, 0);
692     if (!NT_SUCCESS(Status))
693     {
694         DPRINT1("NtDuplicateObject(InputWaitHandle) failed: %lu\n", Status);
695         NtDuplicateObject(ProcessData->Process->ProcessHandle,
696                           ConsoleStartInfo->InitEvents[INIT_FAILURE],
697                           NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
698         NtDuplicateObject(ProcessData->Process->ProcessHandle,
699                           ConsoleStartInfo->InitEvents[INIT_SUCCESS],
700                           NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
701         ConSrvFreeHandlesTable(ProcessData); // NOTE: Always free the handles table.
702         ProcessData->ConsoleHandle = NULL;
703         goto Quit;
704     }
705 
706     /* Mark the process as having a console */
707     ProcessData->ConsoleApp = TRUE;
708     ProcessData->Process->Flags |= CsrProcessIsConsoleApp;
709 
710     /* Return the console handle to the caller */
711     ConsoleStartInfo->ConsoleHandle = ProcessData->ConsoleHandle;
712 
713     /*
714      * Insert the process into the processes list of the console,
715      * and set its foreground priority.
716      */
717     InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink);
718     ConSrvSetProcessFocus(ProcessData->Process, Console->HasFocus);
719 
720     /* Add a reference count because the process is tied to the console */
721     _InterlockedIncrement(&Console->ReferenceCount);
722 
723     /* Update the internal info of the terminal */
724     TermRefreshInternalInfo(Console);
725 
726     Status = STATUS_SUCCESS;
727 
728 Quit:
729     /* Unlock the console and return */
730     LeaveCriticalSection(&Console->Lock);
731     return Status;
732 }
733 
734 NTSTATUS
735 ConSrvRemoveConsole(PCONSOLE_PROCESS_DATA ProcessData)
736 {
737     PCONSOLE Console;
738     PCONSOLE_PROCESS_DATA ConsoleLeaderProcess;
739 
740     DPRINT("ConSrvRemoveConsole\n");
741 
742     /* Mark the process as not having a console anymore */
743     ProcessData->ConsoleApp = FALSE;
744     ProcessData->Process->Flags &= ~CsrProcessIsConsoleApp;
745 
746     /* Validate and lock the console */
747     if (!ConSrvValidateConsole(&Console,
748                                ProcessData->ConsoleHandle,
749                                CONSOLE_RUNNING, TRUE))
750     {
751         // FIXME: Find another status code
752         return STATUS_UNSUCCESSFUL;
753     }
754 
755     DPRINT("ConSrvRemoveConsole - Locking OK\n");
756 
757     /* Retrieve the console leader process */
758     ConsoleLeaderProcess = ConSrvGetConsoleLeaderProcess(Console);
759 
760     /* Close all console handles and free the handles table */
761     ConSrvFreeHandlesTable(ProcessData);
762 
763     /* Detach the process from the console */
764     ProcessData->ConsoleHandle = NULL;
765 
766     /* Remove the process from the console's list of processes */
767     RemoveEntryList(&ProcessData->ConsoleLink);
768 
769     /* Check whether the console should send a last close notification */
770     if (Console->NotifyLastClose)
771     {
772         /* If we are removing the process which wants the last close notification... */
773         if (ProcessData == Console->NotifiedLastCloseProcess)
774         {
775             /* ... just reset the flag and the pointer... */
776             Console->NotifyLastClose = FALSE;
777             Console->NotifiedLastCloseProcess = NULL;
778         }
779         /*
780          * ... otherwise, if we are removing the console leader process
781          * (that cannot be the process wanting the notification, because
782          * the previous case already dealt with it)...
783          */
784         else if (ProcessData == ConsoleLeaderProcess)
785         {
786             /*
787              * ... reset the flag first (so that we avoid multiple notifications)
788              * and then send the last close notification.
789              */
790             Console->NotifyLastClose = FALSE;
791             ConSrvConsoleCtrlEvent(CTRL_LAST_CLOSE_EVENT, Console->NotifiedLastCloseProcess);
792 
793             /* Only now, reset the pointer */
794             Console->NotifiedLastCloseProcess = NULL;
795         }
796     }
797 
798     /* Update the internal info of the terminal */
799     TermRefreshInternalInfo(Console);
800 
801     /* Release the console */
802     DPRINT("ConSrvRemoveConsole - Decrement Console->ReferenceCount = %lu\n", Console->ReferenceCount);
803     ConSrvReleaseConsole(Console, TRUE);
804 
805     return STATUS_SUCCESS;
806 }
807 
808 
809 /* PUBLIC SERVER APIS *********************************************************/
810 
811 CSR_API(SrvOpenConsole)
812 {
813     /*
814      * This API opens a handle to either the input buffer or to
815      * a screen-buffer of the console of the current process.
816      */
817 
818     NTSTATUS Status;
819     PCONSOLE_OPENCONSOLE OpenConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.OpenConsoleRequest;
820     PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
821     PCONSOLE Console;
822 
823     DWORD DesiredAccess = OpenConsoleRequest->DesiredAccess;
824     DWORD ShareMode = OpenConsoleRequest->ShareMode;
825     PCONSOLE_IO_OBJECT Object;
826 
827     OpenConsoleRequest->Handle = INVALID_HANDLE_VALUE;
828 
829     Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
830     if (!NT_SUCCESS(Status))
831     {
832         DPRINT1("Can't get console, status %lx\n", Status);
833         return Status;
834     }
835 
836     RtlEnterCriticalSection(&ProcessData->HandleTableLock);
837 
838     /*
839      * Open a handle to either the active screen buffer or the input buffer.
840      */
841     if (OpenConsoleRequest->HandleType == HANDLE_OUTPUT)
842     {
843         Object = &Console->ActiveBuffer->Header;
844     }
845     else // HANDLE_INPUT
846     {
847         Object = &Console->InputBuffer.Header;
848     }
849 
850     if (((DesiredAccess & GENERIC_READ)  && Object->ExclusiveRead  != 0) ||
851         ((DesiredAccess & GENERIC_WRITE) && Object->ExclusiveWrite != 0) ||
852         (!(ShareMode & FILE_SHARE_READ)  && Object->AccessRead     != 0) ||
853         (!(ShareMode & FILE_SHARE_WRITE) && Object->AccessWrite    != 0))
854     {
855         DPRINT1("Sharing violation\n");
856         Status = STATUS_SHARING_VIOLATION;
857     }
858     else
859     {
860         Status = ConSrvInsertObject(ProcessData,
861                                     &OpenConsoleRequest->Handle,
862                                     Object,
863                                     DesiredAccess,
864                                     OpenConsoleRequest->InheritHandle,
865                                     ShareMode);
866     }
867 
868     RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
869 
870     ConSrvReleaseConsole(Console, TRUE);
871     return Status;
872 }
873 
874 /* API_NUMBER: ConsolepDuplicateHandle */
875 CSR_API(SrvDuplicateHandle)
876 {
877     NTSTATUS Status;
878     PCONSOLE_DUPLICATEHANDLE DuplicateHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.DuplicateHandleRequest;
879     PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
880     PCONSOLE Console;
881 
882     HANDLE SourceHandle = DuplicateHandleRequest->SourceHandle;
883     ULONG Index = HandleToULong(SourceHandle) >> 2;
884     PCONSOLE_IO_HANDLE Entry;
885     DWORD DesiredAccess;
886 
887     DuplicateHandleRequest->TargetHandle = INVALID_HANDLE_VALUE;
888 
889     Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
890     if (!NT_SUCCESS(Status))
891     {
892         DPRINT1("Can't get console, status %lx\n", Status);
893         return Status;
894     }
895 
896     RtlEnterCriticalSection(&ProcessData->HandleTableLock);
897 
898     // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
899     //         (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
900 
901     if ( /** !IsConsoleHandle(SourceHandle)   || **/
902         Index >= ProcessData->HandleTableSize ||
903         (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
904     {
905         DPRINT1("Couldn't duplicate invalid handle 0x%p\n", SourceHandle);
906         Status = STATUS_INVALID_HANDLE;
907         goto Quit;
908     }
909 
910     if (DuplicateHandleRequest->Options & DUPLICATE_SAME_ACCESS)
911     {
912         DesiredAccess = Entry->Access;
913     }
914     else
915     {
916         DesiredAccess = DuplicateHandleRequest->DesiredAccess;
917         /* Make sure the source handle has all the desired flags */
918         if ((Entry->Access & DesiredAccess) == 0)
919         {
920             DPRINT1("Handle 0x%p only has access %X; requested %X\n",
921                     SourceHandle, Entry->Access, DesiredAccess);
922             Status = STATUS_INVALID_PARAMETER;
923             goto Quit;
924         }
925     }
926 
927     /* Insert the new handle inside the process handles table */
928     Status = ConSrvInsertObject(ProcessData,
929                                 &DuplicateHandleRequest->TargetHandle,
930                                 Entry->Object,
931                                 DesiredAccess,
932                                 DuplicateHandleRequest->InheritHandle,
933                                 Entry->ShareMode);
934     if (NT_SUCCESS(Status) &&
935         (DuplicateHandleRequest->Options & DUPLICATE_CLOSE_SOURCE))
936     {
937         /* Close the original handle if needed */
938         ConSrvCloseHandle(Entry);
939     }
940 
941 Quit:
942     RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
943 
944     ConSrvReleaseConsole(Console, TRUE);
945     return Status;
946 }
947 
948 /* API_NUMBER: ConsolepGetHandleInformation */
949 CSR_API(SrvGetHandleInformation)
950 {
951     NTSTATUS Status;
952     PCONSOLE_GETHANDLEINFO GetHandleInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetHandleInfoRequest;
953     PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
954     PCONSOLE Console;
955 
956     HANDLE Handle = GetHandleInfoRequest->Handle;
957     ULONG Index = HandleToULong(Handle) >> 2;
958     PCONSOLE_IO_HANDLE Entry;
959 
960     Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
961     if (!NT_SUCCESS(Status))
962     {
963         DPRINT1("Can't get console, status %lx\n", Status);
964         return Status;
965     }
966 
967     RtlEnterCriticalSection(&ProcessData->HandleTableLock);
968 
969     ASSERT(ProcessData->HandleTable);
970     // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
971     //         (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
972 
973     if (!IsConsoleHandle(Handle)              ||
974         Index >= ProcessData->HandleTableSize ||
975         (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
976     {
977         Status = STATUS_INVALID_HANDLE;
978         goto Quit;
979     }
980 
981     /*
982      * Retrieve the handle information flags. The console server
983      * doesn't support HANDLE_FLAG_PROTECT_FROM_CLOSE.
984      */
985     GetHandleInfoRequest->Flags = 0;
986     if (Entry->Inheritable) GetHandleInfoRequest->Flags |= HANDLE_FLAG_INHERIT;
987 
988     Status = STATUS_SUCCESS;
989 
990 Quit:
991     RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
992 
993     ConSrvReleaseConsole(Console, TRUE);
994     return Status;
995 }
996 
997 /* API_NUMBER: ConsolepSetHandleInformation */
998 CSR_API(SrvSetHandleInformation)
999 {
1000     NTSTATUS Status;
1001     PCONSOLE_SETHANDLEINFO SetHandleInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetHandleInfoRequest;
1002     PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1003     PCONSOLE Console;
1004 
1005     HANDLE Handle = SetHandleInfoRequest->Handle;
1006     ULONG Index = HandleToULong(Handle) >> 2;
1007     PCONSOLE_IO_HANDLE Entry;
1008 
1009     Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1010     if (!NT_SUCCESS(Status))
1011     {
1012         DPRINT1("Can't get console, status %lx\n", Status);
1013         return Status;
1014     }
1015 
1016     RtlEnterCriticalSection(&ProcessData->HandleTableLock);
1017 
1018     ASSERT(ProcessData->HandleTable);
1019     // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
1020     //         (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
1021 
1022     if (!IsConsoleHandle(Handle)              ||
1023         Index >= ProcessData->HandleTableSize ||
1024         (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
1025     {
1026         Status = STATUS_INVALID_HANDLE;
1027         goto Quit;
1028     }
1029 
1030     /*
1031      * Modify the handle information flags. The console server
1032      * doesn't support HANDLE_FLAG_PROTECT_FROM_CLOSE.
1033      */
1034     if (SetHandleInfoRequest->Mask & HANDLE_FLAG_INHERIT)
1035     {
1036         Entry->Inheritable = ((SetHandleInfoRequest->Flags & HANDLE_FLAG_INHERIT) != 0);
1037     }
1038 
1039     Status = STATUS_SUCCESS;
1040 
1041 Quit:
1042     RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
1043 
1044     ConSrvReleaseConsole(Console, TRUE);
1045     return Status;
1046 }
1047 
1048 /* API_NUMBER: ConsolepCloseHandle */
1049 CSR_API(SrvCloseHandle)
1050 {
1051     NTSTATUS Status;
1052     PCONSOLE_CLOSEHANDLE CloseHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.CloseHandleRequest;
1053     PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1054     PCONSOLE Console;
1055 
1056     Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1057     if (!NT_SUCCESS(Status))
1058     {
1059         DPRINT1("Can't get console, status %lx\n", Status);
1060         return Status;
1061     }
1062 
1063     Status = ConSrvRemoveObject(ProcessData, CloseHandleRequest->Handle);
1064 
1065     ConSrvReleaseConsole(Console, TRUE);
1066     return Status;
1067 }
1068 
1069 /* API_NUMBER: ConsolepVerifyIoHandle */
1070 CSR_API(SrvVerifyConsoleIoHandle)
1071 {
1072     NTSTATUS Status;
1073     PCONSOLE_VERIFYHANDLE VerifyHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.VerifyHandleRequest;
1074     PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1075     PCONSOLE Console;
1076 
1077     HANDLE IoHandle = VerifyHandleRequest->Handle;
1078     ULONG Index = HandleToULong(IoHandle) >> 2;
1079 
1080     VerifyHandleRequest->IsValid = FALSE;
1081 
1082     Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1083     if (!NT_SUCCESS(Status))
1084     {
1085         DPRINT1("Can't get console, status %lx\n", Status);
1086         return Status;
1087     }
1088 
1089     RtlEnterCriticalSection(&ProcessData->HandleTableLock);
1090 
1091     // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
1092     //         (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
1093 
1094     if (!IsConsoleHandle(IoHandle)            ||
1095         Index >= ProcessData->HandleTableSize ||
1096         ProcessData->HandleTable[Index].Object == NULL)
1097     {
1098         DPRINT("SrvVerifyConsoleIoHandle failed\n");
1099     }
1100     else
1101     {
1102         VerifyHandleRequest->IsValid = TRUE;
1103     }
1104 
1105     RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
1106 
1107     ConSrvReleaseConsole(Console, TRUE);
1108     return STATUS_SUCCESS;
1109 }
1110 
1111 /* EOF */
1112