mirror of
https://github.com/reactos/reactos.git
synced 2025-08-03 13:26:17 +00:00
[NTOSKRNL] Implement NtQueryInformationJobObject().
This is required by some Python2 applications such as pip.exe
This commit is contained in:
parent
fb9ffed1cd
commit
ffbd6995e6
1 changed files with 262 additions and 3 deletions
|
@ -5,6 +5,7 @@
|
||||||
* PURPOSE: Job Native Functions
|
* PURPOSE: Job Native Functions
|
||||||
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net) (stubs)
|
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net) (stubs)
|
||||||
* Thomas Weidenmueller <w3seek@reactos.com>
|
* Thomas Weidenmueller <w3seek@reactos.com>
|
||||||
|
* Pierre Schweitzer (pierre@reactos.org)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* INCLUDES *****************************************************************/
|
/* INCLUDES *****************************************************************/
|
||||||
|
@ -278,6 +279,8 @@ NtCreateJobObject (
|
||||||
/* inherit the session id from the caller */
|
/* inherit the session id from the caller */
|
||||||
Job->SessionId = PsGetProcessSessionId(CurrentProcess);
|
Job->SessionId = PsGetProcessSessionId(CurrentProcess);
|
||||||
|
|
||||||
|
KeInitializeGuardedMutex(&Job->MemoryLimitsLock);
|
||||||
|
|
||||||
Status = ExInitializeResource(&Job->JobLock);
|
Status = ExInitializeResource(&Job->JobLock);
|
||||||
if(!NT_SUCCESS(Status))
|
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
|
NTSTATUS
|
||||||
NTAPI
|
NTAPI
|
||||||
|
@ -456,8 +466,257 @@ NtQueryInformationJobObject (
|
||||||
ULONG JobInformationLength,
|
ULONG JobInformationLength,
|
||||||
PULONG ReturnLength )
|
PULONG ReturnLength )
|
||||||
{
|
{
|
||||||
UNIMPLEMENTED;
|
PEJOB Job;
|
||||||
return STATUS_NOT_IMPLEMENTED;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue