[REACTX] Fix 64 bit issues
[reactos.git] / ntoskrnl / ps / job.c
index dc279bc..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,17 +197,16 @@ 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))
                 {
-                     // FIXME: This is broken
-                    if(Process->Job == NULL && PtrToUlong(Process->Session) == Job->SessionId)
+                    if(Process->Job == NULL && PsGetProcessSessionId(Process) == Job->SessionId)
                     {
                         /* 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
                     {
@@ -272,9 +302,14 @@ NtCreateJobObject (
         the list before it even gets added! */
         Job->JobLinks.Flink = NULL;
 
-        /* setup the job object */
+        /* setup the job object - FIXME: More to do! */
+        InitializeListHead(&Job->JobSetLinks);
         InitializeListHead(&Job->ProcessListHead);
-        Job->SessionId = PtrToUlong(CurrentProcess->Session); /* inherit the session id from the caller, FIXME: broken */
+
+        /* inherit the session id from the caller */
+        Job->SessionId = PsGetProcessSessionId(CurrentProcess);
+
+        KeInitializeGuardedMutex(&Job->MemoryLimitsLock);
 
         Status = ExInitializeResource(&Job->JobLock);
         if(!NT_SUCCESS(Status))
@@ -443,7 +478,7 @@ NtOpenJobObject (
 
 
 /*
- * @unimplemented
+ * @implemented
  */
 NTSTATUS
 NTAPI
@@ -454,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;
 }
 
 
@@ -470,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;
 }
 
 
@@ -527,12 +894,12 @@ PsGetJobLock ( PEJOB Job )
 /*
  * @implemented
  */
-PVOID
+ULONG
 NTAPI
 PsGetJobSessionId ( PEJOB Job )
 {
     ASSERT(Job);
-    return (PVOID)Job->SessionId;
+    return Job->SessionId;
 }