* PURPOSE: Job Native Functions
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net) (stubs)
* Thomas Weidenmueller <w3seek@reactos.com>
+ * Pierre Schweitzer (pierre@reactos.org)
*/
/* INCLUDES *****************************************************************/
STANDARD_RIGHTS_ALL | THREAD_ALL_ACCESS // bug fixed only in vista
};
+ULONG PspJobInfoLengths[] =
+{
+ 0x0,
+ sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION),
+ sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION),
+ sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST),
+ sizeof(JOBOBJECT_BASIC_UI_RESTRICTIONS),
+ sizeof(JOBOBJECT_SECURITY_LIMIT_INFORMATION),
+ sizeof(JOBOBJECT_END_OF_JOB_TIME_INFORMATION),
+ sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT),
+ sizeof(JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION),
+ sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION),
+ 0x4
+};
+
+ULONG PspJobInfoAlign[] =
+{
+ 0x0,
+ sizeof(ULONG),
+ sizeof(ULONG),
+ sizeof(ULONG),
+ sizeof(ULONG),
+ sizeof(ULONG),
+ sizeof(ULONG),
+ sizeof(ULONG),
+ sizeof(ULONG),
+ sizeof(ULONG),
+ sizeof(ULONG)
+};
+
/* FUNCTIONS *****************************************************************/
VOID
/* 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! */
- ExAcquireRundownProtection(&Process->RundownProtect);
- if(NT_SUCCESS(Status))
+ if(ExAcquireRundownProtection(&Process->RundownProtect))
{
if(Process->Job == NULL && PsGetProcessSessionId(Process) == Job->SessionId)
{
the job object might require it to wait, which is a bad thing
while holding the process lock! */
Process->Job = Job;
+ ObReferenceObject(Job);
}
else
{
/* inherit the session id from the caller */
Job->SessionId = PsGetProcessSessionId(CurrentProcess);
+ KeInitializeGuardedMutex(&Job->MemoryLimitsLock);
+
Status = ExInitializeResource(&Job->JobLock);
if(!NT_SUCCESS(Status))
{
/*
- * @unimplemented
+ * @implemented
*/
NTSTATUS
NTAPI
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))
+ {
+ PROCESS_VALUES Values;
+
+ KeQueryValuesProcess(&Process->Pcb, &Values);
+ BasicAndIo.BasicInfo.TotalUserTime.QuadPart += Values.TotalUserTime.QuadPart;
+ BasicAndIo.BasicInfo.TotalKernelTime.QuadPart += Values.TotalKernelTime.QuadPart;
+ BasicAndIo.IoInfo.ReadOperationCount += Values.IoInfo.ReadOperationCount;
+ BasicAndIo.IoInfo.WriteOperationCount += Values.IoInfo.WriteOperationCount;
+ BasicAndIo.IoInfo.OtherOperationCount += Values.IoInfo.OtherOperationCount;
+ BasicAndIo.IoInfo.ReadTransferCount += Values.IoInfo.ReadTransferCount;
+ BasicAndIo.IoInfo.WriteTransferCount += Values.IoInfo.WriteTransferCount;
+ BasicAndIo.IoInfo.OtherTransferCount += Values.IoInfo.OtherTransferCount;
+ }
+ }
+
+ /* 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;
}
PVOID JobInformation,
ULONG JobInformationLength)
{
- UNIMPLEMENTED;
- return STATUS_NOT_IMPLEMENTED;
+ PEJOB Job;
+ NTSTATUS Status;
+ PKTHREAD CurrentThread;
+ ACCESS_MASK DesiredAccess;
+ KPROCESSOR_MODE PreviousMode;
+ ULONG RequiredLength, RequiredAlign;
+
+ 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];
+
+ 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 read */
+ if (JobInformationLength != 0)
+ {
+ ProbeForRead(JobInformation, JobInformationLength, RequiredAlign);
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+ }
+
+ /* Validate input size */
+ if (JobInformationLength != RequiredLength)
+ {
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ /* Open the given job */
+ DesiredAccess = JOB_OBJECT_SET_ATTRIBUTES;
+ if (JobInformationClass == JobObjectSecurityLimitInformation)
+ {
+ DesiredAccess |= JOB_OBJECT_SET_SECURITY_ATTRIBUTES;
+ }
+ Status = ObReferenceObjectByHandle(JobHandle,
+ DesiredAccess,
+ PsJobType,
+ PreviousMode,
+ (PVOID*)&Job,
+ NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ return Status;
+ }
+
+ /* And set the information */
+ KeEnterGuardedRegionThread(CurrentThread);
+ switch (JobInformationClass)
+ {
+ case JobObjectExtendedLimitInformation:
+ DPRINT1("Class JobObjectExtendedLimitInformation not implemented\n");
+ Status = STATUS_SUCCESS;
+ break;
+
+ default:
+ DPRINT1("Class %d not implemented\n", JobInformationClass);
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+ KeLeaveGuardedRegionThread(CurrentThread);
+
+ ObfDereferenceObject(Job);
+
+ return Status;
}