[WIN32K]
[reactos.git] / reactos / win32ss / user / winsrv / consrv / handle.c
1 /*
2 * LICENSE: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: 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 NtClose(ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS]);
552 ConSrvFreeHandlesTable(ProcessData);
553 ConSrvDeleteConsole(Console);
554 ProcessData->ConsoleHandle = NULL;
555 return Status;
556 }
557
558 /* Duplicate the Input Event */
559 Status = NtDuplicateObject(NtCurrentProcess(),
560 Console->InputBuffer.ActiveEvent,
561 ProcessData->Process->ProcessHandle,
562 &ConsoleInitInfo->ConsoleStartInfo->InputWaitHandle,
563 EVENT_ALL_ACCESS, 0, 0);
564 if (!NT_SUCCESS(Status))
565 {
566 DPRINT1("NtDuplicateObject(InputWaitHandle) failed: %lu\n", Status);
567 NtClose(ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_FAILURE]);
568 NtClose(ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS]);
569 ConSrvFreeHandlesTable(ProcessData);
570 ConSrvDeleteConsole(Console);
571 ProcessData->ConsoleHandle = NULL;
572 return Status;
573 }
574
575 /* Mark the process as having a console */
576 ProcessData->ConsoleApp = TRUE;
577 ProcessData->Process->Flags |= CsrProcessIsConsoleApp;
578
579 /* Return the console handle to the caller */
580 ConsoleInitInfo->ConsoleStartInfo->ConsoleHandle = ProcessData->ConsoleHandle;
581
582 /* Insert the process into the processes list of the console */
583 InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink);
584
585 /* Add a reference count because the process is tied to the console */
586 _InterlockedIncrement(&Console->ReferenceCount);
587
588 /* Update the internal info of the terminal */
589 TermRefreshInternalInfo(Console);
590
591 return STATUS_SUCCESS;
592 }
593
594 NTSTATUS
595 ConSrvInheritConsole(PCONSOLE_PROCESS_DATA ProcessData,
596 HANDLE ConsoleHandle,
597 BOOLEAN CreateNewHandlesTable,
598 PHANDLE pInputHandle,
599 PHANDLE pOutputHandle,
600 PHANDLE pErrorHandle,
601 PCONSOLE_START_INFO ConsoleStartInfo)
602 {
603 NTSTATUS Status = STATUS_SUCCESS;
604 PCONSOLE Console;
605
606 /* Validate and lock the console */
607 if (!ConSrvValidateConsole(&Console,
608 ConsoleHandle,
609 CONSOLE_RUNNING, TRUE))
610 {
611 // FIXME: Find another status code
612 return STATUS_UNSUCCESSFUL;
613 }
614
615 /* Inherit the console */
616 ProcessData->ConsoleHandle = ConsoleHandle;
617
618 if (CreateNewHandlesTable)
619 {
620 /*
621 * We are about to create a new console. However when ConSrvNewProcess
622 * was called, we didn't know that we wanted to create a new console and
623 * therefore, we by default inherited the handles table from our parent
624 * process. It's only now that we notice that in fact we do not need
625 * them, because we've created a new console and thus we must use it.
626 *
627 * Therefore, free the handles table so that we can recreate
628 * a new one later on.
629 */
630 ConSrvFreeHandlesTable(ProcessData);
631
632 /* Initialize the handles table */
633 Status = ConSrvInitHandlesTable(ProcessData,
634 Console,
635 pInputHandle,
636 pOutputHandle,
637 pErrorHandle);
638 if (!NT_SUCCESS(Status))
639 {
640 DPRINT1("Failed to initialize the handles table\n");
641 ProcessData->ConsoleHandle = NULL;
642 goto Quit;
643 }
644 }
645
646 /* Duplicate the Initialization Events */
647 Status = NtDuplicateObject(NtCurrentProcess(),
648 Console->InitEvents[INIT_SUCCESS],
649 ProcessData->Process->ProcessHandle,
650 &ConsoleStartInfo->InitEvents[INIT_SUCCESS],
651 EVENT_ALL_ACCESS, 0, 0);
652 if (!NT_SUCCESS(Status))
653 {
654 DPRINT1("NtDuplicateObject(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status);
655 ConSrvFreeHandlesTable(ProcessData);
656 ProcessData->ConsoleHandle = NULL;
657 goto Quit;
658 }
659
660 Status = NtDuplicateObject(NtCurrentProcess(),
661 Console->InitEvents[INIT_FAILURE],
662 ProcessData->Process->ProcessHandle,
663 &ConsoleStartInfo->InitEvents[INIT_FAILURE],
664 EVENT_ALL_ACCESS, 0, 0);
665 if (!NT_SUCCESS(Status))
666 {
667 DPRINT1("NtDuplicateObject(InitEvents[INIT_FAILURE]) failed: %lu\n", Status);
668 NtClose(ConsoleStartInfo->InitEvents[INIT_SUCCESS]);
669 ConSrvFreeHandlesTable(ProcessData);
670 ProcessData->ConsoleHandle = NULL;
671 goto Quit;
672 }
673
674 /* Duplicate the Input Event */
675 Status = NtDuplicateObject(NtCurrentProcess(),
676 Console->InputBuffer.ActiveEvent,
677 ProcessData->Process->ProcessHandle,
678 &ConsoleStartInfo->InputWaitHandle,
679 EVENT_ALL_ACCESS, 0, 0);
680 if (!NT_SUCCESS(Status))
681 {
682 DPRINT1("NtDuplicateObject(InputWaitHandle) failed: %lu\n", Status);
683 NtClose(ConsoleStartInfo->InitEvents[INIT_FAILURE]);
684 NtClose(ConsoleStartInfo->InitEvents[INIT_SUCCESS]);
685 ConSrvFreeHandlesTable(ProcessData); // NOTE: Always free the handles table.
686 ProcessData->ConsoleHandle = NULL;
687 goto Quit;
688 }
689
690 /* Mark the process as having a console */
691 ProcessData->ConsoleApp = TRUE;
692 ProcessData->Process->Flags |= CsrProcessIsConsoleApp;
693
694 /* Return the console handle to the caller */
695 ConsoleStartInfo->ConsoleHandle = ProcessData->ConsoleHandle;
696
697 /* Insert the process into the processes list of the console */
698 InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink);
699
700 /* Add a reference count because the process is tied to the console */
701 _InterlockedIncrement(&Console->ReferenceCount);
702
703 /* Update the internal info of the terminal */
704 TermRefreshInternalInfo(Console);
705
706 Status = STATUS_SUCCESS;
707
708 Quit:
709 /* Unlock the console and return */
710 LeaveCriticalSection(&Console->Lock);
711 return Status;
712 }
713
714 NTSTATUS
715 ConSrvRemoveConsole(PCONSOLE_PROCESS_DATA ProcessData)
716 {
717 PCONSOLE Console;
718 PCONSOLE_PROCESS_DATA ConsoleLeaderProcess;
719
720 DPRINT("ConSrvRemoveConsole\n");
721
722 /* Mark the process as not having a console anymore */
723 ProcessData->ConsoleApp = FALSE;
724 ProcessData->Process->Flags &= ~CsrProcessIsConsoleApp;
725
726 /* Validate and lock the console */
727 if (!ConSrvValidateConsole(&Console,
728 ProcessData->ConsoleHandle,
729 CONSOLE_RUNNING, TRUE))
730 {
731 // FIXME: Find another status code
732 return STATUS_UNSUCCESSFUL;
733 }
734
735 DPRINT("ConSrvRemoveConsole - Locking OK\n");
736
737 /* Retrieve the console leader process */
738 ConsoleLeaderProcess = ConSrvGetConsoleLeaderProcess(Console);
739
740 /* Close all console handles and free the handles table */
741 ConSrvFreeHandlesTable(ProcessData);
742
743 /* Detach the process from the console */
744 ProcessData->ConsoleHandle = NULL;
745
746 /* Remove the process from the console's list of processes */
747 RemoveEntryList(&ProcessData->ConsoleLink);
748
749 /* Check whether the console should send a last close notification */
750 if (Console->NotifyLastClose)
751 {
752 /* If we are removing the process which wants the last close notification... */
753 if (ProcessData == Console->NotifiedLastCloseProcess)
754 {
755 /* ... just reset the flag and the pointer... */
756 Console->NotifyLastClose = FALSE;
757 Console->NotifiedLastCloseProcess = NULL;
758 }
759 /*
760 * ... otherwise, if we are removing the console leader process
761 * (that cannot be the process wanting the notification, because
762 * the previous case already dealt with it)...
763 */
764 else if (ProcessData == ConsoleLeaderProcess)
765 {
766 /*
767 * ... reset the flag first (so that we avoid multiple notifications)
768 * and then send the last close notification.
769 */
770 Console->NotifyLastClose = FALSE;
771 ConSrvConsoleCtrlEvent(CTRL_LAST_CLOSE_EVENT, Console->NotifiedLastCloseProcess);
772
773 /* Only now, reset the pointer */
774 Console->NotifiedLastCloseProcess = NULL;
775 }
776 }
777
778 /* Update the internal info of the terminal */
779 TermRefreshInternalInfo(Console);
780
781 /* Release the console */
782 DPRINT("ConSrvRemoveConsole - Decrement Console->ReferenceCount = %lu\n", Console->ReferenceCount);
783 ConSrvReleaseConsole(Console, TRUE);
784
785 return STATUS_SUCCESS;
786 }
787
788
789 /* PUBLIC SERVER APIS *********************************************************/
790
791 CSR_API(SrvOpenConsole)
792 {
793 /*
794 * This API opens a handle to either the input buffer or to
795 * a screen-buffer of the console of the current process.
796 */
797
798 NTSTATUS Status;
799 PCONSOLE_OPENCONSOLE OpenConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.OpenConsoleRequest;
800 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
801 PCONSOLE Console;
802
803 DWORD DesiredAccess = OpenConsoleRequest->DesiredAccess;
804 DWORD ShareMode = OpenConsoleRequest->ShareMode;
805 PCONSOLE_IO_OBJECT Object;
806
807 OpenConsoleRequest->Handle = INVALID_HANDLE_VALUE;
808
809 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
810 if (!NT_SUCCESS(Status))
811 {
812 DPRINT1("Can't get console\n");
813 return Status;
814 }
815
816 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
817
818 /*
819 * Open a handle to either the active screen buffer or the input buffer.
820 */
821 if (OpenConsoleRequest->HandleType == HANDLE_OUTPUT)
822 {
823 Object = &Console->ActiveBuffer->Header;
824 }
825 else // HANDLE_INPUT
826 {
827 Object = &Console->InputBuffer.Header;
828 }
829
830 if (((DesiredAccess & GENERIC_READ) && Object->ExclusiveRead != 0) ||
831 ((DesiredAccess & GENERIC_WRITE) && Object->ExclusiveWrite != 0) ||
832 (!(ShareMode & FILE_SHARE_READ) && Object->AccessRead != 0) ||
833 (!(ShareMode & FILE_SHARE_WRITE) && Object->AccessWrite != 0))
834 {
835 DPRINT1("Sharing violation\n");
836 Status = STATUS_SHARING_VIOLATION;
837 }
838 else
839 {
840 Status = ConSrvInsertObject(ProcessData,
841 &OpenConsoleRequest->Handle,
842 Object,
843 DesiredAccess,
844 OpenConsoleRequest->InheritHandle,
845 ShareMode);
846 }
847
848 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
849
850 ConSrvReleaseConsole(Console, TRUE);
851 return Status;
852 }
853
854 CSR_API(SrvDuplicateHandle)
855 {
856 NTSTATUS Status;
857 PCONSOLE_DUPLICATEHANDLE DuplicateHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.DuplicateHandleRequest;
858 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
859 PCONSOLE Console;
860
861 HANDLE SourceHandle = DuplicateHandleRequest->SourceHandle;
862 ULONG Index = HandleToULong(SourceHandle) >> 2;
863 PCONSOLE_IO_HANDLE Entry;
864 DWORD DesiredAccess;
865
866 DuplicateHandleRequest->TargetHandle = INVALID_HANDLE_VALUE;
867
868 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
869 if (!NT_SUCCESS(Status))
870 {
871 DPRINT1("Can't get console\n");
872 return Status;
873 }
874
875 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
876
877 // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
878 // (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
879
880 if ( /** !IsConsoleHandle(SourceHandle) || **/
881 Index >= ProcessData->HandleTableSize ||
882 (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
883 {
884 DPRINT1("Couldn't duplicate invalid handle 0x%p\n", SourceHandle);
885 Status = STATUS_INVALID_HANDLE;
886 goto Quit;
887 }
888
889 if (DuplicateHandleRequest->Options & DUPLICATE_SAME_ACCESS)
890 {
891 DesiredAccess = Entry->Access;
892 }
893 else
894 {
895 DesiredAccess = DuplicateHandleRequest->DesiredAccess;
896 /* Make sure the source handle has all the desired flags */
897 if ((Entry->Access & DesiredAccess) == 0)
898 {
899 DPRINT1("Handle 0x%p only has access %X; requested %X\n",
900 SourceHandle, Entry->Access, DesiredAccess);
901 Status = STATUS_INVALID_PARAMETER;
902 goto Quit;
903 }
904 }
905
906 /* Insert the new handle inside the process handles table */
907 Status = ConSrvInsertObject(ProcessData,
908 &DuplicateHandleRequest->TargetHandle,
909 Entry->Object,
910 DesiredAccess,
911 DuplicateHandleRequest->InheritHandle,
912 Entry->ShareMode);
913 if (NT_SUCCESS(Status) &&
914 (DuplicateHandleRequest->Options & DUPLICATE_CLOSE_SOURCE))
915 {
916 /* Close the original handle if needed */
917 ConSrvCloseHandle(Entry);
918 }
919
920 Quit:
921 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
922
923 ConSrvReleaseConsole(Console, TRUE);
924 return Status;
925 }
926
927 CSR_API(SrvGetHandleInformation)
928 {
929 NTSTATUS Status;
930 PCONSOLE_GETHANDLEINFO GetHandleInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetHandleInfoRequest;
931 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
932 PCONSOLE Console;
933
934 HANDLE Handle = GetHandleInfoRequest->Handle;
935 ULONG Index = HandleToULong(Handle) >> 2;
936 PCONSOLE_IO_HANDLE Entry;
937
938 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
939 if (!NT_SUCCESS(Status))
940 {
941 DPRINT1("Can't get console\n");
942 return Status;
943 }
944
945 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
946
947 ASSERT(ProcessData->HandleTable);
948 // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
949 // (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
950
951 if (!IsConsoleHandle(Handle) ||
952 Index >= ProcessData->HandleTableSize ||
953 (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
954 {
955 Status = STATUS_INVALID_HANDLE;
956 goto Quit;
957 }
958
959 /*
960 * Retrieve the handle information flags. The console server
961 * doesn't support HANDLE_FLAG_PROTECT_FROM_CLOSE.
962 */
963 GetHandleInfoRequest->Flags = 0;
964 if (Entry->Inheritable) GetHandleInfoRequest->Flags |= HANDLE_FLAG_INHERIT;
965
966 Status = STATUS_SUCCESS;
967
968 Quit:
969 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
970
971 ConSrvReleaseConsole(Console, TRUE);
972 return Status;
973 }
974
975 CSR_API(SrvSetHandleInformation)
976 {
977 NTSTATUS Status;
978 PCONSOLE_SETHANDLEINFO SetHandleInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetHandleInfoRequest;
979 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
980 PCONSOLE Console;
981
982 HANDLE Handle = SetHandleInfoRequest->Handle;
983 ULONG Index = HandleToULong(Handle) >> 2;
984 PCONSOLE_IO_HANDLE Entry;
985
986 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
987 if (!NT_SUCCESS(Status))
988 {
989 DPRINT1("Can't get console\n");
990 return Status;
991 }
992
993 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
994
995 ASSERT(ProcessData->HandleTable);
996 // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
997 // (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
998
999 if (!IsConsoleHandle(Handle) ||
1000 Index >= ProcessData->HandleTableSize ||
1001 (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
1002 {
1003 Status = STATUS_INVALID_HANDLE;
1004 goto Quit;
1005 }
1006
1007 /*
1008 * Modify the handle information flags. The console server
1009 * doesn't support HANDLE_FLAG_PROTECT_FROM_CLOSE.
1010 */
1011 if (SetHandleInfoRequest->Mask & HANDLE_FLAG_INHERIT)
1012 {
1013 Entry->Inheritable = ((SetHandleInfoRequest->Flags & HANDLE_FLAG_INHERIT) != 0);
1014 }
1015
1016 Status = STATUS_SUCCESS;
1017
1018 Quit:
1019 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
1020
1021 ConSrvReleaseConsole(Console, TRUE);
1022 return Status;
1023 }
1024
1025 CSR_API(SrvCloseHandle)
1026 {
1027 NTSTATUS Status;
1028 PCONSOLE_CLOSEHANDLE CloseHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.CloseHandleRequest;
1029 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1030 PCONSOLE Console;
1031
1032 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1033 if (!NT_SUCCESS(Status))
1034 {
1035 DPRINT1("Can't get console\n");
1036 return Status;
1037 }
1038
1039 Status = ConSrvRemoveObject(ProcessData, CloseHandleRequest->Handle);
1040
1041 ConSrvReleaseConsole(Console, TRUE);
1042 return Status;
1043 }
1044
1045 CSR_API(SrvVerifyConsoleIoHandle)
1046 {
1047 NTSTATUS Status;
1048 PCONSOLE_VERIFYHANDLE VerifyHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.VerifyHandleRequest;
1049 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1050 PCONSOLE Console;
1051
1052 HANDLE IoHandle = VerifyHandleRequest->Handle;
1053 ULONG Index = HandleToULong(IoHandle) >> 2;
1054
1055 VerifyHandleRequest->IsValid = FALSE;
1056
1057 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1058 if (!NT_SUCCESS(Status))
1059 {
1060 DPRINT1("Can't get console\n");
1061 return Status;
1062 }
1063
1064 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
1065
1066 // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
1067 // (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
1068
1069 if (!IsConsoleHandle(IoHandle) ||
1070 Index >= ProcessData->HandleTableSize ||
1071 ProcessData->HandleTable[Index].Object == NULL)
1072 {
1073 DPRINT("SrvVerifyConsoleIoHandle failed\n");
1074 }
1075 else
1076 {
1077 VerifyHandleRequest->IsValid = TRUE;
1078 }
1079
1080 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
1081
1082 ConSrvReleaseConsole(Console, TRUE);
1083 return STATUS_SUCCESS;
1084 }
1085
1086 /* EOF */