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