https://git.reactos.org/?p=reactos.git;a=commitdiff;h=ffbd6995e6e4fda35ab02…
commit ffbd6995e6e4fda35ab02942c49f6c42e5ca9c83
Author: Pierre Schweitzer <pierre(a)reactos.org>
AuthorDate: Fri May 25 00:18:20 2018 +0200
Commit: Pierre Schweitzer <pierre(a)reactos.org>
CommitDate: Fri May 25 08:48:51 2018 +0200
[NTOSKRNL] Implement NtQueryInformationJobObject().
This is required by some Python2 applications
such as pip.exe
---
ntoskrnl/ps/job.c | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 262 insertions(+), 3 deletions(-)
diff --git a/ntoskrnl/ps/job.c b/ntoskrnl/ps/job.c
index d6f2ba8fc1..7b242c6d60 100644
--- a/ntoskrnl/ps/job.c
+++ b/ntoskrnl/ps/job.c
@@ -5,6 +5,7 @@
* PURPOSE: Job Native Functions
* PROGRAMMERS: Alex Ionescu (alex(a)relsoft.net) (stubs)
* Thomas Weidenmueller <w3seek(a)reactos.com>
+ * Pierre Schweitzer (pierre(a)reactos.org)
*/
/* INCLUDES *****************************************************************/
@@ -278,6 +279,8 @@ NtCreateJobObject (
/* inherit the session id from the caller */
Job->SessionId = PsGetProcessSessionId(CurrentProcess);
+ KeInitializeGuardedMutex(&Job->MemoryLimitsLock);
+
Status = ExInitializeResource(&Job->JobLock);
if(!NT_SUCCESS(Status))
{
@@ -444,8 +447,15 @@ NtOpenJobObject (
}
+ULONG PspJobInfoLengths[] = { 0x0, sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION),
+ sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION),
+ 0x0C, 0x4, 0x14, 0x4, 0x8,
+
sizeof(JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION),
+ sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), 0x4 };
+ULONG PspJobInfoAlign[] = { 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4 };
+
/*
- * @unimplemented
+ * @implemented
*/
NTSTATUS
NTAPI
@@ -456,8 +466,257 @@ NtQueryInformationJobObject (
ULONG JobInformationLength,
PULONG ReturnLength )
{
- UNIMPLEMENTED;
- return STATUS_NOT_IMPLEMENTED;
+ PEJOB Job;
+ NTSTATUS Status;
+ BOOLEAN NoOutput;
+ PVOID GenericCopy;
+ PLIST_ENTRY NextEntry;
+ PKTHREAD CurrentThread;
+ KPROCESSOR_MODE PreviousMode;
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION ExtendedLimit;
+ JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION BasicAndIo;
+ ULONG RequiredLength, RequiredAlign, SizeToCopy, NeededSize;
+
+ PAGED_CODE();
+
+ CurrentThread = KeGetCurrentThread();
+
+ /* Validate class */
+ if (JobInformationClass > JobObjectJobSetInformation || JobInformationClass <
JobObjectBasicAccountingInformation)
+ {
+ return STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* Get associated lengths & alignments */
+ RequiredLength = PspJobInfoLengths[JobInformationClass];
+ RequiredAlign = PspJobInfoAlign[JobInformationClass];
+ SizeToCopy = RequiredLength;
+ NeededSize = RequiredLength;
+
+ /* If length mismatch (needed versus provided) */
+ if (JobInformationLength != RequiredLength)
+ {
+ /* This can only be accepted if: JobObjectBasicProcessIdList or
JobObjectSecurityLimitInformation
+ * Or if size is bigger than needed
+ */
+ if ((JobInformationClass != JobObjectBasicProcessIdList &&
JobInformationClass != JobObjectSecurityLimitInformation) ||
+ JobInformationLength < RequiredLength)
+ {
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ /* Set what we need to copy out */
+ SizeToCopy = JobInformationLength;
+ }
+
+ PreviousMode = ExGetPreviousMode();
+ /* If not comming from umode, we need to probe buffers */
+ if (PreviousMode != KernelMode)
+ {
+ ASSERT(((RequiredAlign) == 1) || ((RequiredAlign) == 2) || ((RequiredAlign) == 4)
|| ((RequiredAlign) == 8) || ((RequiredAlign) == 16));
+
+ _SEH2_TRY
+ {
+ /* Probe out buffer for write */
+ if (JobInformation != NULL)
+ {
+ ProbeForWrite(JobInformation, JobInformationLength, RequiredAlign);
+ }
+
+ /* But also return lenght if asked */
+ if (ReturnLength != NULL)
+ {
+ ProbeForWrite(JobInformation, sizeof(ULONG), sizeof(ULONG));
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+ }
+
+ /* If a job handle was provided, use it */
+ if (JobHandle != NULL)
+ {
+ Status = ObReferenceObjectByHandle(JobHandle,
+ JOB_OBJECT_QUERY,
+ PsJobType,
+ PreviousMode,
+ (PVOID*)&Job,
+ NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ return Status;
+ }
+ }
+ /* Otherwise, get our current process' job, if any */
+ else
+ {
+ PEPROCESS CurrentProcess;
+
+ CurrentProcess = (PEPROCESS)CurrentThread->ApcState.Process;
+ Job = CurrentProcess->Job;
+ if (Job == NULL)
+ {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ ObReferenceObject(Job);
+ }
+
+ /* By default, assume we'll have to copy data */
+ NoOutput = FALSE;
+ /* Select class */
+ switch (JobInformationClass)
+ {
+ /* Basic counters */
+ case JobObjectBasicAccountingInformation:
+ case JobObjectBasicAndIoAccountingInformation:
+ /* Zero basics */
+ RtlZeroMemory(&BasicAndIo.BasicInfo,
sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION));
+
+ /* Lock */
+ KeEnterGuardedRegionThread(CurrentThread);
+ ExAcquireResourceSharedLite(&Job->JobLock, TRUE);
+
+ /* Initialize with job counters */
+ BasicAndIo.BasicInfo.TotalUserTime.QuadPart =
Job->TotalUserTime.QuadPart;
+ BasicAndIo.BasicInfo.TotalKernelTime.QuadPart =
Job->TotalKernelTime.QuadPart;
+ BasicAndIo.BasicInfo.ThisPeriodTotalUserTime.QuadPart =
Job->ThisPeriodTotalUserTime.QuadPart;
+ BasicAndIo.BasicInfo.ThisPeriodTotalKernelTime.QuadPart =
Job->ThisPeriodTotalKernelTime.QuadPart;
+ BasicAndIo.BasicInfo.TotalPageFaultCount = Job->TotalPageFaultCount;
+ BasicAndIo.BasicInfo.TotalProcesses = Job->TotalProcesses;
+ BasicAndIo.BasicInfo.ActiveProcesses = Job->ActiveProcesses;
+ BasicAndIo.BasicInfo.TotalTerminatedProcesses =
Job->TotalTerminatedProcesses;
+
+ /* We also set IoInfo, even though we might not return it */
+ BasicAndIo.IoInfo.ReadOperationCount = Job->ReadOperationCount;
+ BasicAndIo.IoInfo.WriteOperationCount = Job->WriteOperationCount;
+ BasicAndIo.IoInfo.OtherOperationCount = Job->OtherOperationCount;
+ BasicAndIo.IoInfo.ReadTransferCount = Job->ReadTransferCount;
+ BasicAndIo.IoInfo.WriteTransferCount = Job->WriteTransferCount;
+ BasicAndIo.IoInfo.OtherTransferCount = Job->OtherTransferCount;
+
+ /* For every process, sum its counters */
+ for (NextEntry = Job->ProcessListHead.Flink;
+ NextEntry != &Job->ProcessListHead;
+ NextEntry = NextEntry->Flink)
+ {
+ PEPROCESS Process;
+
+ Process = CONTAINING_RECORD(NextEntry, EPROCESS, JobLinks);
+ if (!BooleanFlagOn(Process->JobStatus, 2))
+ {
+ /* FIXME: Call KeQueryValuesProcess()
+ * We should sum BasicInfo values here,
+ * but we don't have them
+ */
+ BasicAndIo.IoInfo.ReadOperationCount +=
Process->ReadOperationCount.QuadPart;
+ BasicAndIo.IoInfo.WriteOperationCount +=
Process->WriteOperationCount.QuadPart;
+ BasicAndIo.IoInfo.OtherOperationCount +=
Process->OtherOperationCount.QuadPart;
+ BasicAndIo.IoInfo.ReadTransferCount +=
Process->ReadTransferCount.QuadPart;
+ BasicAndIo.IoInfo.WriteTransferCount +=
Process->WriteTransferCount.QuadPart;
+ BasicAndIo.IoInfo.OtherTransferCount +=
Process->OtherTransferCount.QuadPart;
+ }
+ }
+
+ /* And done */
+ ExReleaseResourceLite(&Job->JobLock);
+ KeLeaveGuardedRegionThread(CurrentThread);
+
+ /* We'll copy back the buffer */
+ GenericCopy = &BasicAndIo;
+ Status = STATUS_SUCCESS;
+
+ break;
+
+ /* Limits information */
+ case JobObjectBasicLimitInformation:
+ case JobObjectExtendedLimitInformation:
+ /* Lock */
+ KeEnterGuardedRegionThread(CurrentThread);
+ ExAcquireResourceSharedLite(&Job->JobLock, TRUE);
+
+ /* Copy basic information */
+ ExtendedLimit.BasicLimitInformation.LimitFlags = Job->LimitFlags;
+ ExtendedLimit.BasicLimitInformation.MinimumWorkingSetSize =
Job->MinimumWorkingSetSize;
+ ExtendedLimit.BasicLimitInformation.MaximumWorkingSetSize =
Job->MaximumWorkingSetSize;
+ ExtendedLimit.BasicLimitInformation.ActiveProcessLimit =
Job->ActiveProcessLimit;
+ ExtendedLimit.BasicLimitInformation.PriorityClass = Job->PriorityClass;
+ ExtendedLimit.BasicLimitInformation.SchedulingClass =
Job->SchedulingClass;
+ ExtendedLimit.BasicLimitInformation.Affinity = Job->Affinity;
+ ExtendedLimit.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart =
Job->PerProcessUserTimeLimit.QuadPart;
+ ExtendedLimit.BasicLimitInformation.PerJobUserTimeLimit.QuadPart =
Job->PerJobUserTimeLimit.QuadPart;
+
+ /* If asking for extending limits */
+ if (JobInformationClass == JobObjectExtendedLimitInformation)
+ {
+ /* Lock our memory lock */
+ KeAcquireGuardedMutexUnsafe(&Job->MemoryLimitsLock);
+ /* Return limits */
+ ExtendedLimit.ProcessMemoryLimit = Job->ProcessMemoryLimit <<
PAGE_SHIFT;
+ ExtendedLimit.JobMemoryLimit = Job->JobMemoryLimit <<
PAGE_SHIFT;
+ ExtendedLimit.PeakProcessMemoryUsed = Job->PeakProcessMemoryUsed
<< PAGE_SHIFT;
+ ExtendedLimit.PeakJobMemoryUsed = Job->PeakJobMemoryUsed <<
PAGE_SHIFT;
+ KeReleaseGuardedMutexUnsafe(&Job->MemoryLimitsLock);
+
+ /* And done */
+ ExReleaseResourceLite(&Job->JobLock);
+ KeLeaveGuardedRegionThread(CurrentThread);
+
+ /* We'll never return IoInfo, so zero it out to avoid
+ * kernel memory leak
+ */
+ RtlZeroMemory(&ExtendedLimit.IoInfo, sizeof(IO_COUNTERS));
+ }
+ else
+ {
+ /* And done */
+ ExReleaseResourceLite(&Job->JobLock);
+ KeLeaveGuardedRegionThread(CurrentThread);
+ }
+
+ /* We'll copy back the buffer */
+ GenericCopy = &ExtendedLimit;
+ Status = STATUS_SUCCESS;
+
+ break;
+
+ default:
+ DPRINT1("Class %d not implemented\n", JobInformationClass);
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ /* Job is no longer required */
+ ObDereferenceObject(Job);
+
+ /* If we succeeed, copy back data */
+ if (NT_SUCCESS(Status))
+ {
+ _SEH2_TRY
+ {
+ /* If we have anything to copy, do it */
+ if (!NoOutput)
+ {
+ RtlCopyMemory(JobInformation, GenericCopy, SizeToCopy);
+ }
+
+ /* And return length if asked */
+ if (ReturnLength != NULL)
+ {
+ *ReturnLength = NeededSize;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+ }
+
+ return Status;
}