xref: /reactos/win32ss/user/winsrv/consrv/handle.c (revision 5100859e)
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 CSR_API(SrvDuplicateHandle)
875 {
876     NTSTATUS Status;
877     PCONSOLE_DUPLICATEHANDLE DuplicateHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.DuplicateHandleRequest;
878     PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
879     PCONSOLE Console;
880 
881     HANDLE SourceHandle = DuplicateHandleRequest->SourceHandle;
882     ULONG Index = HandleToULong(SourceHandle) >> 2;
883     PCONSOLE_IO_HANDLE Entry;
884     DWORD DesiredAccess;
885 
886     DuplicateHandleRequest->TargetHandle = INVALID_HANDLE_VALUE;
887 
888     Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
889     if (!NT_SUCCESS(Status))
890     {
891         DPRINT1("Can't get console, status %lx\n", Status);
892         return Status;
893     }
894 
895     RtlEnterCriticalSection(&ProcessData->HandleTableLock);
896 
897     // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
898     //         (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
899 
900     if ( /** !IsConsoleHandle(SourceHandle)   || **/
901         Index >= ProcessData->HandleTableSize ||
902         (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
903     {
904         DPRINT1("Couldn't duplicate invalid handle 0x%p\n", SourceHandle);
905         Status = STATUS_INVALID_HANDLE;
906         goto Quit;
907     }
908 
909     if (DuplicateHandleRequest->Options & DUPLICATE_SAME_ACCESS)
910     {
911         DesiredAccess = Entry->Access;
912     }
913     else
914     {
915         DesiredAccess = DuplicateHandleRequest->DesiredAccess;
916         /* Make sure the source handle has all the desired flags */
917         if ((Entry->Access & DesiredAccess) == 0)
918         {
919             DPRINT1("Handle 0x%p only has access %X; requested %X\n",
920                     SourceHandle, Entry->Access, DesiredAccess);
921             Status = STATUS_INVALID_PARAMETER;
922             goto Quit;
923         }
924     }
925 
926     /* Insert the new handle inside the process handles table */
927     Status = ConSrvInsertObject(ProcessData,
928                                 &DuplicateHandleRequest->TargetHandle,
929                                 Entry->Object,
930                                 DesiredAccess,
931                                 DuplicateHandleRequest->InheritHandle,
932                                 Entry->ShareMode);
933     if (NT_SUCCESS(Status) &&
934         (DuplicateHandleRequest->Options & DUPLICATE_CLOSE_SOURCE))
935     {
936         /* Close the original handle if needed */
937         ConSrvCloseHandle(Entry);
938     }
939 
940 Quit:
941     RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
942 
943     ConSrvReleaseConsole(Console, TRUE);
944     return Status;
945 }
946 
947 CSR_API(SrvGetHandleInformation)
948 {
949     NTSTATUS Status;
950     PCONSOLE_GETHANDLEINFO GetHandleInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetHandleInfoRequest;
951     PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
952     PCONSOLE Console;
953 
954     HANDLE Handle = GetHandleInfoRequest->Handle;
955     ULONG Index = HandleToULong(Handle) >> 2;
956     PCONSOLE_IO_HANDLE Entry;
957 
958     Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
959     if (!NT_SUCCESS(Status))
960     {
961         DPRINT1("Can't get console, status %lx\n", Status);
962         return Status;
963     }
964 
965     RtlEnterCriticalSection(&ProcessData->HandleTableLock);
966 
967     ASSERT(ProcessData->HandleTable);
968     // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
969     //         (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
970 
971     if (!IsConsoleHandle(Handle)              ||
972         Index >= ProcessData->HandleTableSize ||
973         (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
974     {
975         Status = STATUS_INVALID_HANDLE;
976         goto Quit;
977     }
978 
979     /*
980      * Retrieve the handle information flags. The console server
981      * doesn't support HANDLE_FLAG_PROTECT_FROM_CLOSE.
982      */
983     GetHandleInfoRequest->Flags = 0;
984     if (Entry->Inheritable) GetHandleInfoRequest->Flags |= HANDLE_FLAG_INHERIT;
985 
986     Status = STATUS_SUCCESS;
987 
988 Quit:
989     RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
990 
991     ConSrvReleaseConsole(Console, TRUE);
992     return Status;
993 }
994 
995 CSR_API(SrvSetHandleInformation)
996 {
997     NTSTATUS Status;
998     PCONSOLE_SETHANDLEINFO SetHandleInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetHandleInfoRequest;
999     PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1000     PCONSOLE Console;
1001 
1002     HANDLE Handle = SetHandleInfoRequest->Handle;
1003     ULONG Index = HandleToULong(Handle) >> 2;
1004     PCONSOLE_IO_HANDLE Entry;
1005 
1006     Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1007     if (!NT_SUCCESS(Status))
1008     {
1009         DPRINT1("Can't get console, status %lx\n", Status);
1010         return Status;
1011     }
1012 
1013     RtlEnterCriticalSection(&ProcessData->HandleTableLock);
1014 
1015     ASSERT(ProcessData->HandleTable);
1016     // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
1017     //         (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
1018 
1019     if (!IsConsoleHandle(Handle)              ||
1020         Index >= ProcessData->HandleTableSize ||
1021         (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
1022     {
1023         Status = STATUS_INVALID_HANDLE;
1024         goto Quit;
1025     }
1026 
1027     /*
1028      * Modify the handle information flags. The console server
1029      * doesn't support HANDLE_FLAG_PROTECT_FROM_CLOSE.
1030      */
1031     if (SetHandleInfoRequest->Mask & HANDLE_FLAG_INHERIT)
1032     {
1033         Entry->Inheritable = ((SetHandleInfoRequest->Flags & HANDLE_FLAG_INHERIT) != 0);
1034     }
1035 
1036     Status = STATUS_SUCCESS;
1037 
1038 Quit:
1039     RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
1040 
1041     ConSrvReleaseConsole(Console, TRUE);
1042     return Status;
1043 }
1044 
1045 CSR_API(SrvCloseHandle)
1046 {
1047     NTSTATUS Status;
1048     PCONSOLE_CLOSEHANDLE CloseHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.CloseHandleRequest;
1049     PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1050     PCONSOLE Console;
1051 
1052     Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1053     if (!NT_SUCCESS(Status))
1054     {
1055         DPRINT1("Can't get console, status %lx\n", Status);
1056         return Status;
1057     }
1058 
1059     Status = ConSrvRemoveObject(ProcessData, CloseHandleRequest->Handle);
1060 
1061     ConSrvReleaseConsole(Console, TRUE);
1062     return Status;
1063 }
1064 
1065 CSR_API(SrvVerifyConsoleIoHandle)
1066 {
1067     NTSTATUS Status;
1068     PCONSOLE_VERIFYHANDLE VerifyHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.VerifyHandleRequest;
1069     PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1070     PCONSOLE Console;
1071 
1072     HANDLE IoHandle = VerifyHandleRequest->Handle;
1073     ULONG Index = HandleToULong(IoHandle) >> 2;
1074 
1075     VerifyHandleRequest->IsValid = FALSE;
1076 
1077     Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1078     if (!NT_SUCCESS(Status))
1079     {
1080         DPRINT1("Can't get console, status %lx\n", Status);
1081         return Status;
1082     }
1083 
1084     RtlEnterCriticalSection(&ProcessData->HandleTableLock);
1085 
1086     // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
1087     //         (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
1088 
1089     if (!IsConsoleHandle(IoHandle)            ||
1090         Index >= ProcessData->HandleTableSize ||
1091         ProcessData->HandleTable[Index].Object == NULL)
1092     {
1093         DPRINT("SrvVerifyConsoleIoHandle failed\n");
1094     }
1095     else
1096     {
1097         VerifyHandleRequest->IsValid = TRUE;
1098     }
1099 
1100     RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
1101 
1102     ConSrvReleaseConsole(Console, TRUE);
1103     return STATUS_SUCCESS;
1104 }
1105 
1106 /* EOF */
1107