partial implementation of NtAssignProcessToJobObject() and NtCreateJobObject()
Modified: trunk/reactos/ntoskrnl/ps/job.c

Modified: trunk/reactos/ntoskrnl/ps/job.c
--- trunk/reactos/ntoskrnl/ps/job.c	2005-01-21 13:25:28 UTC (rev 13183)
+++ trunk/reactos/ntoskrnl/ps/job.c	2005-01-21 14:12:03 UTC (rev 13184)
@@ -19,7 +19,7 @@
 POBJECT_TYPE EXPORTED PsJobType = NULL;
 
 LIST_ENTRY PsJobListHead;
-static KSPIN_LOCK PsJobListLock;
+static FAST_MUTEX PsJobListLock;
 
 static GENERIC_MAPPING PiJobMapping = {STANDARD_RIGHTS_READ | JOB_OBJECT_QUERY,
                                        STANDARD_RIGHTS_WRITE | JOB_OBJECT_ASSIGN_PROCESS | JOB_OBJECT_SET_ATTRIBUTES | JOB_OBJECT_TERMINATE | JOB_OBJECT_SET_SECURITY_ATTRIBUTES,
@@ -31,12 +31,23 @@
 VOID STDCALL
 PiDeleteJob(PVOID ObjectBody)
 {
-  KIRQL oldIrql;
   PEJOB Job = (PEJOB)ObjectBody;
   
-  KeAcquireSpinLock(&PsJobListLock, &oldIrql);
-  RemoveEntryList(&Job->JobLinks);
-  KeReleaseSpinLock(&PsJobListLock, oldIrql);
+  /* remove the reference to the completion port if associated */
+  if(Job->CompletionPort != NULL)
+  {
+    ObDereferenceObject(Job->CompletionPort);
+  }
+
+  /* unlink the job object */
+  if(Job->JobLinks.Flink != NULL)
+  {
+    ExAcquireFastMutex(&PsJobListLock);
+    RemoveEntryList(&Job->JobLinks);
+    ExReleaseFastMutex(&PsJobListLock);
+  }
+  
+  ExDeleteResource(&Job->JobLock);
 }
 
 VOID INIT_FUNCTION
@@ -68,9 +79,18 @@
   ObpCreateTypeObject(PsJobType);
   
   InitializeListHead(&PsJobListHead);
-  KeInitializeSpinLock(&PsJobListLock);
+  ExInitializeFastMutex(&PsJobListLock);
 }
 
+NTSTATUS
+PspAssignProcessToJob(PEPROCESS Process,
+                      PEJOB Job)
+{
+  DPRINT("PspAssignProcessToJob() is unimplemented!\n");
+  return STATUS_NOT_IMPLEMENTED;
+}
+
+
 /*
  * @unimplemented
  */
@@ -79,8 +99,80 @@
 NtAssignProcessToJobObject(HANDLE JobHandle,
                            HANDLE ProcessHandle)
 {
-  UNIMPLEMENTED;
-  return STATUS_NOT_IMPLEMENTED;
+  PEPROCESS Process;
+  KPROCESSOR_MODE PreviousMode;
+  NTSTATUS Status;
+  
+  PreviousMode = ExGetPreviousMode();
+  
+  /* make sure we're having a handle with enough rights, especially the to
+     terminate the process. otherwise one could abuse the job objects to
+     terminate processes without having rights granted to do so! The reason
+     I open the process handle before the job handle is that a simple test showed
+     that it first complains about a invalid process handle! The other way around
+     would be simpler though... */
+  Status = ObReferenceObjectByHandle(ProcessHandle,
+                                     PROCESS_TERMINATE,
+                                     PsProcessType,
+                                     PreviousMode,
+                                     (PVOID*)&Process,
+                                     NULL);
+  if(NT_SUCCESS(Status))
+  {
+    if(Process->Job == NULL)
+    {
+      PEJOB Job;
+      
+      Status = ObReferenceObjectByHandle(JobHandle,
+                                         JOB_OBJECT_ASSIGN_PROCESS,
+                                         PsJobType,
+                                         PreviousMode,
+                                         (PVOID*)&Job,
+                                         NULL);
+      if(NT_SUCCESS(Status))
+      {
+        /* 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! */
+
+        Status = PsLockProcess(Process, FALSE);
+        if(NT_SUCCESS(Status))
+        {
+          if(Process->Job == NULL && Process->SessionId == 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;
+          }
+          else
+          {
+            /* process is already assigned to a job or session id differs! */
+            Status = STATUS_ACCESS_DENIED;
+          }
+          PsUnlockProcess(Process);
+          
+          if(NT_SUCCESS(Status))
+          {
+            /* let's actually assign the process to the job as we're not holding
+               the process lock anymore! */
+            Status = PspAssignProcessToJob(Process, Job);
+          }
+        }
+
+        ObDereferenceObject(Job);
+      }
+    }
+    else
+    {
+      /* process is already assigned to a job or session id differs! */
+      Status = STATUS_ACCESS_DENIED;
+    }
+    
+    ObDereferenceObject(Process);
+  }
+
+  return Status;
 }
 
 
