windows-nt/Source/XPSP1/NT/base/subsys/posix/psxss/process.c
2020-09-26 16:20:57 +08:00

1959 lines
50 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
process.c
Abstract:
Implementation of PSX Process Structure Backbone.
Author:
Mark Lucovsky (markl) 08-Mar-1989
Revision History:
--*/
#include "psxsrv.h"
#include "seposix.h"
#include "sesport.h"
NTSTATUS
PsxInitializeProcessStructure()
/*++
Routine Description:
This function initializes the PSX process structure. This includes
creating an initial process table, open file tables, CLIENT_ID to PID hash
table, and PID allocator.
Arguments:
None.
Return Value:
Status.
--*/
{
LONG i;
PPSX_PROCESS Process;
NTSTATUS Status;
//
// Initialize PsxProcessStructureLock
//
Status = RtlInitializeCriticalSection(&BlockLock);
ASSERT(NT_SUCCESS(Status));
InitializeListHead(&DefaultBlockList);
Status = RtlInitializeCriticalSection(&PsxProcessStructureLock);
ASSERT(NT_SUCCESS(Status));
//
// Initialize the ClientIdHashTable
//
for (i = 0; i < CIDHASHSIZE ; i++) {
InitializeListHead(&ClientIdHashTable[i]);
}
//
// Initialize Process Table
//
FirstProcess = PsxProcessTable;
LastProcess = &PsxProcessTable[32-1];
for (Process = FirstProcess; Process < LastProcess; Process++) {
Process->Flags |= P_FREE;
Process->SequenceNumber = 0;
}
return Status;
}
PPSX_PROCESS
PsxAllocateProcess (
IN PCLIENT_ID ClientId
)
/*++
Routine Description:
This function allocates a slot in the process table for the process
with the specified CLIENT_ID. Once a free process table slot is
allocated, the process is placed in the ClientIdHashTable. The process
slot sequence number is incremented, and a Pid is assigned to the
process. The process' process lock in initialized, and the process
table lock is released. A pointer to the new process is returned.
The process lock of the new process remains held so that the caller
can further initialize the process.
Arguments:
ClientId - Supplies the address of the client id associated with the
process. Only the UniqueProcess portion of the ClientId is used.
Return Value:
Null - No free process table slot could be located.
Non-Null - A pointer to the allocated process. The process' process lock is
held, the process can be located in the ClientIdHashTable, and
the process has a Pid.
--*/
{
PPSX_PROCESS Process;
ULONG index;
NTSTATUS Status;
//
// Lock Process Table
//
AcquireProcessStructureLock();
for (Process = FirstProcess, index = 0; Process < LastProcess;
Process++, index++) {
if (!(Process->Flags & P_FREE)) {
continue;
}
//
// Slot Found. Mark process as allocated and free process table
// lock.
//
Process->Flags &= ~P_FREE;
Process->ClientId = *ClientId;
Process->ClientPort = NULL;
//
// Place Process In CLIENT_ID Hash Table
//
InsertTailList(&ClientIdHashTable[CIDTOHASHINDEX(ClientId)],
&Process->ClientIdHashLinks);
if (++Process->SequenceNumber > 255)
Process->SequenceNumber = 1;
MAKEPID(Process->Pid, Process->SequenceNumber, index);
if (Process->Pid == SPECIALPID) {
KdPrint(("PSXSS: seq %d, index %d\n", Process->SequenceNumber, index));
}
ASSERT(Process->Pid != SPECIALPID);
Status = RtlInitializeCriticalSection(&Process->ProcessLock);
if (!NT_SUCCESS(Status)) {
ReleaseProcessStructureLock();
return NULL;
}
AcquireProcessLock(Process);
//
// Unlock Process Table
//
ReleaseProcessStructureLock();
IF_PSX_DEBUG(EXEC) {
KdPrint(("PsxAllocateProcess: Process = %lx, ClientId %lx.%lx "
"Pid %lx\n", Process, Process->ClientId.UniqueProcess,
Process->ClientId.UniqueThread, Process->Pid));
}
return Process;
}
//
// Unlock Process Table
//
ReleaseProcessStructureLock();
return (PPSX_PROCESS)NULL;
}
PPSX_PROCESS
PsxLocateProcessByClientId (
IN PCLIENT_ID ClientId
)
/*++
Routine Description:
This function attempts to locate the process with the specified CLIENT_ID.
It depends on the way PsxAllocateProcess allocates a process table
slot, assigns it a CLIENT_ID, and places it in the ClientIdHashTable while
holding the PsxProcessStructureLock. Only the UniqueProcess portion of the
specified CLIENT_ID is used.
Arguments:
ClientId - Supplies the client id associated with the process to be located.
Return Value:
Null - No process whose client id matches the specified ClientId parameter
is located in the ClientIdHashTable.
Non-Null - Returns the address of the process whose CLIENT_ID matches the
specified ClientId.
--*/
{
PPSX_PROCESS Process;
PLIST_ENTRY head, next;
head = &ClientIdHashTable[CIDTOHASHINDEX(ClientId)];
//
// Lock Process Table
//
AcquireProcessStructureLock();
next = head->Flink;
while (next != head) {
Process = CONTAINING_RECORD(next, PSX_PROCESS, ClientIdHashLinks);
if (Process->ClientId.UniqueProcess == ClientId->UniqueProcess &&
Process->ClientId.UniqueThread == ClientId->UniqueThread) {
//
// Unlock Process Table
//
ReleaseProcessStructureLock();
return Process;
}
next = next->Flink;
}
//
// Unlock Process Table
//
ReleaseProcessStructureLock();
return (PPSX_PROCESS)NULL;
}
PPSX_PROCESS
PsxLocateProcessBySession (
IN PPSX_SESSION Session
)
{
PPSX_PROCESS Process;
//
// Lock Process Table
//
AcquireProcessStructureLock();
for (Process = FirstProcess; Process < LastProcess; Process++) {
if (!(Process->Flags & P_FREE)) {
continue;
}
if (Process->PsxSession == Session) {
//
// Unlock Process Table
//
ReleaseProcessStructureLock();
return Process;
}
}
//
// Unlock Process Table
//
ReleaseProcessStructureLock();
return (PPSX_PROCESS)NULL;
}
BOOLEAN
IsGroupOrphaned(
IN pid_t ProcessGroup
)
/*++
Routine Description:
This function is called to determine if the specified ProcessGroup
is orphaned.
An orphaned process group is a group where the parent of every member
is itself a member, or is not a member of the group's session. This
definition is a little flawed since it assumes that processes without
a parent are inherited by a real process in a different session.
Arguments:
ProcessGroup - Supplies the process group to check
Return Value:
TRUE - The process group is orphaned.
FALSE - The process group is not orphaned.
--*/
{
PPSX_PROCESS Parent, FirstMember, NextMember;
PLIST_ENTRY Next;
BOOLEAN Orphaned,MemberFound;
LockNtSessionList();
//
// First locate a member of the group by scanning the process
// table. Once a group member is found, then whip through the
// group list looking to see if any member's parent is not in
// the session of the group, or whose parent is SPECIALPID
//
MemberFound = FALSE;
for (FirstMember = FirstProcess; FirstMember < LastProcess; FirstMember++) {
if (FirstMember->Flags & P_FREE) {
continue;
}
if (FirstMember->ProcessGroupId == ProcessGroup) {
MemberFound = TRUE;
break;
}
}
ASSERT(MemberFound);
Orphaned = FALSE;
//
// Scan the group. For each member, look at the members parent.
//
// If the parent is SPECIALPID, then the group is orphaned,
// else if the parent is not in the same group and is in a
// different session, then the group is oprphaned
//
NextMember = FirstMember;
do {
//
// The parent of a member is not in the same session so the
// group is orphaned.
//
if (NextMember->ParentPid == SPECIALPID) {
Orphaned = TRUE;
break;
} else {
//
// The member has a parent, so see if the parent is in
// a different group and in a different session. If this
// is the case, then the group is orphaned.
//
Parent = PIDTOPROCESS(NextMember->ParentPid);
if (Parent->ProcessGroupId != ProcessGroup &&
Parent->PsxSession != FirstMember->PsxSession) {
Orphaned = TRUE;
break;
}
}
Next = NextMember->GroupLinks.Flink;
NextMember = CONTAINING_RECORD(Next, PSX_PROCESS, GroupLinks);
} while (NextMember != FirstMember);
UnlockNtSessionList();
return Orphaned;
}
BOOLEAN
PsxStopProcess(
IN PPSX_PROCESS p,
IN PPSX_API_MSG m,
IN ULONG Signal,
IN sigset_t *RestoreBlockSigset OPTIONAL
)
/*++
Routine Description:
This function is called by PendingSignalHandledInside in response
to a pending stop signal. It simply blocks the process awaiting a
SIGCONT signal.
Arguments:
p - Supplies the address of the process to stop
m - Supplies the reply message to be generated when the process is
continued
Signal - Supplies the signal number used to stop the process.
If the stop signal is anything other than SIGSTOP, a check is
made to see if the processes group is orphaned before stopping
the process.
RestoreBlockSigset - Contains an optional blocked signal mask to
be restured during the reply.
Locks - Process lock always released before return.
Return Value:
TRUE - The process was stopped by this call.
FALSE - The process was not stopped.
--*/
{
sigset_t RestoreBlockMask;
PPSX_PROCESS Parent;
NTSTATUS Status;
if (ARGUMENT_PRESENT(RestoreBlockSigset)) {
RestoreBlockMask = *RestoreBlockSigset;
} else {
RestoreBlockMask = (sigset_t)0xffffffff;
}
ReleaseProcessLock(p);
AcquireProcessStructureLock();
if (Signal != SIGSTOP) {
if (IsGroupOrphaned(p->ProcessGroupId)) {
ReleaseProcessStructureLock();
return FALSE;
}
}
p->State = Stopped;
p->ExitStatus = (Signal << 8) | 0177;
p->Flags &= ~P_WAITED; // proc may satisfy wait
//
// Possibly generate a SIGCHLD to the parent, and satisfy
// a parent wait
//
if (p->ParentPid != SPECIALPID) {
Parent = PIDTOPROCESS(p->ParentPid);
AcquireProcessLock(Parent);
//
// if the SA_NOCLDSTOP flag is not sent, then SIGCHLD the parent
//
if (!Parent->SignalDataBase.SignalDisposition[SIGCHLD-1].sa_flags
& SA_NOCLDSTOP) {
PsxSignalProcess(Parent,SIGCHLD);
}
ReleaseProcessLock(Parent);
if (Parent->State == Waiting) {
Status = BlockProcess(p, (PVOID)RestoreBlockMask,
PsxStopProcessHandler, m, FALSE, &PsxProcessStructureLock);
if (!NT_SUCCESS(Status)) {
m->Error = PsxStatusToErrno(Status);
return FALSE;
}
UnblockProcess(Parent, WaitSatisfyInterrupt, FALSE, 0);
return TRUE;
}
}
Status = BlockProcess(p, (PVOID)RestoreBlockMask, PsxStopProcessHandler,
m, FALSE, &PsxProcessStructureLock);
if (!NT_SUCCESS(Status)) {
m->Error = PsxStatusToErrno(Status);
return FALSE;
}
return TRUE;
}
VOID
PsxStopProcessHandler(
IN PPSX_PROCESS p,
IN PINTCB IntControlBlock,
IN PSX_INTERRUPTREASON InterruptReason,
IN int Signal
)
/*++
Routine Description:
This procedure is called when a stopped process is continued.
Arguments:
p - Supplies the address of the process being continued.
IntControlBlock - Supplies the address of the interrupt control block.
InterruptReason - Supplies the reason that this process is being
interrupted. Not used in this handler.
Return Value:
None.
--*/
{
PPSX_API_MSG m;
sigset_t RestoreBlockSigset;
UNREFERENCED_PARAMETER(InterruptReason);
RtlLeaveCriticalSection(&BlockLock);
RestoreBlockSigset = (sigset_t)((ULONG_PTR)IntControlBlock->IntContext);
m = IntControlBlock->IntMessage;
RtlFreeHeap(PsxHeap, 0, (PVOID)IntControlBlock);
m->Signal = Signal;
if ((ULONG)RestoreBlockSigset == 0xffffffff) {
ApiReply(p, m ,NULL);
} else {
ApiReply(p, m, &RestoreBlockSigset);
}
RtlFreeHeap(PsxHeap, 0, (PVOID)m);
}
VOID
PsxSignalProcess(
IN PPSX_PROCESS p,
IN ULONG Signal
)
/*++
Routine Description:
This function causes the specified signal to be sent to the specified
process. Thsi works by simply marking the signal as pending, and then
making the process looking at ats pending signals.
This function must be called with the PsxProcessTable locked
Getting the process to look at its pending signals has three basic cases:
1) The process is not in PSX.
Cause the process to call __NullPosixAPI
2) The process is inside PSX and is not interruptible
Simply set signal to pending
3) The process is inside PSX and is interruptible
Dispatch to the process' interrupt control block
Arguments:
p - Supplies the address of the process to signal.
Signal - Supplies the Signal value.
Return Value:
None.
--*/
{
NTSTATUS Status;
sigset_t Pending;
ASSERT(NULL != p);
AcquireProcessLock(p);
//
// This can happen due to exit recursion
//
if (p->State == Exited) {
ReleaseProcessLock(p);
return;
}
//
// if signal is SIGCONT, then clear any pending stop signals
// if signal is a stop signal, then clear any pending SIGCONT
//
switch (Signal) {
case SIGCONT:
p->SignalDataBase.PendingSignalMask &= ~_SIGSTOPSIGNALS;
if (p->State == Stopped) {
SIGADDSET(&p->SignalDataBase.PendingSignalMask,SIGCONT);
p->State = Active;
ReleaseProcessLock(p);
UnblockProcess(p, SignalInterrupt, FALSE, Signal);
return;
}
break;
case SIGSTOP:
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
//
// XXX.mjb: FIX to deal w/ orphaned group
//
p->SignalDataBase.PendingSignalMask &= ~(1L << (SIGCONT - 1));
break;
default:
break;
}
//
// If signal is being ignored, don't add it to the pending set.
//
if (p->SignalDataBase.SignalDisposition[Signal-1].sa_handler
== (_handler)SIG_IGN) {
ReleaseProcessLock(p);
return;
}
//
// Add signal to pending set. If any pending but not blocked signals
// exist for the process, then maybe make the process take a look at
// its pending signals.
//
SIGADDSET(&p->SignalDataBase.PendingSignalMask, Signal);
Pending = p->SignalDataBase.PendingSignalMask &
~p->SignalDataBase.BlockedSignalMask;
//
// If the signal is defaulted and the default action is to ignore,
// add it to the pending set but don't interrupt the system call.
// This allows a process to block sigchld and then handle it later
// by changing the action and then unblocking.
//
if (Signal == SIGCHLD &&
p->SignalDataBase.SignalDisposition[Signal-1].sa_handler
== (_handler)SIG_DFL) {
ReleaseProcessLock(p);
return;
}
if (!Pending) {
// Fast path: no pending unblocked signals.
ReleaseProcessLock(p);
return;
}
if (p->InPsx > 0) {
if (Stopped == p->State && SIGKILL != Signal) {
//
// While a process is stopped, any additional signals that
// are sent to the process shall not be delivered until the
// process is continued except SIGKILL, which always terminates
// the receiving process. 1003.1-1990 3.3.1.3 (1b)
//
ReleaseProcessLock(p);
return;
}
// XXX.mjb: I'm tense about this code. The process being
// killed is InPsx, and we're unblocking him. But what if
// a processes is killing himself, or there is more than one
// api request thread and the other process is executing a
// system call, but is not blocked?
ReleaseProcessLock(p);
UnblockProcess(p, SignalInterrupt, FALSE, Signal);
return;
}
if (p->State == Unconnected) {
// leave the signal pending
ReleaseProcessLock(p);
return;
}
ReleaseProcessLock(p);
//
// Drag Process inside so that it looks at its signals. We set
// the NO_FORK bit to ensure that the process calls the NullApi
// before he calls fork. We want to avoid a situation where the
// process stack is modified by RtlRemoteCall while the process
// is blocked in lpc in fork(), and we copy his stack to a child
// process.
//
p->Flags |= P_NO_FORK;
Status = RtlRemoteCall(p->Process, p->Thread, (PVOID)p->NullApiCaller,
0, NULL, TRUE, FALSE);
if (!NT_SUCCESS(Status)) {
KdPrint(("Dragging proc 0x%x inside for sig %d\n", p->Pid, Signal));
KdPrint(("PSXSS: RtlRemoteCall: 0x%x\n", Status));
}
}
static NTSTATUS
InitProcNoParent(
PPSX_PROCESS NewProcess,
IN PPSX_SESSION Session OPTIONAL,
IN ULONG SessionId OPTIONAL
);
static void
InitProcFromParent(
PPSX_PROCESS NewProcess,
PPSX_PROCESS ForkProcess
);
NTSTATUS
PsxInitializeProcess(
IN PPSX_PROCESS NewProcess,
IN PPSX_PROCESS ForkProcess OPTIONAL,
IN ULONG SessionId OPTIONAL,
IN HANDLE ProcessHandle,
IN HANDLE ThreadHandle,
IN PPSX_SESSION Session OPTIONAL
)
{
NTSTATUS Status;
NewProcess->IntControlBlock = (PINTCB)NULL;
NewProcess->State = Unconnected;
NewProcess->Flags = 0;
NewProcess->Process = ProcessHandle;
NewProcess->Thread = ThreadHandle;
NewProcess->AlarmTimer = NULL;
NewProcess->ProcessIsBeingDebugged = FALSE;
NewProcess->BlockingThread = (HANDLE)NULL;
RtlZeroMemory((PVOID)&NewProcess->ProcessTimes, sizeof(struct tms));
if (ARGUMENT_PRESENT(ForkProcess)) {
InitProcFromParent(NewProcess, ForkProcess);
Status = STATUS_SUCCESS;
} else {
Status = InitProcNoParent(NewProcess, Session, SessionId);
}
ReleaseProcessLock(NewProcess);
return Status;
}
static void
InitProcFromParent(
PPSX_PROCESS NewProcess,
PPSX_PROCESS ForkProcess
)
{
NTSTATUS Status;
//
// Propagate Process Attributes
//
//
// InPsx -- this new process will return directly to user-land,
// without the posix subsystem replying to his message. Therefore,
// we set InPsx to 0 here.
//
NewProcess->InPsx = 0;
NewProcess->ParentPid = ForkProcess->Pid;
NewProcess->ProcessGroupId = ForkProcess->ProcessGroupId;
InsertHeadList(&ForkProcess->GroupLinks,
&NewProcess->GroupLinks);
REFERENCE_PSX_SESSION(ForkProcess->PsxSession);
NewProcess->PsxSession = ForkProcess->PsxSession;
NewProcess->EffectiveUid = ForkProcess->EffectiveUid;
NewProcess->RealUid = ForkProcess->RealUid;
NewProcess->EffectiveGid = ForkProcess->EffectiveGid;
NewProcess->RealGid = ForkProcess->RealGid;
NewProcess->DirectoryPrefix = ForkProcess->DirectoryPrefix;
NewProcess->FileModeCreationMask = ForkProcess->FileModeCreationMask;
if (ForkProcess->ProcessIsBeingDebugged && PsxpDebuggerActive) {
Status = NtSetInformationProcess(NewProcess->Process,
ProcessDebugPort, (PVOID)&PsxpDebugPort,
sizeof(HANDLE));
if (NT_SUCCESS(Status)) {
NewProcess->ProcessIsBeingDebugged = TRUE;
NewProcess->DebugUiClientId = ForkProcess->DebugUiClientId;
}
}
//
// Fork the signal database
//
NewProcess->SignalDataBase = ForkProcess->SignalDataBase;
SIGEMPTYSET(&NewProcess->SignalDataBase.PendingSignalMask);
//
// Fork The Open File Table
//
ForkProcessFileTable(ForkProcess, NewProcess);
ReleaseProcessLock(ForkProcess);
}
static NTSTATUS
InitProcNoParent(
PPSX_PROCESS NewProcess,
IN PPSX_SESSION Session,
IN ULONG SessionId OPTIONAL
)
{
HANDLE TokenHandle = NULL;
TOKEN_PRIMARY_GROUP *pGroup = NULL;
TOKEN_GROUPS *pGroups = NULL;
PTOKEN_USER pTokenUser = NULL;
ULONG LengthNeeded;
PULONG pu;
PSID_AND_ATTRIBUTES pSA;
NTSTATUS Status;
PFILEDESCRIPTOR Fd;
ULONG i;
PSID SecondGroupChoice; // to be used if primary group is
// no good.
BOOLEAN PrimaryGroupOk; // is primary group good?
NewProcess->ParentPid = SPECIALPID;
NewProcess->ProcessGroupId = NewProcess->Pid; // process group
InitializeListHead(&NewProcess->GroupLinks);
//
// This process doesn't get to user-land by returning from an lpc
// api, so we take the opportunity to set InPsx to zero here.
//
NewProcess->InPsx = 0;
Session->SessionLeader = NewProcess->Pid;
NewProcess->PsxSession = Session;
//XXX.mjb: is this really right? Or &= ~P_HAS_EXECED?
NewProcess->Flags |= P_HAS_EXECED;
NewProcess->DirectoryPrefix = NULL;
//
// Signal DataBase
//
SIGEMPTYSET(&NewProcess->SignalDataBase.BlockedSignalMask);
SIGEMPTYSET(&NewProcess->SignalDataBase.PendingSignalMask);
SIGEMPTYSET(&NewProcess->SignalDataBase.SigSuspendMask);
for (i = 0; i < _SIGMAXSIGNO; i++) {
NewProcess->SignalDataBase.SignalDisposition[i].sa_handler =
(_handler)SIG_DFL;
NewProcess->SignalDataBase.SignalDisposition[i].sa_flags = 0;
SIGEMPTYSET(&NewProcess->SignalDataBase.SignalDisposition[i].sa_mask);
}
Fd = NewProcess->ProcessFileTable;
RtlEnterCriticalSection(&SystemOpenFileLock);
{
for (i = 0; i < OPEN_MAX; i++, Fd++) {
Fd->SystemOpenFileDesc = (PSYSTEMOPENFILE)NULL;
Fd->Flags = 0;
}
} RtlLeaveCriticalSection(&SystemOpenFileLock);
//
// Examine the new process's token to figure out what the
// uid should be.
//
Status = NtOpenProcessToken(NewProcess->Process, GENERIC_READ,
&TokenHandle);
ASSERT(NT_SUCCESS(Status));
Status = NtQueryInformationToken(TokenHandle, TokenUser,
NULL, 0, &LengthNeeded);
ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
pTokenUser = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
if (NULL == pTokenUser) {
Status = STATUS_NO_MEMORY;
goto out;
}
Status = NtQueryInformationToken(TokenHandle, TokenUser,
(PVOID)pTokenUser, LengthNeeded, &LengthNeeded);
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: NtQueryInfoToken: 0x%x\n", Status));
}
ASSERT(NT_SUCCESS(Status));
NewProcess->EffectiveUid = NewProcess->RealUid =
MakePosixId(pTokenUser->User.Sid);
//
// Figure out the primary group. Security allows the user to
// set his primary group to whatever he wants, so we make sure
// that his choice is reasonable. If his PrimaryGroup is in
// the group array, he's fine. If it's not, we make his
// PrimaryGroup the first group from the group array other
// than World. If there are none other than World, that's
// what he gets.
//
Status = NtQueryInformationToken(TokenHandle, TokenPrimaryGroup,
NULL, 0, &LengthNeeded);
ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
pGroup = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
if (NULL == pGroup) {
NewProcess->EffectiveGid = NewProcess->RealGid = 0;
Status = STATUS_NO_MEMORY;
goto out;
}
Status = NtQueryInformationToken(TokenHandle, TokenPrimaryGroup,
(PVOID)pGroup, LengthNeeded, &LengthNeeded);
ASSERT(NT_SUCCESS(Status));
Status = NtQueryInformationToken(TokenHandle, TokenGroups, NULL,
0, &LengthNeeded);
ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
pGroups = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
if (NULL == pGroups) {
NewProcess->EffectiveGid = NewProcess->RealGid = 0;
Status = STATUS_NO_MEMORY;
goto out;
}
Status = NtQueryInformationToken(TokenHandle, TokenGroups,
(PVOID)pGroups,
LengthNeeded, &LengthNeeded);
ASSERT(NT_SUCCESS(Status));
SecondGroupChoice = NULL;
PrimaryGroupOk = FALSE;
for (i = 0; i < pGroups->GroupCount; ++i) {
if (RtlEqualSid(pGroups->Groups[i].Sid, pGroup->PrimaryGroup)) {
PrimaryGroupOk = TRUE;
}
//
// Our second group choice is the first group in the list
// that is not the World group.
//
if (NULL == SecondGroupChoice &&
SE_WORLD_POSIX_ID != MakePosixId(pGroups->Groups[i].Sid)) {
SecondGroupChoice = pGroups->Groups[i].Sid;
}
}
if (PrimaryGroupOk) {
NewProcess->EffectiveGid = NewProcess->RealGid =
MakePosixId(pGroup->PrimaryGroup);
} else if (NULL != SecondGroupChoice) {
NewProcess->EffectiveGid = NewProcess->RealGid =
MakePosixId(SecondGroupChoice);
} else {
NewProcess->EffectiveGid = NewProcess->RealGid =
SE_WORLD_POSIX_ID;
}
out:
if (pGroup) RtlFreeHeap(PsxHeap, 0, (PVOID)pGroup);
if (pGroups) RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
if (pTokenUser) RtlFreeHeap(PsxHeap, 0, (PVOID)pTokenUser);
if (TokenHandle) NtClose(TokenHandle);
return Status;
}
//
// PsxCreateProcess -- this routine is called only by PsxCreateConSession
// to create the first process for a brand-new session.
//
BOOLEAN
PsxCreateProcess(
PPSX_EXEC_INFO ExecInfo,
OUT PPSX_PROCESS *NewProcess,
IN HANDLE ParentProcess,
IN PPSX_SESSION Session
)
{
NTSTATUS Status;
UNICODE_STRING ImageFileName;
UNICODE_STRING Path;
UNICODE_STRING LibPath;
UNICODE_STRING CWD;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
RTL_USER_PROCESS_INFORMATION ProcessInformation;
PPSX_PROCESS Process;
Path.Buffer = NULL;
LibPath.Buffer = NULL;
CWD.Buffer = NULL;
Status = RtlAnsiStringToUnicodeString(&Path, &ExecInfo->Path, TRUE);
if (NT_SUCCESS(Status)) {
Status = RtlAnsiStringToUnicodeString(&LibPath, &ExecInfo->LibPath, TRUE);
if (NT_SUCCESS(Status)) {
Status = RtlAnsiStringToUnicodeString(&CWD, &ExecInfo->CWD, TRUE);
}
}
if (NT_SUCCESS(Status)) {
//
// Beware - ExecInfo->ArgV is used to pass uninterpreted binary
// data. It is NOT an ANSI string. So don't try to convert
// to Unicode. Just lie to RtlCreateProcessParameters.
//
Status = RtlCreateProcessParameters(&ProcessParameters, &Path,
&LibPath, &CWD, (PUNICODE_STRING)&ExecInfo->Argv, NULL,
NULL, NULL, NULL, NULL);
}
RtlFreeUnicodeString( &Path );
RtlFreeUnicodeString( &LibPath );
RtlFreeUnicodeString( &CWD );
if (!NT_SUCCESS(Status)) {
return FALSE; // ENOMEM
}
ProcessParameters->CurrentDirectory.Handle = NULL;
ImageFileName = ProcessParameters->ImagePathName;
ImageFileName.Buffer = (PWSTR)
((PCHAR)ImageFileName.Buffer + (ULONG_PTR)ProcessParameters);
IF_PSX_DEBUG(EXEC) {
KdPrint(("PSXSS: Creating process %wZ\n", &ImageFileName));
}
Status = RtlCreateUserProcess(&ImageFileName, OBJ_CASE_INSENSITIVE,
ProcessParameters, NULL,
NULL, ParentProcess, TRUE, NULL, NULL, &ProcessInformation);
RtlDestroyProcessParameters(ProcessParameters);
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: RtlCreateUserProcess: 0x%x\n", Status));
return FALSE; // ENOEXEC
}
if (ProcessInformation.ImageInformation.SubSystemType !=
IMAGE_SUBSYSTEM_POSIX_CUI) {
NtClose(ProcessInformation.Process);
return FALSE; // ENOEXEC
}
Status = NtSetInformationProcess(ProcessInformation.Process,
ProcessExceptionPort, (PVOID)&PsxApiPort, sizeof(PsxApiPort));
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: NtSetInfoProc: 0x%x\n", Status));
}
ASSERT(NT_SUCCESS(Status));
{
ULONG HardErrorMode = 0; // disable popups
Status = NtSetInformationProcess(
ProcessInformation.Process,
ProcessDefaultHardErrorMode,
(PVOID)&HardErrorMode,
sizeof(HardErrorMode)
);
ASSERT(NT_SUCCESS(Status));
}
Process = PsxAllocateProcess(&ProcessInformation.ClientId);
if (!Process) {
Status = NtTerminateProcess(ProcessInformation.Process,
STATUS_SUCCESS);
ASSERT(NT_SUCCESS(Status));
NtClose(ProcessInformation.Process);
NtClose(ProcessInformation.Thread);
return FALSE; // EAGAIN
}
if (! PsxInitializeDirectories(Process, &ExecInfo->CWD)) {
Status = NtTerminateProcess(ProcessInformation.Process,
STATUS_SUCCESS);
ASSERT(NT_SUCCESS(Status));
NtClose(ProcessInformation.Process);
NtClose(ProcessInformation.Thread);
return FALSE;
}
Status = PsxInitializeProcess(Process, NULL, 0L,
ProcessInformation.Process, ProcessInformation.Thread, Session);
if (!NT_SUCCESS(Status)) {
RtlFreeHeap(PsxHeap, 0, Process->DirectoryPrefix->PsxRoot.Buffer);
RtlFreeHeap(PsxHeap, 0,
Process->DirectoryPrefix->NtCurrentWorkingDirectory.Buffer);
RtlFreeHeap(PsxHeap, 0, Process->DirectoryPrefix);
Process->DirectoryPrefix = NULL;
Status = NtTerminateProcess(ProcessInformation.Process,
STATUS_SUCCESS);
ASSERT(NT_SUCCESS(Status));
NtClose(ProcessInformation.Process);
NtClose(ProcessInformation.Thread);
return FALSE;
}
Process->InitialPebPsxData.Length = sizeof(Process->InitialPebPsxData);
Process->InitialPebPsxData.ClientStartAddress = NULL;
//
// Set the session port. The client constructs the port name from the
// unique id, opens the port, and sets the actual handle.
//
Process->InitialPebPsxData.SessionPortHandle =
(HANDLE)Process->PsxSession->Terminal->UniqueId;
*NewProcess = Process;
return TRUE;
}
PPSX_SESSION
PsxAllocateSession(
IN PPSX_CONTROLLING_TTY Terminal OPTIONAL,
IN pid_t SessionLeader
)
/*++
Routine Description:
This function is called to allocate and initialize a posix
session.
Each session may be associated with a controlling terminal. If
the Terminal parameter is specified, and if the terminal is
associated with a session, then no new session is created, and the
process simply joins the session as a new process group. This
case can only occur in a double handoff... (a foreign app is execed,
and then it execs a POSIX app with a terminal stdin that is the same
as the one it left with.
Arguments:
Terminal - An optional parameter, that if supplied specifies the
controlling terminal to be associated with the session.
SessionLeader - Supplies the process id of the session leader for
this session.
Return Value:
Returns a pointer to the new session.
--*/
{
PPSX_SESSION Session;
if (ARGUMENT_PRESENT(Terminal)) {
//
// Look inside terminal to see if it is associated with a session.
// If not, then create a session attached to the terminal
// (logon to posix). If it is associated with a session, then become
// a new group in the session and dis-regard SessionLeader parameter
//
;
}
//
// Allocate storage for the session and initialize the session
//
Session = RtlAllocateHeap(PsxHeap, 0,sizeof(PSX_SESSION));
if (NULL == Session) {
return NULL;
}
Session->ReferenceCount = 1;
Session->SessionLeader = SessionLeader;
Session->Terminal = Terminal;
if (ARGUMENT_PRESENT(Terminal)) {
Terminal->ForegroundProcessGroup = SessionLeader;
Terminal->Session = Session;
}
return Session;
}
VOID
PsxDeallocateSession(
IN PPSX_SESSION Session
)
/*++
Routine Description:
This function deallocates a posix session and frees the terminal
so that it can become associated with new sessions.
Arguments:
Session - Supplies the address of the posix session being deallocated
Return Value:
None.
--*/
{
NTSTATUS Status;
if (Session->Terminal) {
Session->Terminal->ForegroundProcessGroup = SPECIALPID;
Session->Terminal->Session = NULL;
RtlDeleteCriticalSection(&Session->Terminal->Lock);
if (NULL != Session->Terminal->IoBuffer) {
Status = NtUnmapViewOfSection(NtCurrentProcess(),
Session->Terminal->IoBuffer);
ASSERT(NT_SUCCESS(Status));
}
RtlFreeHeap(PsxHeap, 0, (PVOID)Session->Terminal);
}
UnlockNtSessionList();
RtlFreeHeap(PsxHeap, 0, (PVOID)Session);
}
VOID
PsxTerminateProcessBySignal(
IN PPSX_PROCESS p,
IN PPSX_API_MSG m,
IN ULONG Signal
)
{
UNREFERENCED_PARAMETER(m);
Exit(p, Signal);
}
VOID
Exit(
IN PPSX_PROCESS p,
IN ULONG ExitStatus
)
/*++
Routine Description:
This function process termination. It is called either
from PsxExit as a result of an applications call to _exit(),
or from within PSX to terminate a process.
Arguments:
p - Supplies the address of the exiting process
ExitStatus - Supplies the exit status for the exiting process
Return Value:
None.
--*/
{
PPSX_PROCESS cp, WaitingParent,Parent;
PPSX_PROCESS FirstMember, NextMember;
PLIST_ENTRY Next;
NTSTATUS Status;
HANDLE AlarmTimer;
BOOLEAN OrphanedAGroupRelatedChild, AlreadyOrphaned, PreviousTimerState;
KERNEL_USER_TIMES ProcessTime;
BOOLEAN WaitForProcess;
if ((ULONG)-1 == ExitStatus) {
//
// This process is dying because of some extraordinary event,
// and we should make sure that we clean up no matter what.
// XXX.mjb: ExitStatus could be different and more informative,
// but I haven't had a chance to test that.
//
WaitForProcess = FALSE;
ExitStatus = 0;
} else {
WaitForProcess = TRUE;
}
AcquireProcessLock(p);
if (Exited == p->State) {
ReleaseProcessLock(p);
return;
}
p->State = Exited;
p->Flags &= ~P_WAITED; // proc may satisfy wait
//
// The goal is that after we've changed the process state, no one else
// will try to muck with this process. So we should be able to
// release the process lock now.
//
ReleaseProcessLock(p);
p->ExitStatus = ExitStatus;
WaitingParent = NULL;
Parent = NULL;
OrphanedAGroupRelatedChild = FALSE;
//
// If this is a local directory prefix, then deallocate it
//
if (!IS_DIRECTORY_PREFIX_REMOTE(p->DirectoryPrefix)) {
if (p->DirectoryPrefix) {
if (p->DirectoryPrefix->NtCurrentWorkingDirectory.Buffer) {
RtlFreeHeap(PsxHeap, 0,
(PVOID)p->DirectoryPrefix->NtCurrentWorkingDirectory.Buffer);
}
if (p->DirectoryPrefix->PsxCurrentWorkingDirectory.Buffer) {
RtlFreeHeap(PsxHeap, 0,
(PVOID)p->DirectoryPrefix->PsxCurrentWorkingDirectory.Buffer);
}
if (p->DirectoryPrefix->PsxRoot.Buffer) {
RtlFreeHeap(PsxHeap,
0,(PVOID)p->DirectoryPrefix->PsxRoot.Buffer);
}
RtlFreeHeap(PsxHeap, 0,(PVOID)p->DirectoryPrefix);
}
}
//
// Lock the process table so that we can clear process' AlarmTimer
// interlocked with AlarmApcRoutine. Can not use ProcessLock since
// it is deallocated during process exit.
//
// REVISIT deallocation of process lock...
//
AcquireProcessStructureLock();
//
// Cancel and close alarm timer if present
//
if (p->AlarmTimer) {
AlarmTimer = p->AlarmTimer;
p->AlarmTimer = NULL;
//
// Unlock process table while doing timer cleanup
//
ReleaseProcessStructureLock();
Status = NtCancelTimer(AlarmTimer, &PreviousTimerState);
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: NtCancelTimer: 0x%x\n", Status));
}
ASSERT(NT_SUCCESS(Status));
Status = NtClose(AlarmTimer);
ASSERT(NT_SUCCESS(Status));
//
// Lock the process table so that we can scan process table.
//
AcquireProcessStructureLock();
}
//
// Scan process table looking for children, and job control
// related processes.
//
AlreadyOrphaned = IsGroupOrphaned(p->ProcessGroupId);
for (cp = FirstProcess; cp < LastProcess; cp++) {
//
// Only look at non-free slots
//
if (cp->Flags & P_FREE) {
continue;
}
if (cp->ParentPid == p->Pid) {
//
// Orphan and send SIGHUP to each child process
//
cp->ParentPid = SPECIALPID;
if (cp->State == Exited) {
//
// Orphaning an exited process is almost like an
// implicit wait in the sense that process resources
// are freed immediately.
//
cp->Flags |= P_FREE;
//
// Remove Process from CLIENT_ID Hash Table
//
RemoveEntryList(&cp->ClientIdHashLinks);
ASSERT(NULL != cp);
try {
RtlDeleteCriticalSection(&cp->ProcessLock);
} except (EXCEPTION_EXECUTE_HANDLER) {
KdPrint(("PSXSS: took a fault\n"));
}
} else {
if (cp->ProcessGroupId == p->ProcessGroupId) {
OrphanedAGroupRelatedChild = TRUE;
}
// PsxSignalProcess(cp, SIGHUP);
}
}
}
if (p->ParentPid != SPECIALPID) {
//
// The process has a parent. Process termination could satisfy a
// wait() or waitpid(). Parent must also be signaled.
//
Parent = PIDTOPROCESS(p->ParentPid);
//
// See if parent is waiting. Arange for wait completion...
//
if (Parent->State == Waiting) {
WaitingParent = Parent;
}
PsxSignalProcess(Parent, SIGCHLD);
}
//
// Check to see if the exiting processes group is already orphaned.
// If so, then do not do anything. Otherwise, remove him from the
// group list and then whip through the group. If any stopped processes
// are found, then SIGCONT, SIGHUP all processes in the group
//
LockNtSessionList();
FirstMember = CONTAINING_RECORD(p->GroupLinks.Flink, PSX_PROCESS,
GroupLinks);
RemoveEntryList(&p->GroupLinks);
InitializeListHead(&p->GroupLinks);
if (!AlreadyOrphaned && FirstMember != p) {
//
// See if exit causes group to be orphaned. This is the
// case if the exiting process is an orphan, or if the
// parent is in a different session.
//
if ( OrphanedAGroupRelatedChild ||
(Parent == NULL) ||
(Parent != NULL && Parent->PsxSession != p->PsxSession) ) {
//
// Scan the group. Looking for stopped processes. If a stopped
// process is found, then for each process in the group,
// send a SIGCONT followed by a SIGHUP.
//
NextMember = FirstMember;
do {
if (NextMember->State == Stopped) {
//
// A stopped group member was found. Scan the group
// and SIGCONT, SIGHUP each member
//
NextMember = FirstMember;
do {
Next = NextMember->GroupLinks.Flink;
PsxSignalProcess(NextMember, SIGCONT);
PsxSignalProcess(NextMember, SIGHUP);
NextMember = CONTAINING_RECORD(Next, PSX_PROCESS,
GroupLinks);
} while (NextMember != FirstMember);
break;
}
Next = NextMember->GroupLinks.Flink;
NextMember = CONTAINING_RECORD(Next, PSX_PROCESS, GroupLinks);
} while (NextMember != FirstMember);
}
}
UnlockNtSessionList();
//
// Unlock process table here; we depend on having just one api
// thread, so no one can call fork while we're still closing the
// files and so forth.
//
ReleaseProcessStructureLock();
//
// Now just close files, deallocate the session, close the thread,
// terminate the process, and close the process.
//
CloseProcessFileTable(p);
if (!(p->Flags & P_FOREIGN_EXEC)) {
//
// When a foreign process has exited, it's responsible
// for shooting its own threads.
//
Status = NtTerminateThread(p->Thread, ExitStatus);
#ifdef PSX_MORE_ERRORS
if (!NT_SUCCESS(Status)) {
// XXX.mjb: this may fail if the thread has already died
// for some reason, f.e. been shot by pview.
KdPrint(("PSXSS: NtTerminateThread: 0x%x\n", Status));
}
#endif
}
p->Flags &= ~P_FOREIGN_EXEC;
//
// We like to call NtWaitForSingle object on the thread handle,
// but we don't do that if the process has
// died in some unusual way, like the dll init routine failed.
//
if (WaitForProcess) {
Status = NtWaitForSingleObject(p->Thread, TRUE, NULL);
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: Exit: NtWait: 0x%x\n", Status));
}
}
Status = NtClose(p->Thread);
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: NtClose dead thread: 0x%x\n", Status));
}
ASSERT(NT_SUCCESS(Status));
//
// We like to wait on the process handle here, sometimes we don't
// do that, see above.
//
if (WaitForProcess) {
Status = NtWaitForSingleObject(p->Process, TRUE, NULL);
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: Exit: NtWait: 0x%x\n", Status));
}
}
//
// Get the time for this process and add to the accumulated time for
// the process
//
Status = NtQueryInformationProcess(p->Process, ProcessTimes,
(PVOID)&ProcessTime, sizeof(KERNEL_USER_TIMES), NULL);
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: NtQueryInfoProc: 0x%x\n", Status));
} else {
ULONG Remainder;
ULONG PosixTime;
PosixTime = RtlExtendedLargeIntegerDivide(
ProcessTime.KernelTime, 10000, &Remainder).LowPart;
p->ProcessTimes.tms_stime += PosixTime;
PosixTime = RtlExtendedLargeIntegerDivide(
ProcessTime.UserTime, 10000, &Remainder).LowPart;
p->ProcessTimes.tms_utime += PosixTime;
}
NtClose(p->Process);
DEREFERENCE_PSX_SESSION(p->PsxSession, ExitStatus >> 8);
Status = NtClose(p->ClientPort);
#ifdef PSX_MORE_ERRORS
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: NtClose: 0x%x\n", Status));
}
#endif
//
// Remove the process from ClientIdHashTable; this must be done
// now rather than in wait(), because unless we do it now a new
// process could be created with the same ClientId as this zombie.
// This process shouldn't be making any more api requests, and
// that's the only time we look processes up by ClientId (right?).
//
RemoveEntryList(&p->ClientIdHashLinks);
InitializeListHead(&p->ClientIdHashLinks);
if (p->ParentPid == SPECIALPID) {
//
// Parent won't clean up after wait, so do all clean up here
//
p->Flags |= P_FREE;
//
// REVISIT... This might have to stay around
//
RtlDeleteCriticalSection(&p->ProcessLock);
}
if (NULL != WaitingParent) {
UnblockProcess(WaitingParent, WaitSatisfyInterrupt, FALSE, 0);
}
}
PSZ PsxpDefaultRoot = "\\DosDevices\\C:";
BOOLEAN
PsxInitializeDirectories(
IN PPSX_PROCESS Process,
IN PANSI_STRING pCwd
)
{
PPSX_DIRECTORY_PREFIX DirectoryPrefix;
PSZ Root;
char sbWorkingDirectory[512];
char sbRoot[512];
ULONG len;
PSX_GET_SESSION_NAME_A(sbWorkingDirectory, DOSDEVICE_A);
(void)strcat(sbWorkingDirectory, pCwd->Buffer);
(void)strcpy(sbRoot, sbWorkingDirectory);
PSX_GET_STRLEN(PsxpDefaultRoot,len);
sbRoot[len] = '\0';
Root = sbRoot;
//
// The current working directory should end in a "\". We add one
// if it isn't there already.
//
if ('\\' != sbWorkingDirectory[strlen(sbWorkingDirectory) - 1]) {
(void)strcat(sbWorkingDirectory, "\\");
}
DirectoryPrefix = RtlAllocateHeap(PsxHeap, 0,sizeof(*DirectoryPrefix));
if (NULL == DirectoryPrefix) {
return FALSE; // ENOMEM
}
DirectoryPrefix->PsxRoot.Buffer = NULL;
DirectoryPrefix->NtCurrentWorkingDirectory.Buffer =
RtlAllocateHeap(PsxHeap, 0, strlen(sbWorkingDirectory) + 1);
if (NULL == DirectoryPrefix->NtCurrentWorkingDirectory.Buffer) {
RtlFreeHeap(PsxHeap, 0, DirectoryPrefix);
return FALSE;
}
DirectoryPrefix->NtCurrentWorkingDirectory.Length =
(USHORT)strlen(sbWorkingDirectory);
DirectoryPrefix->NtCurrentWorkingDirectory.MaximumLength =
DirectoryPrefix->NtCurrentWorkingDirectory.Length + 1;
RtlMoveMemory(DirectoryPrefix->NtCurrentWorkingDirectory.Buffer,
sbWorkingDirectory,
DirectoryPrefix->NtCurrentWorkingDirectory.MaximumLength);
DirectoryPrefix->PsxCurrentWorkingDirectory.Length = 0;
DirectoryPrefix->PsxRoot.Buffer = RtlAllocateHeap(PsxHeap, 0,
strlen(Root) + 1);
if (! DirectoryPrefix->PsxRoot.Buffer) {
RtlFreeHeap(PsxHeap, 0,
DirectoryPrefix->NtCurrentWorkingDirectory.Buffer);
RtlFreeHeap(PsxHeap, 0, DirectoryPrefix);
return FALSE;
}
DirectoryPrefix->PsxRoot.Length = (USHORT)strlen(Root);
DirectoryPrefix->PsxRoot.MaximumLength =
DirectoryPrefix->PsxRoot.Length + 1;
RtlMoveMemory(DirectoryPrefix->PsxRoot.Buffer, Root,
DirectoryPrefix->PsxRoot.MaximumLength);
Process->DirectoryPrefix = DirectoryPrefix;
return TRUE;
}
//
// PsxPropagateDirectories --
//
// Read the root directory, the current working directory
// from an existing process.
//
BOOLEAN
PsxPropagateDirectories(
IN PPSX_PROCESS Process
)
{
NTSTATUS Status;
PPSX_DIRECTORY_PREFIX Prefix = NULL;
STRING ClientPrefix;
PVOID p;
Prefix = RtlAllocateHeap(PsxHeap, 0, sizeof(*Prefix));
if (NULL == Prefix) {
goto ErrorExit;
}
Prefix->PsxRoot.Buffer = NULL;
Prefix->NtCurrentWorkingDirectory.Buffer = NULL;
Status = NtReadVirtualMemory(Process->Process,
(PVOID)MAKE_DIRECTORY_PREFIX_VALID(Process->DirectoryPrefix),
(PVOID)Prefix, sizeof(*Prefix), NULL);
if (!NT_SUCCESS(Status)) {
goto ErrorExit;
}
//
// Capture the Root and NtCurrentWorkingDirectory
//
// XXX.mjb: TODO sanity check lengths ?
//
ClientPrefix = Prefix->PsxRoot;
p = RtlAllocateHeap(PsxHeap, 0, ClientPrefix.Length);
if (NULL == p) {
goto ErrorExit;
}
Prefix->PsxRoot.Buffer = p;
Status = NtReadVirtualMemory(Process->Process, ClientPrefix.Buffer,
Prefix->PsxRoot.Buffer, ClientPrefix.Length, NULL);
if (!NT_SUCCESS(Status)) {
goto ErrorExit;
}
Prefix->PsxRoot.Length = ClientPrefix.Length;
Prefix->PsxRoot.MaximumLength = ClientPrefix.Length;
ClientPrefix = Prefix->NtCurrentWorkingDirectory;
p = RtlAllocateHeap(PsxHeap, 0, ClientPrefix.Length);
if (NULL == p) {
goto ErrorExit;
}
Prefix->NtCurrentWorkingDirectory.Buffer = p;
Status = NtReadVirtualMemory(Process->Process, ClientPrefix.Buffer,
Prefix->NtCurrentWorkingDirectory.Buffer,
ClientPrefix.Length, NULL);
if (!NT_SUCCESS(Status)) {
goto ErrorExit;
}
Prefix->NtCurrentWorkingDirectory.Length = ClientPrefix.Length;
Prefix->NtCurrentWorkingDirectory.MaximumLength = ClientPrefix.Length;
Prefix->PsxCurrentWorkingDirectory.Buffer = NULL;
Prefix->PsxCurrentWorkingDirectory.Length = 0;
Prefix->PsxCurrentWorkingDirectory.MaximumLength = 0;
Process->DirectoryPrefix = Prefix;
return TRUE;
ErrorExit:
if (NULL != Prefix) {
if (NULL != Prefix->NtCurrentWorkingDirectory.Buffer) {
RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix->NtCurrentWorkingDirectory.Buffer);
}
if (NULL != Prefix->PsxRoot.Buffer) {
RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix->PsxRoot.Buffer);
}
RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix);
}
return FALSE;
}
//
// When we allocate space in PsxPropagateDirectories, and then find
// that we can't use it, we free it with PsxFreeDirectories.
//
VOID
PsxFreeDirectories(
IN PPSX_PROCESS p
)
{
PPSX_DIRECTORY_PREFIX Prefix;
Prefix = p->DirectoryPrefix;
if (NULL != Prefix->NtCurrentWorkingDirectory.Buffer) {
RtlFreeHeap(PsxHeap, 0, Prefix->NtCurrentWorkingDirectory.Buffer);
}
if (NULL != Prefix->PsxRoot.Buffer) {
RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix->PsxRoot.Buffer);
}
if (NULL != Prefix) {
RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix);
}
}
/*++
Routine Description:
Locate a Posix Session by it's Unique Id.
Arguments:
UniqueId - the session's unique id.
Return Value:
NULL - no such session could be found.
Non-NULL - the found session.
--*/
PPSX_SESSION
PsxLocateSessionByUniqueId(
ULONG UniqueId
)
{
PPSX_PROCESS Process;
//
// We don't have a list of sessions at the moment, so we
// linear-scan the process table, checking the session of
// each process to see if it's the one we want.
//
AcquireProcessStructureLock();
for (Process = FirstProcess; Process < LastProcess; Process++) {
if (Process->Flags & P_FREE) {
continue;
}
if (NULL == Process->PsxSession->Terminal)
continue;
if (Process->PsxSession->Terminal->UniqueId == UniqueId) {
ReleaseProcessStructureLock();
return Process->PsxSession;
}
}
ReleaseProcessStructureLock();
return NULL;
}