[REACTX] Fix 64 bit issues
[reactos.git] / ntoskrnl / ps / job.c
index 5cd3a8a..fd0049c 100644 (file)
@@ -5,6 +5,7 @@
  * PURPOSE:         Job Native Functions
  * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net) (stubs)
  *                  Thomas Weidenmueller <w3seek@reactos.com>
+ *                  Pierre Schweitzer (pierre@reactos.org)
  */
 
 /* INCLUDES *****************************************************************/
@@ -49,6 +50,36 @@ GENERIC_MAPPING PspJobMapping =
     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
@@ -166,8 +197,7 @@ NtAssignProcessToJobObject (
                 /* 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)
                     {
@@ -176,6 +206,7 @@ NtAssignProcessToJobObject (
                         the job object might require it to wait, which is a bad thing
                         while holding the process lock! */
                         Process->Job = Job;
+                        ObReferenceObject(Job);
                     }
                     else
                     {
@@ -278,6 +309,8 @@ NtCreateJobObject (
         /* inherit the session id from the caller */
         Job->SessionId = PsGetProcessSessionId(CurrentProcess);
 
+        KeInitializeGuardedMutex(&Job->MemoryLimitsLock);
+
         Status = ExInitializeResource(&Job->JobLock);
         if(!NT_SUCCESS(Status))
         {
@@ -445,7 +478,7 @@ NtOpenJobObject (
 
 
 /*
- * @unimplemented
+ * @implemented
  */
 NTSTATUS
 NTAPI
@@ -456,8 +489,258 @@ 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))
+                {
+                    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;
 }
 
 
@@ -472,8 +755,90 @@ NtSetInformationJobObject (
     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;
 }