[NTOSKRNL] Implement KeQueryValuesProcess().
[reactos.git] / ntoskrnl / ps / job.c
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 VOID
109 NTAPI
110 INIT_FUNCTION
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 */