@@ -93,8 +185,90 @@
                   ACCESS_MASK DesiredAccess,
                   POBJECT_ATTRIBUTES ObjectAttributes)
 {
-  UNIMPLEMENTED;
-  return STATUS_NOT_IMPLEMENTED;
+  HANDLE hJob;
+  PEJOB Job;
+  KPROCESSOR_MODE PreviousMode;
+  PEPROCESS CurrentProcess;
+  NTSTATUS Status = STATUS_SUCCESS;
+
+  PreviousMode = ExGetPreviousMode();
+  CurrentProcess = PsGetCurrentProcess();
+
+  /* check for valid buffers */
+  if(PreviousMode == UserMode)
+  {
+    _SEH_TRY
+    {
+      /* probe with 32bit alignment */
+      ProbeForWrite(JobHandle,
+                    sizeof(HANDLE),
+                    sizeof(ULONG));
+    }
+    _SEH_HANDLE
+    {
+      Status = _SEH_GetExceptionCode();
+    }
+    _SEH_END;
+
+    if(!NT_SUCCESS(Status))
+    {
+      return Status;
+    }
+  }
+  
+  Status = ObCreateObject(PreviousMode,
+                          PsJobType,
+                          ObjectAttributes,
+                          PreviousMode,
+                          NULL,
+                          sizeof(EJOB),
+                          0,
+                          0,
+                          (PVOID*)&Job);
+  
+  if(NT_SUCCESS(Status))
+  {
+    /* FIXME - Zero all fields as we don't yet implement all of them */
+    RtlZeroMemory(Job, sizeof(EJOB));
+    
+    /* make sure that early destruction doesn't attempt to remove the object from
+       the list before it even gets added! */
+    Job->JobLinks.Flink = NULL;
+    
+    /* setup the job object */
+    InitializeListHead(&Job->ProcessListHead);
+    Job->SessionId = CurrentProcess->SessionId; /* inherit the session id from the caller */
+    
+    Status = ExInitializeResource(&Job->JobLock);
+    if(!NT_SUCCESS(Status))
+    {
+      DPRINT1("Failed to initialize job lock!!!\n");
+      ObDereferenceObject(Job);
+      return Status;
+    }
+    KeInitializeEvent(&Job->Event, NotificationEvent, FALSE);
+    
+    /* link the object into the global job list */
+    ExAcquireFastMutex(&PsJobListLock);
+    InsertTailList(&PsJobListHead, &Job->JobLinks);
+    ExReleaseFastMutex(&PsJobListLock);
+    
+    /* pass the handle back to the caller */
+    _SEH_TRY
+    {
+      /* NOTE: if the caller passed invalid buffers to receive the handle it's his
+               own fault! the object will still be created and live... It's possible
+               to find the handle using ObFindHandleForObject()! */
+      *JobHandle = hJob;
+    }
+    _SEH_HANDLE
+    {
+      Status = _SEH_GetExceptionCode();
+    }
+    _SEH_END;
+  }
+  
+  return Status;
 }