1973 lines
50 KiB
C
1973 lines
50 KiB
C
/*--
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
srvtask.c
|
|
|
|
Abstract:
|
|
|
|
Implementation of PSX Process Structure APIs.
|
|
|
|
Author:
|
|
|
|
Mark Lucovsky (markl) 08-Mar-1989
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "psxsrv.h"
|
|
#include "sesport.h"
|
|
|
|
#ifdef _MIPS_
|
|
#include "psxmips.h"
|
|
#endif
|
|
|
|
#ifdef _ALPHA_
|
|
#include "psxalpha.h"
|
|
#endif
|
|
|
|
#ifdef _X86_
|
|
#include "psxi386.h"
|
|
#endif
|
|
|
|
#ifdef _PPC_
|
|
#include "psxppc.h"
|
|
#endif
|
|
|
|
#ifdef _IA64_
|
|
#include "psxia64.h"
|
|
#endif
|
|
|
|
#include <time.h>
|
|
|
|
extern VOID
|
|
PsxFreeDirectories(
|
|
IN PPSX_PROCESS
|
|
);
|
|
|
|
VOID
|
|
ConvertPathToWin(char *path);
|
|
|
|
BOOLEAN
|
|
PsxFork(
|
|
IN PPSX_PROCESS p,
|
|
IN PPSX_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements posix fork() API
|
|
|
|
Arguments:
|
|
|
|
p - Supplies the address of the calling process
|
|
|
|
m - Supplies the address of the related message
|
|
|
|
Return Value:
|
|
|
|
TRUE - Always succeeds and generates a reply
|
|
|
|
--*/
|
|
|
|
{
|
|
PPSX_PROCESS ForkProcess, NewProcess;
|
|
HANDLE NewProcessHandle, NewThreadHandle;
|
|
THREAD_BASIC_INFORMATION ThreadBasicInfo;
|
|
PVOID ExcptList;
|
|
CONTEXT Context;
|
|
ULONG Psp;
|
|
NTSTATUS st;
|
|
INITIAL_TEB InitialTeb;
|
|
CLIENT_ID ClientId;
|
|
PPSX_FORK_MSG args;
|
|
HANDLE h;
|
|
|
|
args = &m->u.Fork;
|
|
|
|
if (p->Flags & P_NO_FORK) {
|
|
|
|
//
|
|
// This process may not fork; it's context has been
|
|
// rearranged to make it call the PdxNullApiCaller.
|
|
//
|
|
|
|
m->Error = EINTR;
|
|
m->Signal = SIGCONT;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Impersonate the client to insure that the new process will belong
|
|
// to him instead of to us.
|
|
//
|
|
|
|
st = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
|
|
|
if (!NT_SUCCESS(st)) {
|
|
m->Error = EAGAIN;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Create a new process to be the child. The ExceptionPort is
|
|
// initialized to PsxApiPort.
|
|
//
|
|
|
|
st = NtCreateProcess(&NewProcessHandle, PROCESS_ALL_ACCESS, NULL,
|
|
p->Process, TRUE, NULL, NULL, PsxApiPort);
|
|
if (!NT_SUCCESS(st)) {
|
|
EndImpersonation();
|
|
m->Error = EAGAIN;
|
|
return TRUE;
|
|
}
|
|
|
|
{
|
|
ULONG HardErrorMode = 0; // disable popups
|
|
st = NtSetInformationProcess(
|
|
NewProcessHandle,
|
|
ProcessDefaultHardErrorMode,
|
|
(PVOID)&HardErrorMode,
|
|
sizeof(HardErrorMode)
|
|
);
|
|
ASSERT(NT_SUCCESS(st));
|
|
}
|
|
|
|
Context.ContextFlags = CONTEXT_FULL;
|
|
|
|
st = NtGetContextThread(p->Thread, &Context);
|
|
if (!NT_SUCCESS(st)) {
|
|
NtTerminateProcess(NewProcessHandle, STATUS_SUCCESS);
|
|
NtWaitForSingleObject(NewProcessHandle, FALSE, NULL);
|
|
NtClose(NewProcessHandle);
|
|
EndImpersonation();
|
|
m->Error = EINVAL;
|
|
return TRUE;
|
|
}
|
|
|
|
InitialTeb.OldInitialTeb.OldStackBase = NULL;
|
|
InitialTeb.OldInitialTeb.OldStackLimit = NULL;
|
|
#ifdef _IA64_
|
|
InitialTeb.OldInitialTeb.OldBStoreLimit = NULL;
|
|
InitialTeb.BStoreLimit = args->BStoreLimit;
|
|
#endif
|
|
InitialTeb.StackBase = args->StackBase;
|
|
InitialTeb.StackLimit = args->StackLimit;
|
|
InitialTeb.StackAllocationBase = args->StackAllocationBase;
|
|
|
|
SetPsxForkReturn(Context);
|
|
st = NtCreateThread(&NewThreadHandle, THREAD_ALL_ACCESS, NULL,
|
|
NewProcessHandle, &ClientId, &Context, &InitialTeb, TRUE);
|
|
|
|
EndImpersonation();
|
|
|
|
if (!NT_SUCCESS(st)) {
|
|
st = NtTerminateProcess(NewProcessHandle, STATUS_SUCCESS);
|
|
ASSERT(NT_SUCCESS(st));
|
|
NtWaitForSingleObject(NewProcessHandle, FALSE, NULL);
|
|
NtClose(NewProcessHandle);
|
|
m->Error = EAGAIN;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Allocate a process structure.
|
|
//
|
|
|
|
NewProcess = PsxAllocateProcess(&ClientId);
|
|
|
|
if (NULL == NewProcess) {
|
|
st = NtTerminateProcess(NewProcessHandle, STATUS_SUCCESS);
|
|
ASSERT(NT_SUCCESS(st));
|
|
st = NtResumeThread(NewThreadHandle,&Psp);
|
|
ASSERT(NT_SUCCESS(st));
|
|
NtWaitForSingleObject(NewProcessHandle, FALSE, NULL);
|
|
NtClose(NewProcessHandle);
|
|
NtClose(NewThreadHandle);
|
|
m->Error = EAGAIN;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Copy the ExceptionList pointer from the parent process TEB
|
|
// to the child process TEB.
|
|
//
|
|
|
|
st = NtQueryInformationThread(p->Thread, ThreadBasicInformation,
|
|
(PVOID)&ThreadBasicInfo, sizeof(ThreadBasicInfo), NULL);
|
|
ASSERT(NT_SUCCESS(st));
|
|
|
|
// XXX.mjb: The actual address we want to read is
|
|
// TebBaseAddress->NtTib.ExceptionList, but I happen to know
|
|
// that these are equivalent.
|
|
//
|
|
|
|
st = NtReadVirtualMemory(p->Process,
|
|
(PVOID)ThreadBasicInfo.TebBaseAddress,
|
|
(PVOID)&ExcptList, sizeof(ExcptList), NULL);
|
|
ASSERT(NT_SUCCESS(st));
|
|
|
|
st = NtQueryInformationThread(NewThreadHandle, ThreadBasicInformation,
|
|
(PVOID)&ThreadBasicInfo, sizeof(ThreadBasicInfo), NULL);
|
|
ASSERT(NT_SUCCESS(st));
|
|
|
|
st = NtWriteVirtualMemory(NewProcessHandle,
|
|
(PVOID)ThreadBasicInfo.TebBaseAddress,
|
|
(PVOID)&ExcptList, sizeof(ExcptList), NULL);
|
|
ASSERT(NT_SUCCESS(st));
|
|
|
|
|
|
ForkProcess = p;
|
|
|
|
//
|
|
// The new process is allocated locked, but we need to lock the
|
|
// parent (ForkProcess).
|
|
//
|
|
AcquireProcessLock(ForkProcess);
|
|
|
|
st = PsxInitializeProcess(NewProcess, ForkProcess, 0L, NewProcessHandle,
|
|
NewThreadHandle, NULL);
|
|
if (!NT_SUCCESS(st)) {
|
|
st = NtTerminateProcess(NewProcessHandle, STATUS_SUCCESS);
|
|
ASSERT(NT_SUCCESS(st));
|
|
st = NtResumeThread(NewThreadHandle,&Psp);
|
|
ASSERT(NT_SUCCESS(st));
|
|
st = NtWaitForSingleObject(NewProcessHandle, FALSE, NULL);
|
|
NtClose(NewProcessHandle);
|
|
NtClose(NewThreadHandle);
|
|
m->Error = EAGAIN;
|
|
return TRUE;
|
|
}
|
|
|
|
NewProcess->InitialPebPsxData.Length =
|
|
sizeof(ForkProcess->InitialPebPsxData);
|
|
|
|
NewProcess->InitialPebPsxData.ClientStartAddress = NULL;
|
|
if (NULL != ForkProcess->PsxSession->Terminal) {
|
|
NewProcess->InitialPebPsxData.SessionPortHandle =
|
|
(HANDLE)ForkProcess->PsxSession->Terminal->UniqueId;
|
|
} else {
|
|
//
|
|
// What if there are no open file descriptors?
|
|
//
|
|
|
|
if (NULL != ForkProcess->ProcessFileTable[0].SystemOpenFileDesc)
|
|
NewProcess->InitialPebPsxData.SessionPortHandle =
|
|
(HANDLE)ForkProcess->ProcessFileTable[0].SystemOpenFileDesc->Terminal->UniqueId;
|
|
}
|
|
|
|
m->ReturnValue = NewProcess->Pid;
|
|
|
|
st = NtResumeThread(NewThreadHandle, &Psp);
|
|
ASSERT(NT_SUCCESS(st));
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
PsxExec(
|
|
IN PPSX_PROCESS p,
|
|
IN PPSX_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements posix execve() API
|
|
|
|
Arguments:
|
|
|
|
p - Supplies the address of the calling process
|
|
|
|
m - Supplies the address of the related message
|
|
|
|
Return Value:
|
|
|
|
TRUE - Always succeeds and generates a reply
|
|
|
|
--*/
|
|
|
|
{
|
|
RTL_USER_PROCESS_INFORMATION ProcInfo;
|
|
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
|
|
PPSX_EXEC_MSG args;
|
|
WCHAR ImageFileString[1024];
|
|
ANSI_STRING CommandLine;
|
|
ANSI_STRING CWD;
|
|
NTSTATUS Status;
|
|
ULONG whocares;
|
|
ULONG i;
|
|
KERNEL_USER_TIMES ProcessTime;
|
|
ULONG PosixTime, Remainder;
|
|
UNICODE_STRING uCWD, uImageFileName;
|
|
PUNICODE_STRING u;
|
|
PVOID SaveDirectoryPrefix;
|
|
HANDLE h;
|
|
USHORT len;
|
|
|
|
args = &m->u.Exec;
|
|
|
|
//
|
|
// If pathname is too big, then return error. This should
|
|
// really allow for names that are longer because of PSX
|
|
// prefixes
|
|
//
|
|
|
|
if (args->Path.Length > PATH_MAX) {
|
|
m->Error = ENAMETOOLONG;
|
|
return TRUE;
|
|
}
|
|
|
|
AcquireProcessLock(p);
|
|
if (Exited == p->State) {
|
|
ReleaseProcessLock(p);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Capture the pathname
|
|
//
|
|
|
|
Status = NtReadVirtualMemory(p->Process, args->Path.Buffer,
|
|
&ImageFileString[0], args->Path.Length, NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ReleaseProcessLock(p);
|
|
m->Error = ENOMEM;
|
|
return TRUE;
|
|
}
|
|
|
|
uImageFileName.Buffer = &ImageFileString[0];
|
|
uImageFileName.Length = args->Path.Length;
|
|
uImageFileName.MaximumLength = args->Path.Length;
|
|
|
|
//
|
|
// Propagate Current Working Directory
|
|
//
|
|
|
|
SaveDirectoryPrefix = (PVOID)p->DirectoryPrefix;
|
|
|
|
if (!PsxPropagateDirectories(p)) {
|
|
ReleaseProcessLock(p);
|
|
p->DirectoryPrefix = SaveDirectoryPrefix;
|
|
m->Error = ENOMEM;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Format the process parameters
|
|
//
|
|
|
|
CommandLine.Buffer = args->Args;
|
|
CommandLine.Length = ARG_MAX;
|
|
CommandLine.MaximumLength = ARG_MAX;
|
|
|
|
PSX_GET_STRLEN(DOSDEVICE_A,len);
|
|
|
|
CWD.Buffer = p->DirectoryPrefix->NtCurrentWorkingDirectory.Buffer + len;
|
|
|
|
CWD.MaximumLength = p->DirectoryPrefix->NtCurrentWorkingDirectory.Length - len;
|
|
|
|
CWD.Length = CWD.MaximumLength;
|
|
Status = RtlAnsiStringToUnicodeString(&uCWD, &CWD, TRUE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
PsxFreeDirectories(p);
|
|
p->DirectoryPrefix = SaveDirectoryPrefix;
|
|
ReleaseProcessLock(p);
|
|
m->Error = ENOMEM;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Somewhere along the line someone changes the old process's
|
|
// DllPath to Unicode. If we pass a NULL DllPath here, we find
|
|
// that Ansi is expected, and we can't find the Dll. So here we
|
|
// take the Unicode DllPath, convert to Ansi, and pass it
|
|
// explicitly.
|
|
//
|
|
|
|
u = (PUNICODE_STRING)&NtCurrentPeb()->ProcessParameters->DllPath;
|
|
Status = RtlCreateProcessParameters(&ProcessParameters, &uImageFileName,
|
|
u, &uCWD, (PUNICODE_STRING)&CommandLine, NULL, NULL, NULL, NULL, NULL);
|
|
|
|
#ifndef EXEC_FOREIGN
|
|
RtlFreeUnicodeString(&uCWD);
|
|
#endif
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
#ifdef EXEC_FOREIGN
|
|
RtlFreeUnicodeString(&uCWD);
|
|
#endif
|
|
PsxFreeDirectories(p);
|
|
p->DirectoryPrefix = SaveDirectoryPrefix;
|
|
ReleaseProcessLock(p);
|
|
m->Error = ENOMEM;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Create the process and thread. We impersonate the client so that
|
|
// they end up being owned by him, instead of owned by us.
|
|
//
|
|
|
|
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = RtlCreateUserProcess(&uImageFileName, 0, ProcessParameters,
|
|
NULL, NULL, p->Process, FALSE, NULL, NULL, &ProcInfo);
|
|
|
|
EndImpersonation();
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
#ifdef EXEC_FOREIGN
|
|
RtlFreeUnicodeString(&uCWD);
|
|
#endif
|
|
PsxFreeDirectories(p);
|
|
p->DirectoryPrefix = SaveDirectoryPrefix;
|
|
ReleaseProcessLock(p);
|
|
|
|
if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
|
|
m->Error = PsxStatusToErrnoPath(&uImageFileName);
|
|
return TRUE;
|
|
}
|
|
m->Error = PsxStatusToErrno(Status);
|
|
return TRUE;
|
|
}
|
|
|
|
RtlDestroyProcessParameters(ProcessParameters);
|
|
|
|
//
|
|
// Set the exception port for the new process so we'll find out
|
|
// if he takes a fault.
|
|
//
|
|
|
|
Status = NtSetInformationProcess(ProcInfo.Process,
|
|
ProcessExceptionPort, (PVOID)&PsxApiPort, sizeof(PsxApiPort));
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("PSXSS: NtSetInfoProcess: 0x%x\n", Status));
|
|
}
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
{
|
|
ULONG HardErrorMode = 0; // disable popups
|
|
Status = NtSetInformationProcess(
|
|
ProcInfo.Process,
|
|
ProcessDefaultHardErrorMode,
|
|
(PVOID)&HardErrorMode,
|
|
sizeof(HardErrorMode)
|
|
);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
|
|
//
|
|
// check to make sure it is a POSIX app
|
|
//
|
|
|
|
if (ProcInfo.ImageInformation.SubSystemType !=
|
|
IMAGE_SUBSYSTEM_POSIX_CUI) {
|
|
|
|
//
|
|
// The image is not a Posix program. Tear down the
|
|
// process we just created in the usual way, and then
|
|
// call the windows subsystem to do the same thing
|
|
// over.
|
|
//
|
|
|
|
PsxFreeDirectories(p);
|
|
p->DirectoryPrefix = SaveDirectoryPrefix;
|
|
#ifndef EXEC_FOREIGN
|
|
ReleaseProcessLock(p);
|
|
#endif
|
|
Status = NtTerminateProcess(ProcInfo.Process, STATUS_SUCCESS);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("PSXSS: NtTerminateProcess: 0x%x\n", Status));
|
|
}
|
|
Status = NtWaitForSingleObject(ProcInfo.Process, FALSE, NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("PSXSS: NtWaitForSingleObject: 0x%x\n", Status));
|
|
}
|
|
#ifdef EXEC_FOREIGN
|
|
Status = ExecForeignImage(p, m, &uImageFileName, &uCWD);
|
|
ReleaseProcessLock(p);
|
|
RtlFreeUnicodeString(&uCWD);
|
|
if (!NT_SUCCESS(Status)) {
|
|
m->Error = ENOEXEC;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
#else
|
|
m->Error = ENOEXEC;
|
|
return TRUE;
|
|
#endif
|
|
|
|
}
|
|
#ifdef EXEC_FOREIGN
|
|
RtlFreeUnicodeString(&uCWD);
|
|
#endif
|
|
|
|
//
|
|
// Close open files that have their close-on-exec bit set.
|
|
//
|
|
|
|
for (i = 0; i < OPEN_MAX; ++i) {
|
|
if (NULL != p->ProcessFileTable[i].SystemOpenFileDesc &&
|
|
p->ProcessFileTable[i].Flags & PSX_FD_CLOSE_ON_EXEC) {
|
|
(void)DeallocateFd(p, i);
|
|
}
|
|
}
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
//
|
|
// 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);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
PosixTime = RtlExtendedLargeIntegerDivide(ProcessTime.KernelTime,
|
|
10000, &Remainder).LowPart;
|
|
p->ProcessTimes.tms_stime += PosixTime;
|
|
|
|
PosixTime = RtlExtendedLargeIntegerDivide(ProcessTime.UserTime,
|
|
10000, &Remainder).LowPart;
|
|
p->ProcessTimes.tms_utime += PosixTime;
|
|
|
|
//
|
|
// Terminate the current process, and munge in the
|
|
// new process
|
|
//
|
|
|
|
RemoveEntryList(&p->ClientIdHashLinks);
|
|
p->ClientIdHashLinks.Flink = p->ClientIdHashLinks.Blink = NULL;
|
|
|
|
Status = NtTerminateProcess(p->Process, STATUS_SUCCESS);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = NtWaitForSingleObject(p->Process, FALSE, NULL);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
|
|
Status = NtClose(p->ClientPort);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = NtClose(p->Process);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = NtClose(p->Thread);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
p->Process = ProcInfo.Process;
|
|
p->Thread = ProcInfo.Thread;
|
|
p->ClientId = ProcInfo.ClientId;
|
|
p->ClientPort = NULL;
|
|
|
|
InsertTailList(&ClientIdHashTable[CIDTOHASHINDEX(&p->ClientId)],
|
|
&p->ClientIdHashLinks);
|
|
|
|
p->Flags |= P_HAS_EXECED;
|
|
|
|
ReleaseProcessStructureLock();
|
|
|
|
//
|
|
// Restore signals being caught to SIG_DFL
|
|
// --- Posix does not specify what to do w/ associated flags or mask
|
|
//
|
|
|
|
for (i = 0; i < _SIGMAXSIGNO; i++) {
|
|
if (p->SignalDataBase.SignalDisposition[i].sa_handler !=
|
|
SIG_IGN) {
|
|
p->SignalDataBase.SignalDisposition[i].sa_handler =
|
|
SIG_DFL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Since we don't reply to the old process (he's gone now), we need
|
|
// to start the new process's InPsx count at zero.
|
|
//
|
|
|
|
p->InPsx = 0;
|
|
|
|
ReleaseProcessLock(p);
|
|
ExecProcessFileTable(p);
|
|
|
|
if (p->ProcessIsBeingDebugged && PsxpDebuggerActive) {
|
|
Status = NtSetInformationProcess(p->Process, ProcessDebugPort,
|
|
(PVOID)&PsxpDebugPort, sizeof(HANDLE));
|
|
if (!NT_SUCCESS(Status)) {
|
|
p->ProcessIsBeingDebugged = FALSE;
|
|
}
|
|
}
|
|
|
|
Status = NtResumeThread(p->Thread,&whocares);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("PSXSS: NtResumeThread: 0x%x\n", Status));
|
|
}
|
|
ASSERT(NT_SUCCESS(Status) && whocares == 1);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
PsxGetIds(
|
|
IN PPSX_PROCESS p,
|
|
IN PPSX_API_MSG m
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function provides all the support needed to implement
|
|
getpid(), getppid(), getuid(), geteuid(), getgid(), and getegid().
|
|
|
|
Arguments:
|
|
|
|
p - Supplies the address of the calling process
|
|
|
|
m - Supplies the address of the related message
|
|
|
|
Return Value:
|
|
|
|
TRUE - Always succeeds and generates a reply
|
|
|
|
--*/
|
|
{
|
|
PPSX_GETIDS_MSG args;
|
|
|
|
args = &m->u.GetIds;
|
|
|
|
args->Pid = p->Pid;
|
|
args->ParentPid = p->ParentPid;
|
|
args->GroupId = p->ProcessGroupId;
|
|
args->RealUid = p->RealUid;
|
|
args->EffectiveUid = p->EffectiveUid;
|
|
args->RealGid = p->RealGid;
|
|
args->EffectiveGid = p->EffectiveGid;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
PsxExit(
|
|
IN PPSX_PROCESS p,
|
|
IN PPSX_API_MSG m
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements the _exit() API.
|
|
|
|
Arguments:
|
|
|
|
p - Supplies the address of the calling process
|
|
|
|
m - Supplies the address of the related message
|
|
|
|
Return Value:
|
|
|
|
TRUE - Always succeeds and generates a reply
|
|
|
|
--*/
|
|
{
|
|
PPSX_EXIT_MSG args;
|
|
|
|
args = &m->u.Exit;
|
|
|
|
Exit(p, (args->ExitStatus & 0xff) << 8);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
WaitPidHandler(
|
|
IN PPSX_PROCESS p,
|
|
IN PINTCB IntControlBlock,
|
|
IN PSX_INTERRUPTREASON InterruptReason,
|
|
IN int Signal
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called whenever a process that is in a waitpid wait
|
|
is sent a signal, or has a child stop/terminate that could possibly
|
|
satisfy a wait.
|
|
|
|
This function is responsible for unlocking the process.
|
|
|
|
Arguments:
|
|
|
|
p - Supplies the address of the process being interrupted.
|
|
|
|
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;
|
|
PPSX_WAITPID_MSG args;
|
|
PPSX_PROCESS cp;
|
|
BOOLEAN WaitSatisfied;
|
|
pid_t TargetProcess;
|
|
pid_t TargetGroup;
|
|
enum _WaitType { AnyProcess, SpecificProcess, SpecificGroup };
|
|
enum _WaitType WaitType;
|
|
|
|
RtlLeaveCriticalSection(&BlockLock);
|
|
|
|
m = IntControlBlock->IntMessage;
|
|
|
|
args = &m->u.WaitPid;
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
p->State = Active;
|
|
|
|
if (InterruptReason == SignalInterrupt) {
|
|
|
|
ReleaseProcessStructureLock();
|
|
|
|
RtlFreeHeap(PsxHeap, 0, (PVOID)IntControlBlock);
|
|
|
|
m->Error = EINTR;
|
|
m->Signal = Signal;
|
|
ApiReply(p,m,NULL);
|
|
RtlFreeHeap(PsxHeap, 0, (PVOID)m);
|
|
return;
|
|
}
|
|
|
|
WaitSatisfied = FALSE;
|
|
|
|
TargetProcess = SPECIALPID;
|
|
TargetGroup = SPECIALPID;
|
|
WaitType = AnyProcess;
|
|
|
|
if (args->Pid <= 0 && args->Pid != (pid_t)-1) {
|
|
|
|
WaitType = SpecificGroup;
|
|
|
|
//
|
|
// Process group id is specified.
|
|
//
|
|
|
|
if (args->Pid == 0) {
|
|
TargetGroup = p->ProcessGroupId;
|
|
} else {
|
|
TargetGroup = -1 * args->Pid;
|
|
}
|
|
} else {
|
|
|
|
if (args->Pid != (pid_t)-1) {
|
|
TargetProcess = args->Pid;
|
|
WaitType = SpecificProcess;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Scan process table
|
|
//
|
|
|
|
for (cp = FirstProcess; cp < LastProcess; cp++) {
|
|
|
|
if (cp->Flags & P_FREE) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Just look at processes that could possibly satisfy a wait.
|
|
// - Processes that have exited
|
|
// - Stopped processes that have not previously satisfied a
|
|
// wait (if WUNTRACED was set)
|
|
//
|
|
|
|
if (cp->State == Exited ||
|
|
(cp->State == Stopped && (args->Options & WUNTRACED) &&
|
|
!(cp->Flags & P_WAITED))) {
|
|
|
|
if (cp->ParentPid != p->Pid) {
|
|
continue;
|
|
}
|
|
|
|
switch (WaitType) {
|
|
|
|
case AnyProcess:
|
|
|
|
m->ReturnValue = cp->Pid;
|
|
args->StatLocValue = cp->ExitStatus;
|
|
break;
|
|
|
|
case SpecificProcess:
|
|
|
|
if ( cp->Pid == TargetProcess ) {
|
|
m->ReturnValue = cp->Pid;
|
|
args->StatLocValue = cp->ExitStatus;
|
|
}
|
|
break;
|
|
|
|
case SpecificGroup:
|
|
|
|
if ( cp->ProcessGroupId == TargetGroup){
|
|
m->ReturnValue = cp->Pid;
|
|
args->StatLocValue = cp->ExitStatus;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ( m->ReturnValue ) {
|
|
|
|
//
|
|
// wait was satisfied
|
|
//
|
|
|
|
if ( cp->State == Exited ) {
|
|
|
|
p->ProcessTimes.tms_cstime += (cp->ProcessTimes.tms_stime
|
|
+ cp->ProcessTimes.tms_cstime);
|
|
p->ProcessTimes.tms_cutime += (cp->ProcessTimes.tms_utime
|
|
+ cp->ProcessTimes.tms_cutime);
|
|
|
|
//
|
|
// Deallocate the process
|
|
//
|
|
|
|
cp->Flags |= P_FREE;
|
|
RtlDeleteCriticalSection(&cp->ProcessLock);
|
|
|
|
} else {
|
|
|
|
//
|
|
// set bit so this stopped process won't satisfy another
|
|
// wait until it stops again or exits.
|
|
//
|
|
|
|
cp->Flags |= P_WAITED;
|
|
args->StatLocValue = cp->ExitStatus | (1L << 30);
|
|
}
|
|
|
|
WaitSatisfied = TRUE;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( WaitSatisfied ) {
|
|
|
|
ReleaseProcessStructureLock();
|
|
|
|
RtlFreeHeap(PsxHeap, 0, (PVOID)IntControlBlock);
|
|
ApiReply(p,m,NULL);
|
|
RtlFreeHeap(PsxHeap, 0, (PVOID)m);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Rewait
|
|
//
|
|
|
|
p->State = Waiting;
|
|
|
|
(void)BlockProcess(p,
|
|
NULL,
|
|
WaitPidHandler,
|
|
m,
|
|
NULL,
|
|
&PsxProcessStructureLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
PsxWaitPid(
|
|
IN PPSX_PROCESS p,
|
|
IN PPSX_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements the wait() and waitpid() APIs.
|
|
|
|
Arguments:
|
|
|
|
p - Supplies the address of the calling process
|
|
|
|
m - Supplies the address of the related message
|
|
|
|
Return Value:
|
|
|
|
TRUE - Always succeeds and generates a reply
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PPSX_WAITPID_MSG args;
|
|
PPSX_PROCESS cp;
|
|
pid_t TargetProcess;
|
|
pid_t TargetGroup;
|
|
BOOLEAN EmitEchild;
|
|
enum _WaitType { AnyProcess, SpecificProcess, SpecificGroup };
|
|
enum _WaitType WaitType;
|
|
|
|
args = &m->u.WaitPid;
|
|
|
|
//
|
|
// Test for invalid options
|
|
//
|
|
|
|
if (args->Options & ~(WNOHANG|WUNTRACED)) {
|
|
m->Error = EINVAL;
|
|
return TRUE;
|
|
}
|
|
|
|
TargetProcess = SPECIALPID;
|
|
TargetGroup = SPECIALPID;
|
|
WaitType = AnyProcess;
|
|
|
|
if (args->Pid <= 0 && args->Pid != (pid_t)-1) {
|
|
|
|
WaitType = SpecificGroup;
|
|
|
|
if (args->Pid == 0) {
|
|
TargetGroup = p->ProcessGroupId;
|
|
} else {
|
|
TargetGroup = -1 * args->Pid;
|
|
}
|
|
} else {
|
|
if (args->Pid != (pid_t)-1) {
|
|
TargetProcess = args->Pid;
|
|
WaitType = SpecificProcess;
|
|
}
|
|
}
|
|
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
EmitEchild = TRUE;
|
|
|
|
//
|
|
// Scan process table
|
|
//
|
|
|
|
for (cp = FirstProcess; cp < LastProcess; cp++) {
|
|
if (cp->Flags & P_FREE) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Until we know whether or not there is a process that
|
|
// could possibly satisfy a wait, we have to keep looking
|
|
// at all processes.
|
|
//
|
|
|
|
if (EmitEchild) {
|
|
if (WaitType == SpecificGroup) {
|
|
if (cp->ParentPid == p->Pid &&
|
|
cp->ProcessGroupId == TargetGroup) {
|
|
EmitEchild = FALSE;
|
|
}
|
|
} else {
|
|
if (cp->ParentPid == p->Pid) {
|
|
if (WaitType == SpecificProcess) {
|
|
if (cp->Pid == TargetProcess) {
|
|
EmitEchild = FALSE;
|
|
}
|
|
} else {
|
|
EmitEchild = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Just look at processes that could possibly satisfy a wait.
|
|
// - Processes that have exited
|
|
// - Stopped processes that have not previously satisfied
|
|
// a wait (if WUNTRACED was set)
|
|
//
|
|
|
|
if (cp->State == Exited ||
|
|
(cp->State == Stopped && (args->Options & WUNTRACED) &&
|
|
!(cp->Flags & P_WAITED))) {
|
|
|
|
if (cp->ParentPid != p->Pid) {
|
|
continue;
|
|
}
|
|
|
|
switch (WaitType) {
|
|
case AnyProcess:
|
|
m->ReturnValue = cp->Pid;
|
|
args->StatLocValue = cp->ExitStatus;
|
|
break;
|
|
|
|
case SpecificProcess:
|
|
|
|
if (cp->Pid == TargetProcess) {
|
|
m->ReturnValue = cp->Pid;
|
|
args->StatLocValue = cp->ExitStatus;
|
|
}
|
|
break;
|
|
|
|
case SpecificGroup:
|
|
|
|
if (cp->ProcessGroupId == TargetGroup) {
|
|
m->ReturnValue = cp->Pid;
|
|
args->StatLocValue = cp->ExitStatus;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (m->ReturnValue) {
|
|
|
|
//
|
|
// wait was satisfied
|
|
//
|
|
|
|
if ( cp->State == Exited ) {
|
|
|
|
p->ProcessTimes.tms_cstime += (cp->ProcessTimes.tms_stime
|
|
+ cp->ProcessTimes.tms_cstime);
|
|
p->ProcessTimes.tms_cutime += (cp->ProcessTimes.tms_utime
|
|
+ cp->ProcessTimes.tms_cutime);
|
|
|
|
//
|
|
// Deallocate the process
|
|
//
|
|
|
|
cp->Flags |= P_FREE;
|
|
RtlDeleteCriticalSection(&cp->ProcessLock);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Set a bit to keep this stopped process from satisfying
|
|
// another wait.
|
|
//
|
|
|
|
cp->Flags |= P_WAITED;
|
|
args->StatLocValue = cp->ExitStatus | (1L << 30);
|
|
}
|
|
|
|
ReleaseProcessStructureLock();
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (EmitEchild) {
|
|
m->Error = ECHILD;
|
|
ReleaseProcessStructureLock();
|
|
return TRUE;
|
|
}
|
|
|
|
if (args->Options & WNOHANG) {
|
|
m->ReturnValue = 0;
|
|
args->StatLocValue = 0;
|
|
ReleaseProcessStructureLock();
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Make the process sleep until the wait can be satisfied.
|
|
//
|
|
|
|
p->State = Waiting;
|
|
Status = BlockProcess(p, NULL, WaitPidHandler, m, NULL,
|
|
&PsxProcessStructureLock);
|
|
if (!NT_SUCCESS(Status)) {
|
|
m->Error = PsxStatusToErrno(Status);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// The process has successfully been blocked. Don't reply to the api
|
|
// request message.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
PsxSetSid(
|
|
IN PPSX_PROCESS p,
|
|
IN PPSX_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements the setsid() API.
|
|
|
|
Arguments:
|
|
|
|
p - Supplies the address of the calling process
|
|
|
|
m - Supplies the address of the related message
|
|
|
|
Return Value:
|
|
|
|
TRUE - Always succeeds and generates a reply
|
|
|
|
--*/
|
|
|
|
{
|
|
PPSX_SESSION OldSession;
|
|
|
|
//
|
|
// 1003.1-90 (4.3.2.4): EPERM when the calling process
|
|
// is already a process group leader.
|
|
//
|
|
|
|
if (p->Pid == p->ProcessGroupId) {
|
|
m->Error = EPERM;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Create a new session with no controlling tty and make
|
|
// the calling process the session leader.
|
|
//
|
|
|
|
AcquireProcessStructureLock();
|
|
LockNtSessionList();
|
|
|
|
RemoveEntryList(&p->GroupLinks);
|
|
InitializeListHead(&p->GroupLinks);
|
|
|
|
//
|
|
// Make the process the leader of his process group.
|
|
//
|
|
|
|
p->ProcessGroupId = p->Pid;
|
|
|
|
OldSession = p->PsxSession;
|
|
p->PsxSession = PsxAllocateSession(NULL, p->Pid);
|
|
|
|
UnlockNtSessionList();
|
|
ReleaseProcessStructureLock();
|
|
|
|
DEREFERENCE_PSX_SESSION(OldSession, 0);
|
|
|
|
m->ReturnValue = (pid_t)p->ProcessGroupId;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
PsxSetPGroupId(
|
|
IN PPSX_PROCESS p,
|
|
IN PPSX_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements the setpgid() API.
|
|
|
|
Arguments:
|
|
|
|
p - Supplies the address of the calling process
|
|
|
|
m - Supplies the address of the related message
|
|
|
|
Return Value:
|
|
|
|
TRUE - Always succeeds and generates a reply
|
|
|
|
--*/
|
|
|
|
{
|
|
PPSX_SETPGROUPID_MSG args;
|
|
pid_t TargetPid, TargetGroup;
|
|
PPSX_PROCESS cp, Target;
|
|
BOOLEAN EmitEsrch;
|
|
|
|
args = &m->u.SetPGroupId;
|
|
|
|
if (args->Pid < 0 || args->Pgid < 0) {
|
|
m->Error = EINVAL;
|
|
return TRUE;
|
|
}
|
|
|
|
TargetPid = (args->Pid ? args->Pid : p->Pid);
|
|
TargetGroup = (args->Pgid ? args->Pgid : TargetPid);
|
|
|
|
AcquireProcessStructureLock();
|
|
LockNtSessionList();
|
|
|
|
if ( p->Pid == TargetPid ) {
|
|
Target = p;
|
|
} else {
|
|
|
|
//
|
|
// Scan process table
|
|
//
|
|
|
|
EmitEsrch = TRUE;
|
|
|
|
for (cp = FirstProcess ;cp < LastProcess ;cp++ ) {
|
|
|
|
if ( cp->Flags & P_FREE ) {
|
|
continue;
|
|
}
|
|
|
|
if ( cp->ParentPid == p->Pid && cp->Pid == TargetPid ) {
|
|
|
|
EmitEsrch = FALSE;
|
|
Target = cp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( EmitEsrch ) {
|
|
m->Error = ESRCH;
|
|
goto done;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// If target is a child who has executed an exec, then report error
|
|
//
|
|
|
|
if (Target->ParentPid == p->Pid && (Target->Flags & P_HAS_EXECED)) {
|
|
m->Error = EACCES;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// If target is a session leader, then report error
|
|
//
|
|
|
|
if ( Target->PsxSession->SessionLeader == TargetPid ) {
|
|
m->Error = EPERM;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// If target is not in the same session as the calling process, then
|
|
// report error
|
|
//
|
|
|
|
if ( Target->PsxSession != p->PsxSession ) {
|
|
m->Error = EPERM;
|
|
goto done;
|
|
}
|
|
|
|
if ( TargetPid != TargetGroup ) {
|
|
|
|
//
|
|
// Scan process table looking for a pgrp id the same
|
|
// as TargetGroup and is in the same session is the calling process
|
|
//
|
|
|
|
EmitEsrch = TRUE;
|
|
|
|
for (cp = FirstProcess ;cp < LastProcess ;cp++ ) {
|
|
|
|
if ( cp->Flags & P_FREE ) {
|
|
continue;
|
|
}
|
|
|
|
if ( cp->ProcessGroupId == TargetGroup &&
|
|
p->PsxSession == cp->PsxSession ) {
|
|
|
|
EmitEsrch = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( EmitEsrch ) {
|
|
m->Error = EPERM;
|
|
goto done;
|
|
}
|
|
} else {
|
|
cp = Target;
|
|
}
|
|
|
|
//
|
|
// Everything is ok, so set the Target's pgrp id to the specified id
|
|
//
|
|
|
|
RemoveEntryList(&Target->GroupLinks);
|
|
|
|
if (cp != Target) {
|
|
InsertHeadList(&cp->GroupLinks, &Target->GroupLinks);
|
|
} else {
|
|
InitializeListHead(&Target->GroupLinks);
|
|
}
|
|
|
|
Target->ProcessGroupId = TargetGroup;
|
|
|
|
done:
|
|
UnlockNtSessionList();
|
|
ReleaseProcessStructureLock();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
PsxGetProcessTimes(
|
|
IN PPSX_PROCESS p,
|
|
IN PPSX_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements the times() API.
|
|
|
|
Arguments:
|
|
|
|
p - Supplies the address of the calling process
|
|
|
|
m - Supplies the address of the related message
|
|
|
|
Return Value:
|
|
|
|
TRUE - Always succeeds and generates a reply
|
|
|
|
--*/
|
|
|
|
{
|
|
PPSX_GETPROCESSTIMES_MSG args;
|
|
NTSTATUS Status;
|
|
KERNEL_USER_TIMES ProcessTime;
|
|
ULONG PosixTime, Remainder;
|
|
|
|
ULONG LengthNeeded;
|
|
|
|
args = &m->u.GetProcessTimes;
|
|
|
|
{
|
|
LARGE_INTEGER DelayInterval;
|
|
|
|
DelayInterval.HighPart = 0;
|
|
DelayInterval.LowPart = 100;
|
|
|
|
NtDelayExecution(TRUE, &DelayInterval);
|
|
}
|
|
|
|
//
|
|
// Get the time for this process and add to the accumulated time for
|
|
// the process
|
|
//
|
|
|
|
Status = NtQueryInformationProcess(p->Process, ProcessTimes,
|
|
(PVOID)&ProcessTime, sizeof(ProcessTime), &LengthNeeded);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
PosixTime = RtlExtendedLargeIntegerDivide(ProcessTime.UserTime,
|
|
10000, &Remainder).LowPart;
|
|
|
|
args->ProcessTimes.tms_utime = p->ProcessTimes.tms_utime + PosixTime;
|
|
|
|
PosixTime = RtlExtendedLargeIntegerDivide(ProcessTime.KernelTime,
|
|
10000, &Remainder).LowPart;
|
|
|
|
args->ProcessTimes.tms_stime = p->ProcessTimes.tms_stime + PosixTime;
|
|
|
|
args->ProcessTimes.tms_cutime = p->ProcessTimes.tms_cutime;
|
|
args->ProcessTimes.tms_cstime = p->ProcessTimes.tms_cstime;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
PsxGetGroups(
|
|
IN PPSX_PROCESS p,
|
|
IN PPSX_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements the getgroups() API.
|
|
|
|
Arguments:
|
|
|
|
p - Supplies the address of the calling process
|
|
|
|
m - Supplies the address of the related message
|
|
|
|
Return Value:
|
|
|
|
TRUE - Always succeeds and generates a reply
|
|
|
|
XXX.mjb:
|
|
|
|
NT essentially puts no limit on the number of supplementary groups
|
|
a user may belong to. This is bad for Posix, since we want a limit,
|
|
and we want it small enough that people don't mess themselves up by
|
|
allocating an array[NGROUPS_MAX] of gid_t.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPSX_GETGROUPS_MSG args;
|
|
NTSTATUS Status;
|
|
HANDLE TokenHandle;
|
|
TOKEN_GROUPS *pGroups;
|
|
ULONG outlen, i, j;
|
|
gid_t *GroupList;
|
|
|
|
args = &m->u.GetGroups;
|
|
|
|
//
|
|
// Check args->GroupList for group array address validity.
|
|
//
|
|
|
|
//
|
|
// Examine the new process's token to figure out what the
|
|
// uid's should be.
|
|
//
|
|
|
|
Status = NtOpenProcessToken(p->Process, GENERIC_READ,
|
|
&TokenHandle);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
//
|
|
// Get the supplemental groups.
|
|
//
|
|
|
|
Status = NtQueryInformationToken(TokenHandle, TokenGroups, NULL,
|
|
0, &outlen);
|
|
ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
|
|
|
|
pGroups = RtlAllocateHeap(PsxHeap, 0, outlen);
|
|
if (NULL == pGroups) {
|
|
//
|
|
// We don't have enough memory to hold the list of the process's
|
|
// groups. What is there to do except return an error?
|
|
//
|
|
NtClose(TokenHandle);
|
|
m->Error = ENOMEM;
|
|
return TRUE;
|
|
}
|
|
|
|
Status = NtQueryInformationToken(TokenHandle, TokenGroups, (PVOID)pGroups,
|
|
outlen, &outlen);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("PSXSS: NtQueryInformationToken failed: 0x%x\n",
|
|
Status));
|
|
RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
|
|
NtClose(TokenHandle);
|
|
m->Error = EACCES;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If the user has passed in a listsize of 0, then we only return
|
|
// the number of supplementary groups, without writing anything in
|
|
// the group array.
|
|
//
|
|
if (0 == args->NGroups) {
|
|
m->ReturnValue = pGroups->GroupCount;
|
|
m->Error = 0;
|
|
|
|
NtClose(TokenHandle);
|
|
RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if ((ULONG)args->NGroups < pGroups->GroupCount) {
|
|
if (args->NGroups < NGROUPS_MAX) {
|
|
m->Error = EINVAL;
|
|
return TRUE;
|
|
}
|
|
//
|
|
// XXX.mjb: We're having a problem here. The caller has
|
|
// allocated space for the maximum number of groups that the
|
|
// user can have, according to the _NGROUPS_MAX limit, but
|
|
// the user actually has more groups than that. This can
|
|
// happen because NT's limit is indeterminate.
|
|
//
|
|
|
|
// ignore the groups that we cannot return.
|
|
|
|
pGroups->GroupCount = args->NGroups;
|
|
}
|
|
|
|
//
|
|
// Make an array of gid_t's and copy it to the user address space.
|
|
//
|
|
|
|
GroupList = RtlAllocateHeap(PsxHeap, 0, pGroups->GroupCount *
|
|
sizeof(gid_t));
|
|
if (NULL == GroupList) {
|
|
RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
|
|
NtClose(TokenHandle);
|
|
m->Error = ENOMEM;
|
|
return TRUE;
|
|
}
|
|
|
|
for (i = 0, j = 0; i < pGroups->GroupCount; ++i) {
|
|
PSID Sid = pGroups->Groups[i].Sid;
|
|
GroupList[j] = MakePosixId(Sid);
|
|
if (0 != GroupList[j]) {
|
|
++j;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy GroupList to client's address space, at the place
|
|
// specified.
|
|
//
|
|
|
|
Status = NtWriteVirtualMemory(p->Process, args->GroupList,
|
|
GroupList, j * sizeof(gid_t), NULL);
|
|
|
|
RtlFreeHeap(PsxHeap, 0, (PVOID)GroupList);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
m->Error = PsxStatusToErrno(Status);
|
|
RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
|
|
NtClose(TokenHandle);
|
|
return TRUE;
|
|
}
|
|
|
|
m->ReturnValue = j;
|
|
RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
|
|
NtClose(TokenHandle);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
PsxGetLogin(
|
|
IN PPSX_PROCESS p,
|
|
IN PPSX_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements the getlogin() API.
|
|
|
|
Arguments:
|
|
|
|
p - Supplies the address of the calling process
|
|
|
|
m - Supplies the address of the related message
|
|
|
|
Return Value:
|
|
|
|
TRUE - Always succeeds and generates a reply
|
|
|
|
XXX.mjb: this routine is never called; getlogin() is implemented as
|
|
"getpwuid(getuid())->pw_name", kind of. See client-side getlogin().
|
|
|
|
--*/
|
|
|
|
{
|
|
PPSX_GETLOGIN_MSG args;
|
|
NTSTATUS Status;
|
|
|
|
args = &m->u.GetLogin;
|
|
args->LoginName; // w.r.t. our address space
|
|
|
|
m->Error = ENOSYS;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
PsxSysconf(
|
|
IN PPSX_PROCESS p,
|
|
IN PPSX_API_MSG m
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements the sysconf() api.
|
|
|
|
Arguments:
|
|
|
|
p - Supplies the address of the calling process.
|
|
|
|
m - Supplies the address of the related message.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Always succeeds and generates a reply.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPSX_SYSCONF_MSG args;
|
|
NTSTATUS Status;
|
|
long value;
|
|
PPSX_PROCESS Process;
|
|
|
|
args = &m->u.Sysconf;
|
|
|
|
switch (args->Name) {
|
|
case _SC_ARG_MAX:
|
|
value = ARG_MAX;
|
|
break;
|
|
case _SC_CHILD_MAX:
|
|
//
|
|
// This is the reason sysconf is implemented in the
|
|
// server. We don't bother to grab any locks, since the
|
|
// result is out of date by the time it gets back to the
|
|
// user anyway.
|
|
//
|
|
|
|
value = 1;
|
|
for (Process = FirstProcess; Process < LastProcess; ++Process) {
|
|
if (Process->Flags & P_FREE) {
|
|
++value;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _SC_CLK_TCK:
|
|
value = CLK_TCK;
|
|
break;
|
|
case _SC_NGROUPS_MAX:
|
|
value = NGROUPS_MAX;
|
|
break;
|
|
case _SC_OPEN_MAX:
|
|
value = OPEN_MAX;
|
|
break;
|
|
case _SC_JOB_CONTROL:
|
|
#ifdef _POSIX_JOB_CONTROL
|
|
value = 1;
|
|
break;
|
|
#else
|
|
value = 0;
|
|
break;
|
|
#endif
|
|
case _SC_SAVED_IDS:
|
|
#ifdef _POSIX_SAVED_IDS
|
|
value = 1;
|
|
#else
|
|
value = 0;
|
|
#endif
|
|
break;
|
|
case _SC_VERSION:
|
|
value = _POSIX_VERSION;
|
|
break;
|
|
case _SC_STREAM_MAX:
|
|
value = STREAM_MAX;
|
|
break;
|
|
case _SC_TZNAME_MAX:
|
|
value = TZNAME_MAX;
|
|
break;
|
|
default:
|
|
value = -1;
|
|
m->Error = EINVAL;
|
|
}
|
|
m->ReturnValue = value;
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef EXEC_FOREIGN
|
|
|
|
#define UNICODE
|
|
#include <windows.h>
|
|
|
|
DWORD ForeignProcessWait(PVOID);
|
|
|
|
NTSTATUS
|
|
ExecForeignImage(
|
|
PPSX_PROCESS p,
|
|
PPSX_API_MSG m,
|
|
PUNICODE_STRING Image,
|
|
PUNICODE_STRING CurDir
|
|
)
|
|
{
|
|
PPSX_EXEC_MSG args;
|
|
NTSTATUS Status;
|
|
LPWSTR CommandLine;
|
|
LPVOID Environment;
|
|
STARTUPINFO StartInfo;
|
|
PROCESS_INFORMATION ProcInfo;
|
|
BOOL Success = FALSE;
|
|
char **ppch;
|
|
PWCHAR pwc;
|
|
ULONG flags;
|
|
ULONG ThreadId;
|
|
HANDLE ForeignProc;
|
|
ULONG len;
|
|
|
|
args = &m->u.Exec;
|
|
|
|
//
|
|
// Convert argv array to command line format.
|
|
//
|
|
|
|
CommandLine = RtlAllocateHeap(PsxHeap, 0, ARG_MAX);
|
|
if (NULL == CommandLine) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
CommandLine[0] = 0;
|
|
|
|
ppch = (PVOID)args->Args;
|
|
|
|
for (ppch = (PVOID)args->Args; NULL != *ppch; ++ppch) {
|
|
ANSI_STRING A;
|
|
UNICODE_STRING U;
|
|
|
|
// this breaks on args with spaces in them
|
|
|
|
A.Buffer = *ppch + (ULONG)args->Args;
|
|
A.Length = A.MaximumLength = strlen(A.Buffer);
|
|
|
|
U.Buffer = &CommandLine[wcslen(CommandLine)];
|
|
U.Length = 0;
|
|
U.MaximumLength = ARG_MAX;
|
|
|
|
Status = RtlAnsiStringToUnicodeString(&U, &A, FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
wcscat((PVOID)CommandLine, L" ");
|
|
}
|
|
|
|
Status = RtlCreateEnvironment(FALSE, &Environment);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RtlFreeHeap(PsxHeap, 0, CommandLine);
|
|
return Status;
|
|
}
|
|
|
|
for (++ppch; NULL != *ppch; ++ppch) {
|
|
ANSI_STRING aName, aValue;
|
|
UNICODE_STRING nU, vU;
|
|
char *pch;
|
|
|
|
pch = strchr(*ppch + (ULONG)args->Args, '=');
|
|
ASSERT(NULL != pch);
|
|
*pch = '\0';
|
|
|
|
aName.Buffer = *ppch + (ULONG)args->Args;
|
|
aName.Length = strlen(aName.Buffer);
|
|
aName.MaximumLength = aName.Length;
|
|
|
|
aValue.Buffer = pch + 1;
|
|
aValue.Length = strlen(aValue.Buffer);
|
|
aValue.MaximumLength = aName.Length;
|
|
|
|
if (0 == stricmp("PATH", aName.Buffer)) {
|
|
ConvertPathToWin(aValue.Buffer);
|
|
aValue.Length = strlen(aValue.Buffer);
|
|
}
|
|
|
|
*pch = '=';
|
|
|
|
Status = RtlAnsiStringToUnicodeString(&nU, &aName, TRUE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RtlFreeHeap(PsxHeap, 0, CommandLine);
|
|
RtlDestroyEnvironment(Environment);
|
|
return Status;
|
|
}
|
|
|
|
Status = RtlAnsiStringToUnicodeString(&vU, &aValue, TRUE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RtlFreeHeap(PsxHeap, 0, nU.Buffer);
|
|
RtlFreeHeap(PsxHeap, 0, CommandLine);
|
|
RtlDestroyEnvironment(Environment);
|
|
return Status;
|
|
}
|
|
|
|
Status = RtlSetEnvironmentVariable_U(&Environment, &nU, &vU);
|
|
RtlFreeHeap(PsxHeap, 0, nU.Buffer);
|
|
RtlFreeHeap(PsxHeap, 0, vU.Buffer);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("PSXSS: RtlSetEnvVar: 0x%x\n", Status));
|
|
RtlFreeHeap(PsxHeap, 0, CommandLine);
|
|
RtlDestroyEnvironment(Environment);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
// Make Image into correct format: "X:\path", return to original
|
|
// after we're done.
|
|
|
|
pwc = Image->Buffer;
|
|
PSX_GET_SIZEOF(DOSDEVICE_W,len);
|
|
Image->Buffer += (len - 2)/2;
|
|
Image->Length -= (len - 2);
|
|
|
|
flags = CREATE_SUSPENDED|CREATE_NEW_CONSOLE|
|
|
CREATE_NEW_PROCESS_GROUP|CREATE_UNICODE_ENVIRONMENT;
|
|
|
|
// set up StartInfo
|
|
|
|
StartInfo.cb = sizeof(STARTUPINFO);
|
|
StartInfo.lpReserved = 0;
|
|
StartInfo.lpDesktop = NULL;
|
|
StartInfo.lpTitle = NULL;
|
|
StartInfo.dwX = StartInfo.dwY = 0;
|
|
StartInfo.dwXSize = StartInfo.dwYSize = 400;
|
|
StartInfo.dwFlags = 0;
|
|
StartInfo.wShowWindow = SW_SHOWDEFAULT;
|
|
StartInfo.cbReserved2 = 0;
|
|
StartInfo.lpReserved2 = NULL;
|
|
|
|
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Success = CreateProcess(
|
|
Image->Buffer, // address of module name
|
|
CommandLine, // command line
|
|
NULL, // process security attr
|
|
NULL, // thread security attr
|
|
FALSE, // inherit handles
|
|
flags, // creation flags
|
|
Environment, // address of new environ
|
|
CurDir->Buffer, // current working dir
|
|
&StartInfo, // startup info
|
|
&ProcInfo // process information
|
|
);
|
|
|
|
EndImpersonation();
|
|
}
|
|
|
|
// restore image name
|
|
Image->Buffer = pwc;
|
|
|
|
(void)RtlDestroyEnvironment(Environment);
|
|
// ASSERT(NT_SUCCESS(Status));
|
|
|
|
RtlFreeHeap(PsxHeap, 0, CommandLine);
|
|
|
|
if (!Success) {
|
|
KdPrint(("PSXSS: CreateProcess: %d\n", GetLastError()));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
p->Thread = ProcInfo.hThread;
|
|
p->Process = ProcInfo.hProcess;
|
|
|
|
// set bit in the process to indicate it's a foreign
|
|
// image type
|
|
|
|
p->Flags |= P_FOREIGN_EXEC;
|
|
|
|
// create additional thread in psxss to wait for new
|
|
// process to exit.
|
|
|
|
p->BlockingThread = NULL;
|
|
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
p->BlockingThread = CreateThread(
|
|
NULL,
|
|
0,
|
|
ForeignProcessWait,
|
|
(PVOID)p,
|
|
CREATE_SUSPENDED,
|
|
&ThreadId
|
|
);
|
|
EndImpersonation();
|
|
|
|
if (NULL == p->BlockingThread) {
|
|
//XXX.mjb: clean process
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Success = ResumeThread(p->BlockingThread);
|
|
ASSERT(Success);
|
|
Success = ResumeThread(p->Thread);
|
|
ASSERT(Success);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ForeignProcessWait(PVOID arg)
|
|
{
|
|
PPSX_PROCESS p = arg;
|
|
ULONG ExitStatus;
|
|
|
|
WaitForSingleObject(p->Process, (DWORD)-1);
|
|
|
|
Exit(p, ExitStatus);
|
|
|
|
p->BlockingThread = NULL;
|
|
|
|
ExitThread(0);
|
|
//NOTREACHED
|
|
ASSERT(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Change a posix-type path variable to win32 path. The given
|
|
// buffer is modified in place.
|
|
//
|
|
VOID
|
|
ConvertPathToWin(char *path)
|
|
{
|
|
char *pch;
|
|
|
|
pch = path;
|
|
|
|
while (*path) {
|
|
|
|
// change ':' to ';'
|
|
if (':' == *path) {
|
|
*pch = ';';
|
|
++path;
|
|
++pch;
|
|
continue;
|
|
}
|
|
|
|
// change "//X" to "X:"
|
|
if ('/' == *path && '/' == path[1]) {
|
|
path += 2;
|
|
*pch = *path;
|
|
++pch;
|
|
*pch = ':';
|
|
|
|
++path;
|
|
++pch;
|
|
continue;
|
|
}
|
|
// change slash to backslash
|
|
if ('/' == *path) {
|
|
*pch = '\\';
|
|
++path;
|
|
++pch;
|
|
continue;
|
|
}
|
|
|
|
*pch = *path;
|
|
++pch;
|
|
++path;
|
|
}
|
|
*pch = '\0';
|
|
}
|
|
|
|
#endif /* EXEC_FOREIGN */
|