xref: /reactos/ntoskrnl/ps/job.c (revision 4561998a)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS kernel
4  * FILE:            ntoskrnl/ps/job.c
5  * PURPOSE:         Job Native Functions
6  * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net) (stubs)
7  *                  Thomas Weidenmueller <w3seek@reactos.com>
8  *                  Pierre Schweitzer (pierre@reactos.org)
9  */
10 
11 /* INCLUDES *****************************************************************/
12 
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 
18 /* GLOBALS *******************************************************************/
19 
20 POBJECT_TYPE PsJobType = NULL;
21 
22 LIST_ENTRY PsJobListHead;
23 static FAST_MUTEX PsJobListLock;
24 
25 BOOLEAN PspUseJobSchedulingClasses;
26 
27 CHAR PspJobSchedulingClasses[PSP_JOB_SCHEDULING_CLASSES] =
28 {
29     1 * 6,
30     2 * 6,
31     3 * 6,
32     4 * 6,
33     5 * 6,
34     6 * 6,
35     7 * 6,
36     8 * 6,
37     9 * 6,
38     10 * 6
39 };
40 
41 GENERIC_MAPPING PspJobMapping =
42 {
43     STANDARD_RIGHTS_READ | JOB_OBJECT_QUERY,
44 
45     STANDARD_RIGHTS_WRITE | JOB_OBJECT_ASSIGN_PROCESS |
46     JOB_OBJECT_SET_ATTRIBUTES | JOB_OBJECT_TERMINATE,
47 
48     STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
49 
50     STANDARD_RIGHTS_ALL | THREAD_ALL_ACCESS // bug fixed only in vista
51 };
52 
53 ULONG PspJobInfoLengths[] =
54 {
55     0x0,
56     sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION),
57     sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION),
58     sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST),
59     sizeof(JOBOBJECT_BASIC_UI_RESTRICTIONS),
60     sizeof(JOBOBJECT_SECURITY_LIMIT_INFORMATION),
61     sizeof(JOBOBJECT_END_OF_JOB_TIME_INFORMATION),
62     sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT),
63     sizeof(JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION),
64     sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION),
65     0x4
66 };
67 
68 ULONG PspJobInfoAlign[] =
69 {
70     0x0,
71     sizeof(ULONG),
72     sizeof(ULONG),
73     sizeof(ULONG),
74     sizeof(ULONG),
75     sizeof(ULONG),
76     sizeof(ULONG),
77     sizeof(ULONG),
78     sizeof(ULONG),
79     sizeof(ULONG),
80     sizeof(ULONG)
81 };
82 
83 /* FUNCTIONS *****************************************************************/
84 
85 VOID
86 NTAPI
87 PspDeleteJob ( PVOID ObjectBody )
88 {
89     PEJOB Job = (PEJOB)ObjectBody;
90 
91     /* remove the reference to the completion port if associated */
92     if(Job->CompletionPort != NULL)
93     {
94         ObDereferenceObject(Job->CompletionPort);
95     }
96 
97     /* unlink the job object */
98     if(Job->JobLinks.Flink != NULL)
99     {
100         ExAcquireFastMutex(&PsJobListLock);
101         RemoveEntryList(&Job->JobLinks);
102         ExReleaseFastMutex(&PsJobListLock);
103     }
104 
105     ExDeleteResource(&Job->JobLock);
106 }
107 
108 INIT_FUNCTION
109 VOID
110 NTAPI
111 PspInitializeJobStructures(VOID)
112 {
113     InitializeListHead(&PsJobListHead);
114     ExInitializeFastMutex(&PsJobListLock);
115 }
116 
117 NTSTATUS
118 NTAPI
119 PspAssignProcessToJob(PEPROCESS Process,
120     PEJOB Job)
121 {
122     DPRINT("PspAssignProcessToJob() is unimplemented!\n");
123     return STATUS_NOT_IMPLEMENTED;
124 }
125 
126 NTSTATUS
127 NTAPI
128 PspTerminateJobObject(PEJOB Job,
129     KPROCESSOR_MODE AccessMode,
130     NTSTATUS ExitStatus )
131 {
132     DPRINT("PspTerminateJobObject() is unimplemented!\n");
133     return STATUS_NOT_IMPLEMENTED;
134 }
135 
136 VOID
137 NTAPI
138 PspRemoveProcessFromJob(IN PEPROCESS Process,
139                         IN PEJOB Job)
140 {
141     /* FIXME */
142 }
143 
144 VOID
145 NTAPI
146 PspExitProcessFromJob(IN PEJOB Job,
147                       IN PEPROCESS Process)
148 {
149     /* FIXME */
150 }
151 
152 /*
153  * @unimplemented
154  */
155 NTSTATUS
156 NTAPI
157 NtAssignProcessToJobObject (
158     HANDLE JobHandle,
159     HANDLE ProcessHandle)
160 {
161     PEPROCESS Process;
162     KPROCESSOR_MODE PreviousMode;
163     NTSTATUS Status;
164 
165     PAGED_CODE();
166 
167     PreviousMode = ExGetPreviousMode();
168 
169     /* make sure we're having a handle with enough rights, especially the to
170     terminate the process. otherwise one could abuse the job objects to
171     terminate processes without having rights granted to do so! The reason
172     I open the process handle before the job handle is that a simple test showed
173     that it first complains about a invalid process handle! The other way around
174     would be simpler though... */
175     Status = ObReferenceObjectByHandle(
176         ProcessHandle,
177         PROCESS_TERMINATE,
178         PsProcessType,
179         PreviousMode,
180         (PVOID*)&Process,
181         NULL);
182     if(NT_SUCCESS(Status))
183     {
184         if(Process->Job == NULL)
185         {
186             PEJOB Job;
187 
188             Status = ObReferenceObjectByHandle(
189                 JobHandle,
190                 JOB_OBJECT_ASSIGN_PROCESS,
191                 PsJobType,
192                 PreviousMode,
193                 (PVOID*)&Job,
194                 NULL);
195             if(NT_SUCCESS(Status))
196             {
197                 /* lock the process so we can safely assign the process. Note that in the
198                 meanwhile another thread could have assigned this process to a job! */
199 
200                 if(ExAcquireRundownProtection(&Process->RundownProtect))
201                 {
202                     if(Process->Job == NULL && PsGetProcessSessionId(Process) == Job->SessionId)
203                     {
204                         /* Just store the pointer to the job object in the process, we'll
205                         assign it later. The reason we can't do this here is that locking
206                         the job object might require it to wait, which is a bad thing
207                         while holding the process lock! */
208                         Process->Job = Job;
209                         ObReferenceObject(Job);
210                     }
211                     else
212                     {
213                         /* process is already assigned to a job or session id differs! */
214                         Status = STATUS_ACCESS_DENIED;
215                     }
216                     ExReleaseRundownProtection(&Process->RundownProtect);
217 
218                     if(NT_SUCCESS(Status))
219                     {
220                         /* let's actually assign the process to the job as we're not holding
221                         the process lock anymore! */
222                         Status = PspAssignProcessToJob(Process, Job);
223                     }
224                 }
225 
226                 ObDereferenceObject(Job);
227             }
228         }
229         else
230         {
231             /* process is already assigned to a job or session id differs! */
232             Status = STATUS_ACCESS_DENIED;
233         }
234 
235         ObDereferenceObject(Process);
236     }
237 
238     return Status;
239 }
240 
241 NTSTATUS
242 NTAPI
243 NtCreateJobSet(IN ULONG NumJob,
244                IN PJOB_SET_ARRAY UserJobSet,
245                IN ULONG Flags)
246 {
247     UNIMPLEMENTED;
248     return STATUS_NOT_IMPLEMENTED;
249 }
250 
251 /*
252  * @unimplemented
253  */
254 NTSTATUS
255 NTAPI
256 NtCreateJobObject (
257     PHANDLE JobHandle,
258     ACCESS_MASK DesiredAccess,
259     POBJECT_ATTRIBUTES ObjectAttributes )
260 {
261     HANDLE hJob;
262     PEJOB Job;
263     KPROCESSOR_MODE PreviousMode;
264     PEPROCESS CurrentProcess;
265     NTSTATUS Status;
266 
267     PAGED_CODE();
268 
269     PreviousMode = ExGetPreviousMode();
270     CurrentProcess = PsGetCurrentProcess();
271 
272     /* check for valid buffers */
273     if (PreviousMode != KernelMode)
274     {
275         _SEH2_TRY
276         {
277             ProbeForWriteHandle(JobHandle);
278         }
279         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
280         {
281             _SEH2_YIELD(return _SEH2_GetExceptionCode());
282         }
283         _SEH2_END;
284     }
285 
286     Status = ObCreateObject(PreviousMode,
287         PsJobType,
288         ObjectAttributes,
289         PreviousMode,
290         NULL,
291         sizeof(EJOB),
292         0,
293         0,
294         (PVOID*)&Job);
295 
296     if(NT_SUCCESS(Status))
297     {
298         /* FIXME - Zero all fields as we don't yet implement all of them */
299         RtlZeroMemory(Job, sizeof(EJOB));
300 
301         /* make sure that early destruction doesn't attempt to remove the object from
302         the list before it even gets added! */
303         Job->JobLinks.Flink = NULL;
304 
305         /* setup the job object - FIXME: More to do! */
306         InitializeListHead(&Job->JobSetLinks);
307         InitializeListHead(&Job->ProcessListHead);
308 
309         /* inherit the session id from the caller */
310         Job->SessionId = PsGetProcessSessionId(CurrentProcess);
311 
312         KeInitializeGuardedMutex(&Job->MemoryLimitsLock);
313 
314         Status = ExInitializeResource(&Job->JobLock);
315         if(!NT_SUCCESS(Status))
316         {
317             DPRINT1("Failed to initialize job lock!!!\n");
318             ObDereferenceObject(Job);
319             return Status;
320         }
321         KeInitializeEvent(&Job->Event, NotificationEvent, FALSE);
322 
323         /* link the object into the global job list */
324         ExAcquireFastMutex(&PsJobListLock);
325         InsertTailList(&PsJobListHead, &Job->JobLinks);
326         ExReleaseFastMutex(&PsJobListLock);
327 
328         Status = ObInsertObject(Job,
329             NULL,
330             DesiredAccess,
331             0,
332             NULL,
333             &hJob);
334         if(NT_SUCCESS(Status))
335         {
336             /* pass the handle back to the caller */
337             _SEH2_TRY
338             {
339                 /* NOTE: if the caller passed invalid buffers to receive the handle it's his
340                 own fault! the object will still be created and live... It's possible
341                 to find the handle using ObFindHandleForObject()! */
342                 *JobHandle = hJob;
343             }
344             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
345             {
346                 Status = _SEH2_GetExceptionCode();
347             }
348             _SEH2_END;
349         }
350     }
351 
352     return Status;
353 }
354 
355 
356 /*
357  * @implemented
358  */
359 NTSTATUS
360 NTAPI
361 NtIsProcessInJob (
362     IN HANDLE ProcessHandle,
363     IN HANDLE JobHandle OPTIONAL )
364 {
365     KPROCESSOR_MODE PreviousMode;
366     PEPROCESS Process;
367     NTSTATUS Status;
368 
369     PreviousMode = ExGetPreviousMode();
370 
371     PAGED_CODE();
372 
373     Status = ObReferenceObjectByHandle(
374         ProcessHandle,
375         PROCESS_QUERY_INFORMATION,
376         PsProcessType,
377         PreviousMode,
378         (PVOID*)&Process,
379         NULL);
380     if(NT_SUCCESS(Status))
381     {
382         /* FIXME - make sure the job object doesn't get exchanged or deleted while trying to
383         reference it, e.g. by locking it somehow until it is referenced... */
384 
385         PEJOB ProcessJob = Process->Job;
386 
387         if(ProcessJob != NULL)
388         {
389             if(JobHandle == NULL)
390             {
391                 /* the process is assigned to a job */
392                 Status = STATUS_PROCESS_IN_JOB;
393             }
394             else /* JobHandle != NULL */
395             {
396                 PEJOB JobObject;
397 
398                 /* get the job object and compare the object pointer with the one assigned to the process */
399                 Status = ObReferenceObjectByHandle(JobHandle,
400                     JOB_OBJECT_QUERY,
401                     PsJobType,
402                     PreviousMode,
403                     (PVOID*)&JobObject,
404                     NULL);
405                 if(NT_SUCCESS(Status))
406                 {
407                     Status = ((ProcessJob == JobObject) ? STATUS_PROCESS_IN_JOB : STATUS_PROCESS_NOT_IN_JOB);
408                     ObDereferenceObject(JobObject);
409                 }
410             }
411         }
412         else
413         {
414             /* the process is not assigned to any job */
415             Status = STATUS_PROCESS_NOT_IN_JOB;
416         }
417         ObDereferenceObject(Process);
418     }
419 
420     return Status;
421 }
422 
423 
424 /*
425  * @implemented
426  */
427 NTSTATUS
428 NTAPI
429 NtOpenJobObject (
430     PHANDLE JobHandle,
431     ACCESS_MASK DesiredAccess,
432     POBJECT_ATTRIBUTES ObjectAttributes)
433 {
434     KPROCESSOR_MODE PreviousMode;
435     HANDLE hJob;
436     NTSTATUS Status;
437 
438     PAGED_CODE();
439 
440     PreviousMode = ExGetPreviousMode();
441 
442     /* check for valid buffers */
443     if (PreviousMode != KernelMode)
444     {
445         _SEH2_TRY
446         {
447             ProbeForWriteHandle(JobHandle);
448         }
449         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
450         {
451             _SEH2_YIELD(return _SEH2_GetExceptionCode());
452         }
453         _SEH2_END;
454     }
455 
456     Status = ObOpenObjectByName(ObjectAttributes,
457         PsJobType,
458         PreviousMode,
459         NULL,
460         DesiredAccess,
461         NULL,
462         &hJob);
463     if(NT_SUCCESS(Status))
464     {
465         _SEH2_TRY
466         {
467             *JobHandle = hJob;
468         }
469         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
470         {
471             Status = _SEH2_GetExceptionCode();
472         }
473         _SEH2_END;
474     }
475 
476     return Status;
477 }
478 
479 
480 /*
481  * @implemented
482  */
483 NTSTATUS
484 NTAPI
485 NtQueryInformationJobObject (
486     HANDLE JobHandle,
487     JOBOBJECTINFOCLASS JobInformationClass,
488     PVOID JobInformation,
489     ULONG JobInformationLength,
490     PULONG ReturnLength )
491 {
492     PEJOB Job;
493     NTSTATUS Status;
494     BOOLEAN NoOutput;
495     PVOID GenericCopy;
496     PLIST_ENTRY NextEntry;
497     PKTHREAD CurrentThread;
498     KPROCESSOR_MODE PreviousMode;
499     JOBOBJECT_EXTENDED_LIMIT_INFORMATION ExtendedLimit;
500     JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION BasicAndIo;
501     ULONG RequiredLength, RequiredAlign, SizeToCopy, NeededSize;
502 
503     PAGED_CODE();
504 
505     CurrentThread  = KeGetCurrentThread();
506 
507     /* Validate class */
508     if (JobInformationClass > JobObjectJobSetInformation || JobInformationClass < JobObjectBasicAccountingInformation)
509     {
510         return STATUS_INVALID_INFO_CLASS;
511     }
512 
513     /* Get associated lengths & alignments */
514     RequiredLength = PspJobInfoLengths[JobInformationClass];
515     RequiredAlign = PspJobInfoAlign[JobInformationClass];
516     SizeToCopy = RequiredLength;
517     NeededSize = RequiredLength;
518 
519     /* If length mismatch (needed versus provided) */
520     if (JobInformationLength != RequiredLength)
521     {
522         /* This can only be accepted if: JobObjectBasicProcessIdList or JobObjectSecurityLimitInformation
523          * Or if size is bigger than needed
524          */
525         if ((JobInformationClass != JobObjectBasicProcessIdList && JobInformationClass != JobObjectSecurityLimitInformation) ||
526             JobInformationLength < RequiredLength)
527         {
528             return STATUS_INFO_LENGTH_MISMATCH;
529         }
530 
531         /* Set what we need to copy out */
532         SizeToCopy = JobInformationLength;
533     }
534 
535     PreviousMode = ExGetPreviousMode();
536     /* If not comming from umode, we need to probe buffers */
537     if (PreviousMode != KernelMode)
538     {
539         ASSERT(((RequiredAlign) == 1) || ((RequiredAlign) == 2) || ((RequiredAlign) == 4) || ((RequiredAlign) == 8) || ((RequiredAlign) == 16));
540 
541         _SEH2_TRY
542         {
543             /* Probe out buffer for write */
544             if (JobInformation != NULL)
545             {
546                 ProbeForWrite(JobInformation, JobInformationLength, RequiredAlign);
547             }
548 
549             /* But also return lenght if asked */
550             if (ReturnLength != NULL)
551             {
552                 ProbeForWrite(JobInformation, sizeof(ULONG), sizeof(ULONG));
553             }
554         }
555         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
556         {
557             _SEH2_YIELD(return _SEH2_GetExceptionCode());
558         }
559         _SEH2_END;
560     }
561 
562     /* If a job handle was provided, use it */
563     if (JobHandle != NULL)
564     {
565         Status = ObReferenceObjectByHandle(JobHandle,
566                                            JOB_OBJECT_QUERY,
567                                            PsJobType,
568                                            PreviousMode,
569                                            (PVOID*)&Job,
570                                            NULL);
571         if (!NT_SUCCESS(Status))
572         {
573             return Status;
574         }
575     }
576     /* Otherwise, get our current process' job, if any */
577     else
578     {
579         PEPROCESS CurrentProcess;
580 
581         CurrentProcess = (PEPROCESS)CurrentThread->ApcState.Process;
582         Job = CurrentProcess->Job;
583         if (Job == NULL)
584         {
585             return STATUS_ACCESS_DENIED;
586         }
587 
588         ObReferenceObject(Job);
589     }
590 
591     /* By default, assume we'll have to copy data */
592     NoOutput = FALSE;
593     /* Select class */
594     switch (JobInformationClass)
595     {
596         /* Basic counters */
597         case JobObjectBasicAccountingInformation:
598         case JobObjectBasicAndIoAccountingInformation:
599             /* Zero basics */
600             RtlZeroMemory(&BasicAndIo.BasicInfo, sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION));
601 
602             /* Lock */
603             KeEnterGuardedRegionThread(CurrentThread);
604             ExAcquireResourceSharedLite(&Job->JobLock, TRUE);
605 
606             /* Initialize with job counters */
607             BasicAndIo.BasicInfo.TotalUserTime.QuadPart = Job->TotalUserTime.QuadPart;
608             BasicAndIo.BasicInfo.TotalKernelTime.QuadPart = Job->TotalKernelTime.QuadPart;
609             BasicAndIo.BasicInfo.ThisPeriodTotalUserTime.QuadPart = Job->ThisPeriodTotalUserTime.QuadPart;
610             BasicAndIo.BasicInfo.ThisPeriodTotalKernelTime.QuadPart = Job->ThisPeriodTotalKernelTime.QuadPart;
611             BasicAndIo.BasicInfo.TotalPageFaultCount = Job->TotalPageFaultCount;
612             BasicAndIo.BasicInfo.TotalProcesses = Job->TotalProcesses;
613             BasicAndIo.BasicInfo.ActiveProcesses = Job->ActiveProcesses;
614             BasicAndIo.BasicInfo.TotalTerminatedProcesses = Job->TotalTerminatedProcesses;
615 
616             /* We also set IoInfo, even though we might not return it */
617             BasicAndIo.IoInfo.ReadOperationCount = Job->ReadOperationCount;
618             BasicAndIo.IoInfo.WriteOperationCount = Job->WriteOperationCount;
619             BasicAndIo.IoInfo.OtherOperationCount = Job->OtherOperationCount;
620             BasicAndIo.IoInfo.ReadTransferCount = Job->ReadTransferCount;
621             BasicAndIo.IoInfo.WriteTransferCount = Job->WriteTransferCount;
622             BasicAndIo.IoInfo.OtherTransferCount = Job->OtherTransferCount;
623 
624             /* For every process, sum its counters */
625             for (NextEntry = Job->ProcessListHead.Flink;
626                  NextEntry != &Job->ProcessListHead;
627                  NextEntry = NextEntry->Flink)
628             {
629                 PEPROCESS Process;
630 
631                 Process = CONTAINING_RECORD(NextEntry, EPROCESS, JobLinks);
632                 if (!BooleanFlagOn(Process->JobStatus, 2))
633                 {
634                     PROCESS_VALUES Values;
635 
636                     KeQueryValuesProcess(&Process->Pcb, &Values);
637                     BasicAndIo.BasicInfo.TotalUserTime.QuadPart += Values.TotalUserTime.QuadPart;
638                     BasicAndIo.BasicInfo.TotalKernelTime.QuadPart += Values.TotalKernelTime.QuadPart;
639                     BasicAndIo.IoInfo.ReadOperationCount += Values.IoInfo.ReadOperationCount;
640                     BasicAndIo.IoInfo.WriteOperationCount += Values.IoInfo.WriteOperationCount;
641                     BasicAndIo.IoInfo.OtherOperationCount += Values.IoInfo.OtherOperationCount;
642                     BasicAndIo.IoInfo.ReadTransferCount += Values.IoInfo.ReadTransferCount;
643                     BasicAndIo.IoInfo.WriteTransferCount += Values.IoInfo.WriteTransferCount;
644                     BasicAndIo.IoInfo.OtherTransferCount += Values.IoInfo.OtherTransferCount;
645                 }
646             }
647 
648             /* And done */
649             ExReleaseResourceLite(&Job->JobLock);
650             KeLeaveGuardedRegionThread(CurrentThread);
651 
652             /* We'll copy back the buffer */
653             GenericCopy = &BasicAndIo;
654             Status = STATUS_SUCCESS;
655 
656             break;
657 
658         /* Limits information */
659         case JobObjectBasicLimitInformation:
660         case JobObjectExtendedLimitInformation:
661             /* Lock */
662             KeEnterGuardedRegionThread(CurrentThread);
663             ExAcquireResourceSharedLite(&Job->JobLock, TRUE);
664 
665             /* Copy basic information */
666             ExtendedLimit.BasicLimitInformation.LimitFlags = Job->LimitFlags;
667             ExtendedLimit.BasicLimitInformation.MinimumWorkingSetSize = Job->MinimumWorkingSetSize;
668             ExtendedLimit.BasicLimitInformation.MaximumWorkingSetSize = Job->MaximumWorkingSetSize;
669             ExtendedLimit.BasicLimitInformation.ActiveProcessLimit = Job->ActiveProcessLimit;
670             ExtendedLimit.BasicLimitInformation.PriorityClass = Job->PriorityClass;
671             ExtendedLimit.BasicLimitInformation.SchedulingClass = Job->SchedulingClass;
672             ExtendedLimit.BasicLimitInformation.Affinity = Job->Affinity;
673             ExtendedLimit.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart = Job->PerProcessUserTimeLimit.QuadPart;
674             ExtendedLimit.BasicLimitInformation.PerJobUserTimeLimit.QuadPart = Job->PerJobUserTimeLimit.QuadPart;
675 
676             /* If asking for extending limits */
677             if (JobInformationClass == JobObjectExtendedLimitInformation)
678             {
679                 /* Lock our memory lock */
680                 KeAcquireGuardedMutexUnsafe(&Job->MemoryLimitsLock);
681                 /* Return limits */
682                 ExtendedLimit.ProcessMemoryLimit = Job->ProcessMemoryLimit << PAGE_SHIFT;
683                 ExtendedLimit.JobMemoryLimit = Job->JobMemoryLimit << PAGE_SHIFT;
684                 ExtendedLimit.PeakProcessMemoryUsed = Job->PeakProcessMemoryUsed << PAGE_SHIFT;
685                 ExtendedLimit.PeakJobMemoryUsed = Job->PeakJobMemoryUsed << PAGE_SHIFT;
686                 KeReleaseGuardedMutexUnsafe(&Job->MemoryLimitsLock);
687 
688                 /* And done */
689                 ExReleaseResourceLite(&Job->JobLock);
690                 KeLeaveGuardedRegionThread(CurrentThread);
691 
692                 /* We'll never return IoInfo, so zero it out to avoid
693                  * kernel memory leak
694                  */
695                 RtlZeroMemory(&ExtendedLimit.IoInfo, sizeof(IO_COUNTERS));
696             }
697             else
698             {
699                 /* And done */
700                 ExReleaseResourceLite(&Job->JobLock);
701                 KeLeaveGuardedRegionThread(CurrentThread);
702             }
703 
704             /* We'll copy back the buffer */
705             GenericCopy = &ExtendedLimit;
706             Status = STATUS_SUCCESS;
707 
708             break;
709 
710         default:
711             DPRINT1("Class %d not implemented\n", JobInformationClass);
712             Status = STATUS_NOT_IMPLEMENTED;
713             break;
714     }
715 
716     /* Job is no longer required */
717     ObDereferenceObject(Job);
718 
719     /* If we succeeed, copy back data */
720     if (NT_SUCCESS(Status))
721     {
722         _SEH2_TRY
723         {
724             /* If we have anything to copy, do it */
725             if (!NoOutput)
726             {
727                 RtlCopyMemory(JobInformation, GenericCopy, SizeToCopy);
728             }
729 
730             /* And return length if asked */
731             if (ReturnLength != NULL)
732             {
733                 *ReturnLength = NeededSize;
734             }
735         }
736         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
737         {
738             _SEH2_YIELD(return _SEH2_GetExceptionCode());
739         }
740         _SEH2_END;
741     }
742 
743     return Status;
744 }
745 
746 
747 /*
748  * @unimplemented
749  */
750 NTSTATUS
751 NTAPI
752 NtSetInformationJobObject (
753     HANDLE JobHandle,
754     JOBOBJECTINFOCLASS JobInformationClass,
755     PVOID JobInformation,
756     ULONG JobInformationLength)
757 {
758     PEJOB Job;
759     NTSTATUS Status;
760     PKTHREAD CurrentThread;
761     ACCESS_MASK DesiredAccess;
762     KPROCESSOR_MODE PreviousMode;
763     ULONG RequiredLength, RequiredAlign;
764 
765     PAGED_CODE();
766 
767     CurrentThread  = KeGetCurrentThread();
768 
769     /* Validate class */
770     if (JobInformationClass > JobObjectJobSetInformation || JobInformationClass < JobObjectBasicAccountingInformation)
771     {
772         return STATUS_INVALID_INFO_CLASS;
773     }
774 
775     /* Get associated lengths & alignments */
776     RequiredLength = PspJobInfoLengths[JobInformationClass];
777     RequiredAlign = PspJobInfoAlign[JobInformationClass];
778 
779     PreviousMode = ExGetPreviousMode();
780     /* If not comming from umode, we need to probe buffers */
781     if (PreviousMode != KernelMode)
782     {
783         ASSERT(((RequiredAlign) == 1) || ((RequiredAlign) == 2) || ((RequiredAlign) == 4) || ((RequiredAlign) == 8) || ((RequiredAlign) == 16));
784 
785         _SEH2_TRY
786         {
787             /* Probe out buffer for read */
788             if (JobInformationLength != 0)
789             {
790                 ProbeForRead(JobInformation, JobInformationLength, RequiredAlign);
791             }
792         }
793         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
794         {
795             _SEH2_YIELD(return _SEH2_GetExceptionCode());
796         }
797         _SEH2_END;
798     }
799 
800     /* Validate input size */
801     if (JobInformationLength != RequiredLength)
802     {
803         return STATUS_INFO_LENGTH_MISMATCH;
804     }
805 
806     /* Open the given job */
807     DesiredAccess = JOB_OBJECT_SET_ATTRIBUTES;
808     if (JobInformationClass == JobObjectSecurityLimitInformation)
809     {
810         DesiredAccess |= JOB_OBJECT_SET_SECURITY_ATTRIBUTES;
811     }
812     Status = ObReferenceObjectByHandle(JobHandle,
813                                        DesiredAccess,
814                                        PsJobType,
815                                        PreviousMode,
816                                        (PVOID*)&Job,
817                                        NULL);
818     if (!NT_SUCCESS(Status))
819     {
820         return Status;
821     }
822 
823     /* And set the information */
824     KeEnterGuardedRegionThread(CurrentThread);
825     switch (JobInformationClass)
826     {
827         case JobObjectExtendedLimitInformation:
828             DPRINT1("Class JobObjectExtendedLimitInformation not implemented\n");
829             Status = STATUS_SUCCESS;
830             break;
831 
832         default:
833             DPRINT1("Class %d not implemented\n", JobInformationClass);
834             Status = STATUS_NOT_IMPLEMENTED;
835             break;
836     }
837     KeLeaveGuardedRegionThread(CurrentThread);
838 
839     ObfDereferenceObject(Job);
840 
841     return Status;
842 }
843 
844 
845 /*
846  * @unimplemented
847  */
848 NTSTATUS
849 NTAPI
850 NtTerminateJobObject (
851     HANDLE JobHandle,
852     NTSTATUS ExitStatus )
853 {
854     KPROCESSOR_MODE PreviousMode;
855     PEJOB Job;
856     NTSTATUS Status;
857 
858     PAGED_CODE();
859 
860     PreviousMode = ExGetPreviousMode();
861 
862     Status = ObReferenceObjectByHandle(
863         JobHandle,
864         JOB_OBJECT_TERMINATE,
865         PsJobType,
866         PreviousMode,
867         (PVOID*)&Job,
868         NULL);
869     if(NT_SUCCESS(Status))
870     {
871         Status = PspTerminateJobObject(
872             Job,
873             PreviousMode,
874             ExitStatus);
875         ObDereferenceObject(Job);
876     }
877 
878     return Status;
879 }
880 
881 
882 /*
883  * @implemented
884  */
885 PVOID
886 NTAPI
887 PsGetJobLock ( PEJOB Job )
888 {
889     ASSERT(Job);
890     return (PVOID)&Job->JobLock;
891 }
892 
893 
894 /*
895  * @implemented
896  */
897 ULONG
898 NTAPI
899 PsGetJobSessionId ( PEJOB Job )
900 {
901     ASSERT(Job);
902     return Job->SessionId;
903 }
904 
905 
906 /*
907  * @implemented
908  */
909 ULONG
910 NTAPI
911 PsGetJobUIRestrictionsClass ( PEJOB Job )
912 {
913     ASSERT(Job);
914     return Job->UIRestrictionsClass;
915 }
916 
917 
918 /*
919  * @unimplemented
920  */
921 VOID
922 NTAPI
923 PsSetJobUIRestrictionsClass(PEJOB Job,
924     ULONG UIRestrictionsClass)
925 {
926     ASSERT(Job);
927     (void)InterlockedExchangeUL(&Job->UIRestrictionsClass, UIRestrictionsClass);
928     /* FIXME - walk through the job process list and update the restrictions? */
929 }
930 
931 /* EOF */
932