Index: reactos/ntoskrnl/include/internal/ps.h =================================================================== --- reactos/ntoskrnl/include/internal/ps.h (revision 75928) +++ reactos/ntoskrnl/include/internal/ps.h (working copy) @@ -163,6 +163,13 @@ IN BOOLEAN InJob ); +NTSTATUS +NTAPI +PspAssignProcessToJob( + IN PEPROCESS Process, + IN PEJOB Job +); + // // Security Routines // Index: reactos/ntoskrnl/ps/job.c =================================================================== --- reactos/ntoskrnl/ps/job.c (revision 75928) +++ reactos/ntoskrnl/ps/job.c (working copy) @@ -5,12 +5,13 @@ * PURPOSE: Job Native Functions * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) (stubs) * Thomas Weidenmueller + * Samuel SerapiĆ³n Vega (encoded@reactos.org) */ /* INCLUDES *****************************************************************/ #include -#define NDEBUG +//#define NDEBUG #include @@ -53,18 +54,18 @@ VOID NTAPI -PspDeleteJob ( PVOID ObjectBody ) +PspDeleteJob(PVOID ObjectBody) { PEJOB Job = (PEJOB)ObjectBody; /* remove the reference to the completion port if associated */ - if(Job->CompletionPort != NULL) + if (Job->CompletionPort != NULL) { ObDereferenceObject(Job->CompletionPort); } /* unlink the job object */ - if(Job->JobLinks.Flink != NULL) + if (!IsListEmpty(&Job->JobLinks)) { ExAcquireFastMutex(&PsJobListLock); RemoveEntryList(&Job->JobLinks); @@ -86,20 +87,91 @@ NTSTATUS NTAPI PspAssignProcessToJob(PEPROCESS Process, - PEJOB Job) + PEJOB Job) { - DPRINT("PspAssignProcessToJob() is unimplemented!\n"); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status = STATUS_SUCCESS; + DPRINT("PspAssignProcessToJob(%p,%p)\n", Process, Job); + + /* Grab a reference for the lifetime of the process, released in ps/kill.c */ + ObReferenceObject(Job); + + /* Make sure we are not interrupted */ + ExEnterCriticalRegionAndAcquireResourceExclusive(&Job->JobLock); + + /* Assign process to job object */ + InsertTailList(&Job->ProcessListHead, &Process->JobLinks); + + /* Increment counters */ + Job->TotalProcesses++; + Job->ActiveProcesses++; + + if (Job->CompletionPort && Process->UniqueProcessId) + { + /* notify the job object of the new proccess */ + Status = IoSetIoCompletion(Job->CompletionPort, + Job->CompletionKey, + (PVOID)Process->UniqueProcessId, + STATUS_SUCCESS, + JOB_OBJECT_MSG_NEW_PROCESS, + FALSE); + } + + /* FIXME: limit flags not supported */ + if (Job->LimitFlags) + { + DPRINT1("Unhandled limit flags %x\n", Job->LimitFlags); + } + + /* resume APCs and release lock */ + ExReleaseResourceAndLeaveCriticalRegion(&Job->JobLock); + + return Status; } NTSTATUS NTAPI PspTerminateJobObject(PEJOB Job, - KPROCESSOR_MODE AccessMode, - NTSTATUS ExitStatus ) + KPROCESSOR_MODE AccessMode, + NTSTATUS ExitStatus) { - DPRINT("PspTerminateJobObject() is unimplemented!\n"); - return STATUS_NOT_IMPLEMENTED; + PEPROCESS Process; + + DPRINT("PspTerminateJobObject(%p,%x,%x)\n", Job, AccessMode, ExitStatus); + + ExEnterCriticalRegionAndAcquireResourceExclusive(&Job->JobLock); + + /* for each process in job process list */ + while (!IsListEmpty(&Job->ProcessListHead)) + { + /* fetch process object */ + Process = CONTAINING_RECORD(Job->ProcessListHead.Flink, EPROCESS, JobLinks); + ObReferenceObject(Process); + + /* process might be getting deleted already */ + if (ExAcquireRundownProtection(&Process->RundownProtect)) + { + /* terminate process */ + if (NT_SUCCESS(PsTerminateProcess(Process, ExitStatus))) + { + if (InterlockedCompareExchangePointer((PVOID)&Process->Job, NULL, Job) == Job) + { + RemoveEntryList(&Process->JobLinks); + Job->TotalTerminatedProcesses++; + ASSERT((Job->ActiveProcesses-1) < Job->ActiveProcesses); + Job->ActiveProcesses--; + } + } + ExReleaseRundownProtection(&Process->RundownProtect); + } + + /* say good bye and get next one */ + ObDereferenceObject(Process); + } + + /* resume APCs and release lock */ + ExReleaseResourceAndLeaveCriticalRegion(&Job->JobLock); + + return STATUS_SUCCESS; } VOID @@ -107,7 +179,21 @@ PspRemoveProcessFromJob(IN PEPROCESS Process, IN PEJOB Job) { - /* FIXME */ + /* This function is called as the Process is destroyed, see ps/kill.c */ + DPRINT("PspRemoveProcessFromJob(%p,%p)\n", Process, Job); + + ExEnterCriticalRegionAndAcquireResourceExclusive(&Job->JobLock); + + if (InterlockedCompareExchangePointer((PVOID)&Process->Job, NULL, Job) == Job) + { + RemoveEntryList(&Process->JobLinks); + } + else + { + DPRINT1("PspRemoveProcessFromJob(%p,%p) Process not in job\n", Process, Job); + } + + ExReleaseResourceAndLeaveCriticalRegion(&Job->JobLock); } VOID @@ -115,17 +201,53 @@ PspExitProcessFromJob(IN PEJOB Job, IN PEPROCESS Process) { - /* FIXME */ + /* This function is called when the last thread exits, see ps/kill.c */ + DPRINT("PspExitProcessFromJob(0x%p, 0x%p)\n", Job, Process); + + /* Make sure we are not interrupted */ + ExEnterCriticalRegionAndAcquireResourceExclusive(&Job->JobLock); + + /* Decrement counters */ + if (Process->Job == Job) + { + ASSERT((Job->ActiveProcesses-1) < Job->ActiveProcesses); + Job->ActiveProcesses--; + + if (Job->CompletionPort) + { + /* Notify Job of process exit */ + IoSetIoCompletion(Job->CompletionPort, + Job->CompletionKey, + NULL, + STATUS_SUCCESS, + JOB_OBJECT_MSG_EXIT_PROCESS, + FALSE); + } + + if (Job->ActiveProcesses == 0 && Job->CompletionPort) + { + /* Notify Job that all process have exited */ + IoSetIoCompletion(Job->CompletionPort, + Job->CompletionKey, + NULL, + STATUS_SUCCESS, + JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO, + FALSE); + } + } + /* FIXME: Update limits and process stats */ + + /* resume APCs and release lock */ + ExReleaseResourceAndLeaveCriticalRegion(&Job->JobLock); } /* - * @unimplemented + * @implemented */ NTSTATUS NTAPI -NtAssignProcessToJobObject ( - HANDLE JobHandle, - HANDLE ProcessHandle) +NtAssignProcessToJobObject(HANDLE JobHandle, + HANDLE ProcessHandle) { PEPROCESS Process; KPROCESSOR_MODE PreviousMode; @@ -132,6 +254,7 @@ NTSTATUS Status; PAGED_CODE(); + DPRINT("NtAssignProcessToJobObject(%x,%x)\n", JobHandle, ProcessHandle); PreviousMode = ExGetPreviousMode(); @@ -141,66 +264,51 @@ I open the process handle before the job handle is that a simple test showed that it first complains about a invalid process handle! The other way around would be simpler though... */ - Status = ObReferenceObjectByHandle( - ProcessHandle, - PROCESS_TERMINATE, - PsProcessType, - PreviousMode, - (PVOID*)&Process, - NULL); - if(NT_SUCCESS(Status)) + Status = ObReferenceObjectByHandle(ProcessHandle, + PROCESS_TERMINATE, + PsProcessType, + PreviousMode, + (PVOID*)&Process, + NULL); + if (NT_SUCCESS(Status)) { - if(Process->Job == NULL) + PEJOB Job; + Status = ObReferenceObjectByHandle(JobHandle, + JOB_OBJECT_ASSIGN_PROCESS, + PsJobType, + PreviousMode, + (PVOID*)&Job, + NULL); + if (NT_SUCCESS(Status)) { - PEJOB Job; - - Status = ObReferenceObjectByHandle( - JobHandle, - JOB_OBJECT_ASSIGN_PROCESS, - PsJobType, - PreviousMode, - (PVOID*)&Job, - NULL); - if(NT_SUCCESS(Status)) + if (ExAcquireRundownProtection(&Process->RundownProtect)) { - /* lock the process so we can safely assign the process. Note that in the - meanwhile another thread could have assigned this process to a job! */ - - if(ExAcquireRundownProtection(&Process->RundownProtect)) + if (Process->Job == NULL && PsGetProcessSessionId(Process) == Job->SessionId) { - if(Process->Job == NULL && PsGetProcessSessionId(Process) == Job->SessionId) + /* Change the pointer */ + if (InterlockedCompareExchangePointer((PVOID)&Process->Job, Job, NULL)) { - /* Just store the pointer to the job object in the process, we'll - assign it later. The reason we can't do this here is that locking - the job object might require it to wait, which is a bad thing - while holding the process lock! */ - Process->Job = Job; - ObReferenceObject(Job); - } - else - { - /* process is already assigned to a job or session id differs! */ + /* we already had one set, fail */ Status = STATUS_ACCESS_DENIED; } - ExReleaseRundownProtection(&Process->RundownProtect); - - if(NT_SUCCESS(Status)) - { - /* let's actually assign the process to the job as we're not holding - the process lock anymore! */ - Status = PspAssignProcessToJob(Process, Job); - } } - - ObDereferenceObject(Job); + else + { + /* process is already assigned to a job or session id differs! */ + Status = STATUS_ACCESS_DENIED; + } + if (NT_SUCCESS(Status)) + { + Status = PspAssignProcessToJob(Process, Job); + } + ExReleaseRundownProtection(&Process->RundownProtect); } + else + { + Status = STATUS_PROCESS_IS_TERMINATING; + } + ObDereferenceObject(Job); } - else - { - /* process is already assigned to a job or session id differs! */ - Status = STATUS_ACCESS_DENIED; - } - ObDereferenceObject(Process); } @@ -213,7 +321,7 @@ IN PJOB_SET_ARRAY UserJobSet, IN ULONG Flags) { - UNIMPLEMENTED; + DPRINT("NtCreateJobSet(%u, %p, 0x%x)\n", NumJob, UserJobSet, Flags); return STATUS_NOT_IMPLEMENTED; } @@ -222,10 +330,9 @@ */ NTSTATUS NTAPI -NtCreateJobObject ( - PHANDLE JobHandle, - ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes ) +NtCreateJobObject(PHANDLE JobHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes) { HANDLE hJob; PEJOB Job; @@ -253,16 +360,16 @@ } Status = ObCreateObject(PreviousMode, - PsJobType, - ObjectAttributes, - PreviousMode, - NULL, - sizeof(EJOB), - 0, - 0, - (PVOID*)&Job); + PsJobType, + ObjectAttributes, + PreviousMode, + NULL, + sizeof(EJOB), + 0, + 0, + (PVOID*)&Job); - if(NT_SUCCESS(Status)) + if (NT_SUCCESS(Status)) { /* FIXME - Zero all fields as we don't yet implement all of them */ RtlZeroMemory(Job, sizeof(EJOB)); @@ -269,7 +376,7 @@ /* make sure that early destruction doesn't attempt to remove the object from the list before it even gets added! */ - Job->JobLinks.Flink = NULL; + InitializeListHead(&Job->JobLinks); /* setup the job object - FIXME: More to do! */ InitializeListHead(&Job->JobSetLinks); @@ -279,7 +386,7 @@ Job->SessionId = PsGetProcessSessionId(CurrentProcess); Status = ExInitializeResource(&Job->JobLock); - if(!NT_SUCCESS(Status)) + if (!NT_SUCCESS(Status)) { DPRINT1("Failed to initialize job lock!!!\n"); ObDereferenceObject(Job); @@ -293,12 +400,12 @@ ExReleaseFastMutex(&PsJobListLock); Status = ObInsertObject(Job, - NULL, - DesiredAccess, - 0, - NULL, - &hJob); - if(NT_SUCCESS(Status)) + NULL, + DesiredAccess, + 0, + NULL, + &hJob); + if (NT_SUCCESS(Status)) { /* pass the handle back to the caller */ _SEH2_TRY @@ -325,9 +432,8 @@ */ NTSTATUS NTAPI -NtIsProcessInJob ( - IN HANDLE ProcessHandle, - IN HANDLE JobHandle OPTIONAL ) +NtIsProcessInJob(IN HANDLE ProcessHandle, + IN HANDLE JobHandle OPTIONAL) { KPROCESSOR_MODE PreviousMode; PEPROCESS Process; @@ -337,23 +443,18 @@ PAGED_CODE(); - Status = ObReferenceObjectByHandle( - ProcessHandle, - PROCESS_QUERY_INFORMATION, - PsProcessType, - PreviousMode, - (PVOID*)&Process, - NULL); - if(NT_SUCCESS(Status)) + DPRINT("NtIsProcessInJob(%x, %x)\n", ProcessHandle, JobHandle); + Status = ObReferenceObjectByHandle(ProcessHandle, + PROCESS_QUERY_INFORMATION, + PsProcessType, + PreviousMode, + (PVOID*)&Process, + NULL); + if (NT_SUCCESS(Status)) { - /* FIXME - make sure the job object doesn't get exchanged or deleted while trying to - reference it, e.g. by locking it somehow until it is referenced... */ - - PEJOB ProcessJob = Process->Job; - - if(ProcessJob != NULL) + if (Process->Job != NULL) { - if(JobHandle == NULL) + if (JobHandle == NULL) { /* the process is assigned to a job */ Status = STATUS_PROCESS_IN_JOB; @@ -364,14 +465,14 @@ /* get the job object and compare the object pointer with the one assigned to the process */ Status = ObReferenceObjectByHandle(JobHandle, - JOB_OBJECT_QUERY, - PsJobType, - PreviousMode, - (PVOID*)&JobObject, - NULL); - if(NT_SUCCESS(Status)) + JOB_OBJECT_QUERY, + PsJobType, + PreviousMode, + (PVOID*)&JobObject, + NULL); + if (NT_SUCCESS(Status)) { - Status = ((ProcessJob == JobObject) ? STATUS_PROCESS_IN_JOB : STATUS_PROCESS_NOT_IN_JOB); + Status = ((Process->Job == JobObject) ? STATUS_PROCESS_IN_JOB : STATUS_PROCESS_NOT_IN_JOB); ObDereferenceObject(JobObject); } } @@ -393,10 +494,9 @@ */ NTSTATUS NTAPI -NtOpenJobObject ( - PHANDLE JobHandle, - ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes) +NtOpenJobObject(PHANDLE JobHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes) { KPROCESSOR_MODE PreviousMode; HANDLE hJob; @@ -421,13 +521,13 @@ } Status = ObOpenObjectByName(ObjectAttributes, - PsJobType, - PreviousMode, - NULL, - DesiredAccess, - NULL, - &hJob); - if(NT_SUCCESS(Status)) + PsJobType, + PreviousMode, + NULL, + DesiredAccess, + NULL, + &hJob); + if (NT_SUCCESS(Status)) { _SEH2_TRY { @@ -445,46 +545,328 @@ /* - * @unimplemented + * @implemented */ NTSTATUS NTAPI -NtQueryInformationJobObject ( - HANDLE JobHandle, - JOBOBJECTINFOCLASS JobInformationClass, - PVOID JobInformation, - ULONG JobInformationLength, - PULONG ReturnLength ) +NtQueryInformationJobObject(HANDLE JobHandle, + JOBOBJECTINFOCLASS JobInformationClass, + PVOID JobInformation, + ULONG JobInformationLength, + PULONG ReturnLength) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + PEJOB JobObject; + NTSTATUS Status; + ULONG Length; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + + PAGED_CODE(); + + DPRINT("NtQueryInformationJobObject(%x,%x,%p,%d)\n", JobHandle, JobInformationClass, JobInformation, JobInformationLength); + + /* Check for user-mode caller */ + if (PreviousMode != KernelMode) + { + /* Prepare to probe parameters */ + _SEH2_TRY + { + /* Probe the buffer */ + ProbeForWrite(JobInformation, + JobInformationLength, + sizeof(ULONG)); + + /* Probe the return length if required */ + if (ReturnLength) ProbeForWriteUlong(ReturnLength); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* Return the exception code */ + _SEH2_YIELD(return _SEH2_GetExceptionCode()); + } + _SEH2_END; + } + + /* Grab a reference to Job */ + Status = ObReferenceObjectByHandle(JobHandle, + JOB_OBJECT_QUERY, + PsJobType, + PreviousMode, + (PVOID*)&JobObject, + NULL); + + if (!NT_SUCCESS(Status)) + return Status; + + /* protect the job from changes while we query it */ + ExEnterCriticalRegionAndAcquireResourceExclusive(&JobObject->JobLock); + + _SEH2_TRY + { + switch (JobInformationClass) + { + case JobObjectBasicLimitInformation: + case JobObjectExtendedLimitInformation: + { + PJOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimit = (PJOBOBJECT_BASIC_LIMIT_INFORMATION)JobInformation; + PJOBOBJECT_EXTENDED_LIMIT_INFORMATION ExtendedLimit = (PJOBOBJECT_EXTENDED_LIMIT_INFORMATION)JobInformation; + + if (JobInformationClass == JobObjectBasicLimitInformation) + { + /* Set the length required and validate it */ + Length = sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION); + if (JobInformationLength != Length) + { + Status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + } + else if (JobInformationClass == JobObjectExtendedLimitInformation) + { + /* Set the length required and validate it */ + Length = sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION); + if (JobInformationLength != Length) + { + Status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + /* copy extended limit field */ + ExtendedLimit->ProcessMemoryLimit = JobObject->ProcessMemoryLimit; + ExtendedLimit->JobMemoryLimit = JobObject->JobMemoryLimit; + ExtendedLimit->PeakProcessMemoryUsed = JobObject->PeakProcessMemoryUsed; + ExtendedLimit->PeakJobMemoryUsed = JobObject->PeakJobMemoryUsed; + } + + /* Copy basic limit fields */ + BasicLimit->PerProcessUserTimeLimit = JobObject->PerProcessUserTimeLimit; + BasicLimit->PerJobUserTimeLimit = JobObject->PerJobUserTimeLimit; + BasicLimit->LimitFlags = JobObject->LimitFlags; + BasicLimit->MinimumWorkingSetSize = JobObject->MinimumWorkingSetSize; + BasicLimit->MaximumWorkingSetSize = JobObject->MaximumWorkingSetSize; + BasicLimit->ActiveProcessLimit = JobObject->ActiveProcessLimit; + BasicLimit->Affinity = JobObject->Affinity; + BasicLimit->PriorityClass = JobObject->PriorityClass; + BasicLimit->SchedulingClass = JobObject->SchedulingClass; + break; + } + case JobObjectBasicProcessIdList: + { + PJOBOBJECT_BASIC_PROCESS_ID_LIST ProcIdList = (PJOBOBJECT_BASIC_PROCESS_ID_LIST)JobInformation; + PULONG_PTR IdListArray = &ProcIdList->ProcessIdList[0]; + PLIST_ENTRY Next = JobObject->ProcessListHead.Flink; + ULONG ListLength = JobInformationLength - FIELD_OFFSET(JOBOBJECT_BASIC_PROCESS_ID_LIST, ProcessIdList); + + /* This info param needs enough space for the list */ + Length = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST); + if (JobInformationLength < Length) + { + /* minimum requeried is the structure */ + Status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + ProcIdList->NumberOfAssignedProcesses = JobObject->ActiveProcesses; + ProcIdList->NumberOfProcessIdsInList = 0; + + while (Next != &JobObject->ProcessListHead) + { + PEPROCESS Process = (PEPROCESS)CONTAINING_RECORD(Next, EPROCESS, JobLinks); + + /* does another ULONG_PTR fit in the structure? */ + if (ListLength >= sizeof(ULONG_PTR)) + { + /* check process is not shutting down */ + if (ExAcquireRundownProtection(&Process->RundownProtect)) + { + /* add process to list */ + *IdListArray++ = (ULONG_PTR)Process->UniqueProcessId; + ListLength -= sizeof(ULONG_PTR); + ProcIdList->NumberOfProcessIdsInList++; + + /* release rundown protection */ + ExReleaseRundownProtection(&Process->RundownProtect); + } + } + else + { + Status = STATUS_BUFFER_OVERFLOW; + Length = ListLength; + break; + } + Next = Next->Flink; + } + + /* we returned this ammount of bytes */ + Length = JobInformationLength - ListLength; + break; + } + default: + { + DPRINT1("Unhandled info class 0x%x\n", JobInformationClass); + Status = STATUS_NOT_IMPLEMENTED; + break; + } + } + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* Return the exception code */ + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + + /* resume APCs and release lock */ + ExReleaseResourceAndLeaveCriticalRegion(&JobObject->JobLock); + + /* Release reference to Job */ + ObDereferenceObject(JobObject); + + /* Protect write with SEH */ + _SEH2_TRY + { + /* Check if caller wanted return length */ + if ((ReturnLength) && (Length)) *ReturnLength = Length; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* Get exception code */ + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + + return Status; } /* - * @unimplemented + * @implemented */ NTSTATUS NTAPI -NtSetInformationJobObject ( - HANDLE JobHandle, - JOBOBJECTINFOCLASS JobInformationClass, - PVOID JobInformation, - ULONG JobInformationLength) +NtSetInformationJobObject(HANDLE JobHandle, + JOBOBJECTINFOCLASS JobInformationClass, + PVOID JobInformation, + ULONG JobInformationLength) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + PEJOB JobObject; + PVOID IoCompletionPtr; + KPROCESSOR_MODE PreviousMode; + + PAGED_CODE(); + PreviousMode = KeGetPreviousMode(); + + DPRINT("NtSetInformationJobObject(%x,%x,%p,%d)\n", JobHandle, JobInformationClass, JobInformation, JobInformationLength); + + /* Grab a reference to Job */ + Status = ObReferenceObjectByHandle(JobHandle, + JOB_OBJECT_SET_ATTRIBUTES, + PsJobType, + PreviousMode, + (PVOID*)&JobObject, + NULL); + + if (!NT_SUCCESS(Status)) return Status; + + ExEnterCriticalRegionAndAcquireResourceExclusive(&JobObject->JobLock); + + switch (JobInformationClass) + { + case JobObjectAssociateCompletionPortInformation: + { + JOBOBJECT_ASSOCIATE_COMPLETION_PORT CompletionPortInfo; + _SEH2_TRY + { + CompletionPortInfo = *(PJOBOBJECT_ASSOCIATE_COMPLETION_PORT)JobInformation; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* Get the exception code */ + Status = _SEH2_GetExceptionCode(); + _SEH2_YIELD(break); + } + _SEH2_END; + + if (JobInformationLength != sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT)) + { + Status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + if (!JobObject->CompletionPort && CompletionPortInfo.CompletionPort) + { + /* Get the new completion port object */ + Status = ObReferenceObjectByHandle(CompletionPortInfo.CompletionPort, + IO_COMPLETION_MODIFY_STATE, + IoCompletionType, + PreviousMode, + &IoCompletionPtr, + NULL); + if (NT_SUCCESS(Status)) + { + JobObject->CompletionPort = IoCompletionPtr; + JobObject->CompletionKey = CompletionPortInfo.CompletionKey; + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + break; + } + case JobObjectExtendedLimitInformation: + { + JOBOBJECT_EXTENDED_LIMIT_INFORMATION ExtendedLimit; + _SEH2_TRY + { + ExtendedLimit = *(PJOBOBJECT_EXTENDED_LIMIT_INFORMATION)JobInformation; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* Get the exception code */ + Status = _SEH2_GetExceptionCode(); + _SEH2_YIELD(break); + } + _SEH2_END; + + /* FIXME: Quota limits, Time Limits */ + + /* FIXME: No limit flags supported, should detect invalid flags and return failure */ + if (ExtendedLimit.BasicLimitInformation.LimitFlags) + { + Status = STATUS_INVALID_PARAMETER; + } + else + { + JobObject->LimitFlags = ExtendedLimit.BasicLimitInformation.LimitFlags; + } + break; + } + default: + { + DPRINT1("Unhandled info class 0x%x\n", JobInformationClass); + Status = STATUS_NOT_IMPLEMENTED; + break; + } + } + + /* resume APCs and release lock */ + ExReleaseResourceAndLeaveCriticalRegion(&JobObject->JobLock); + + /* Release reference to Job */ + ObDereferenceObject(JobObject); + + return Status; } /* - * @unimplemented + * @implemented */ NTSTATUS NTAPI -NtTerminateJobObject ( - HANDLE JobHandle, - NTSTATUS ExitStatus ) +NtTerminateJobObject(HANDLE JobHandle, + NTSTATUS ExitStatus) { KPROCESSOR_MODE PreviousMode; PEJOB Job; @@ -492,21 +874,18 @@ PAGED_CODE(); + DPRINT("NtTerminateJobObject(%x,%x)\n", JobHandle, ExitStatus); PreviousMode = ExGetPreviousMode(); - Status = ObReferenceObjectByHandle( - JobHandle, - JOB_OBJECT_TERMINATE, - PsJobType, - PreviousMode, - (PVOID*)&Job, - NULL); - if(NT_SUCCESS(Status)) + Status = ObReferenceObjectByHandle(JobHandle, + JOB_OBJECT_TERMINATE, + PsJobType, + PreviousMode, + (PVOID*)&Job, + NULL); + if (NT_SUCCESS(Status)) { - Status = PspTerminateJobObject( - Job, - PreviousMode, - ExitStatus); + Status = PspTerminateJobObject(Job, PreviousMode, ExitStatus); ObDereferenceObject(Job); } @@ -519,7 +898,7 @@ */ PVOID NTAPI -PsGetJobLock ( PEJOB Job ) +PsGetJobLock(PEJOB Job) { ASSERT(Job); return (PVOID)&Job->JobLock; @@ -531,7 +910,7 @@ */ ULONG NTAPI -PsGetJobSessionId ( PEJOB Job ) +PsGetJobSessionId(PEJOB Job) { ASSERT(Job); return Job->SessionId; @@ -543,7 +922,7 @@ */ ULONG NTAPI -PsGetJobUIRestrictionsClass ( PEJOB Job ) +PsGetJobUIRestrictionsClass(PEJOB Job) { ASSERT(Job); return Job->UIRestrictionsClass; @@ -556,7 +935,7 @@ VOID NTAPI PsSetJobUIRestrictionsClass(PEJOB Job, - ULONG UIRestrictionsClass) + ULONG UIRestrictionsClass) { ASSERT(Job); (void)InterlockedExchangeUL(&Job->UIRestrictionsClass, UIRestrictionsClass); Index: reactos/ntoskrnl/ps/kill.c =================================================================== --- reactos/ntoskrnl/ps/kill.c (revision 75928) +++ reactos/ntoskrnl/ps/kill.c (working copy) @@ -256,6 +256,7 @@ PspDeleteProcess(IN PVOID ObjectBody) { PEPROCESS Process = (PEPROCESS)ObjectBody; + PEJOB Job; KAPC_STATE ApcState; PAGED_CODE(); PSTRACE(PS_KILL_DEBUG, "ObjectBody: %p\n", ObjectBody); @@ -282,14 +283,14 @@ } /* Check if we have a job */ - if (Process->Job) + Job = Process->Job; + if (Job) { /* Remove the process from the job */ - PspRemoveProcessFromJob(Process, Process->Job); + PspRemoveProcessFromJob(Process, Job); /* Dereference it */ - ObDereferenceObject(Process->Job); - Process->Job = NULL; + ObDereferenceObject(Job); } /* Increase the stack count */ Index: reactos/ntoskrnl/ps/process.c =================================================================== --- reactos/ntoskrnl/ps/process.c (revision 75928) +++ reactos/ntoskrnl/ps/process.c (working copy) @@ -712,8 +712,10 @@ /* Check if the parent had a job */ if ((Parent) && (Parent->Job)) { - /* FIXME: We need to insert this process */ - DPRINT1("Jobs not yet supported\n"); + if (InterlockedCompareExchangePointer((PVOID)&Process->Job, Parent->Job, NULL) == NULL) + { + PspAssignProcessToJob(Process, Parent->Job); + } } /* Create PEB only for User-Mode Processes */ Index: reactos/sdk/include/ndk/pstypes.h =================================================================== --- reactos/sdk/include/ndk/pstypes.h (revision 75928) +++ reactos/sdk/include/ndk/pstypes.h (working copy) @@ -953,6 +953,62 @@ #ifndef NTOS_MODE_USER // +// Job message flags +// + +#define JOB_OBJECT_MSG_END_OF_JOB_TIME 1 +#define JOB_OBJECT_MSG_END_OF_PROCESS_TIME 2 +#define JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT 3 +#define JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO 4 +#define JOB_OBJECT_MSG_NEW_PROCESS 6 +#define JOB_OBJECT_MSG_EXIT_PROCESS 7 +#define JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS 8 +#define JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT 9 +#define JOB_OBJECT_MSG_JOB_MEMORY_LIMIT 10 +#define JOB_OBJECT_MSG_NOTIFICATION_LIMIT 11 +#define JOB_OBJECT_MSG_JOB_CYCLE_TIME_LIMIT 12 + +// +// Job Information Structures for NtQuery/SetInformationJobObject +// + +typedef struct _JOBOBJECT_BASIC_LIMIT_INFORMATION +{ + LARGE_INTEGER PerProcessUserTimeLimit; + LARGE_INTEGER PerJobUserTimeLimit; + ULONG LimitFlags; + SIZE_T MinimumWorkingSetSize; + SIZE_T MaximumWorkingSetSize; + ULONG ActiveProcessLimit; + ULONG_PTR Affinity; + ULONG PriorityClass; + ULONG SchedulingClass; +} JOBOBJECT_BASIC_LIMIT_INFORMATION, *PJOBOBJECT_BASIC_LIMIT_INFORMATION; + +typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION +{ + JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; + IO_COUNTERS IoInfo; + SIZE_T ProcessMemoryLimit; + SIZE_T JobMemoryLimit; + SIZE_T PeakProcessMemoryUsed; + SIZE_T PeakJobMemoryUsed; +} JOBOBJECT_EXTENDED_LIMIT_INFORMATION, *PJOBOBJECT_EXTENDED_LIMIT_INFORMATION; + +typedef struct _JOBOBJECT_BASIC_PROCESS_ID_LIST +{ + ULONG NumberOfAssignedProcesses; + ULONG NumberOfProcessIdsInList; + ULONG_PTR ProcessIdList[1]; +} JOBOBJECT_BASIC_PROCESS_ID_LIST, *PJOBOBJECT_BASIC_PROCESS_ID_LIST; + +typedef struct _JOBOBJECT_ASSOCIATE_COMPLETION_PORT +{ + PVOID CompletionKey; + HANDLE CompletionPort; +} JOBOBJECT_ASSOCIATE_COMPLETION_PORT, *PJOBOBJECT_ASSOCIATE_COMPLETION_PORT; + +// // Job Set Array // typedef struct _JOB_SET_ARRAY