[NTOSKRNL] Make NtSetInformationJobObject() success for JobObjectExtendedLimitInformation
[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 /* FIXME: Call KeQueryValuesProcess()
635 * We should sum BasicInfo values here,
636 * but we don't have them
637 */
638 BasicAndIo.IoInfo.ReadOperationCount += Process->ReadOperationCount.QuadPart;
639 BasicAndIo.IoInfo.WriteOperationCount += Process->WriteOperationCount.QuadPart;
640 BasicAndIo.IoInfo.OtherOperationCount += Process->OtherOperationCount.QuadPart;
641 BasicAndIo.IoInfo.ReadTransferCount += Process->ReadTransferCount.QuadPart;
642 BasicAndIo.IoInfo.WriteTransferCount += Process->WriteTransferCount.QuadPart;
643 BasicAndIo.IoInfo.OtherTransferCount += Process->OtherTransferCount.QuadPart;
644 }
645 }
646
647 /* And done */
648 ExReleaseResourceLite(&Job->JobLock);
649 KeLeaveGuardedRegionThread(CurrentThread);
650
651 /* We'll copy back the buffer */
652 GenericCopy = &BasicAndIo;
653 Status = STATUS_SUCCESS;
654
655 break;
656
657 /* Limits information */
658 case JobObjectBasicLimitInformation:
659 case JobObjectExtendedLimitInformation:
660 /* Lock */
661 KeEnterGuardedRegionThread(CurrentThread);
662 ExAcquireResourceSharedLite(&Job->JobLock, TRUE);
663
664 /* Copy basic information */
665 ExtendedLimit.BasicLimitInformation.LimitFlags = Job->LimitFlags;
666 ExtendedLimit.BasicLimitInformation.MinimumWorkingSetSize = Job->MinimumWorkingSetSize;
667 ExtendedLimit.BasicLimitInformation.MaximumWorkingSetSize = Job->MaximumWorkingSetSize;
668 ExtendedLimit.BasicLimitInformation.ActiveProcessLimit = Job->ActiveProcessLimit;
669 ExtendedLimit.BasicLimitInformation.PriorityClass = Job->PriorityClass;
670 ExtendedLimit.BasicLimitInformation.SchedulingClass = Job->SchedulingClass;
671 ExtendedLimit.BasicLimitInformation.Affinity = Job->Affinity;
672 ExtendedLimit.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart = Job->PerProcessUserTimeLimit.QuadPart;
673 ExtendedLimit.BasicLimitInformation.PerJobUserTimeLimit.QuadPart = Job->PerJobUserTimeLimit.QuadPart;
674
675 /* If asking for extending limits */
676 if (JobInformationClass == JobObjectExtendedLimitInformation)
677 {
678 /* Lock our memory lock */
679 KeAcquireGuardedMutexUnsafe(&Job->MemoryLimitsLock);
680 /* Return limits */
681 ExtendedLimit.ProcessMemoryLimit = Job->ProcessMemoryLimit << PAGE_SHIFT;
682 ExtendedLimit.JobMemoryLimit = Job->JobMemoryLimit << PAGE_SHIFT;
683 ExtendedLimit.PeakProcessMemoryUsed = Job->PeakProcessMemoryUsed << PAGE_SHIFT;
684 ExtendedLimit.PeakJobMemoryUsed = Job->PeakJobMemoryUsed << PAGE_SHIFT;
685 KeReleaseGuardedMutexUnsafe(&Job->MemoryLimitsLock);
686
687 /* And done */
688 ExReleaseResourceLite(&Job->JobLock);
689 KeLeaveGuardedRegionThread(CurrentThread);
690
691 /* We'll never return IoInfo, so zero it out to avoid
692 * kernel memory leak
693 */
694 RtlZeroMemory(&ExtendedLimit.IoInfo, sizeof(IO_COUNTERS));
695 }
696 else
697 {
698 /* And done */
699 ExReleaseResourceLite(&Job->JobLock);
700 KeLeaveGuardedRegionThread(CurrentThread);
701 }
702
703 /* We'll copy back the buffer */
704 GenericCopy = &ExtendedLimit;
705 Status = STATUS_SUCCESS;
706
707 break;
708
709 default:
710 DPRINT1("Class %d not implemented\n", JobInformationClass);
711 Status = STATUS_NOT_IMPLEMENTED;
712 break;
713 }
714
715 /* Job is no longer required */
716 ObDereferenceObject(Job);
717
718 /* If we succeeed, copy back data */
719 if (NT_SUCCESS(Status))
720 {
721 _SEH2_TRY
722 {
723 /* If we have anything to copy, do it */
724 if (!NoOutput)
725 {
726 RtlCopyMemory(JobInformation, GenericCopy, SizeToCopy);
727 }
728
729 /* And return length if asked */
730 if (ReturnLength != NULL)
731 {
732 *ReturnLength = NeededSize;
733 }
734 }
735 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
736 {
737 _SEH2_YIELD(return _SEH2_GetExceptionCode());
738 }
739 _SEH2_END;
740 }
741
742 return Status;
743 }
744
745
746 /*
747 * @unimplemented
748 */
749 NTSTATUS
750 NTAPI
751 NtSetInformationJobObject (
752 HANDLE JobHandle,
753 JOBOBJECTINFOCLASS JobInformationClass,
754 PVOID JobInformation,
755 ULONG JobInformationLength)
756 {
757 PEJOB Job;
758 NTSTATUS Status;
759 PKTHREAD CurrentThread;
760 ACCESS_MASK DesiredAccess;
761 KPROCESSOR_MODE PreviousMode;
762 ULONG RequiredLength, RequiredAlign;
763
764 PAGED_CODE();
765
766 CurrentThread = KeGetCurrentThread();
767
768 /* Validate class */
769 if (JobInformationClass > JobObjectJobSetInformation || JobInformationClass < JobObjectBasicAccountingInformation)
770 {
771 return STATUS_INVALID_INFO_CLASS;
772 }
773
774 /* Get associated lengths & alignments */
775 RequiredLength = PspJobInfoLengths[JobInformationClass];
776 RequiredAlign = PspJobInfoAlign[JobInformationClass];
777
778 PreviousMode = ExGetPreviousMode();
779 /* If not comming from umode, we need to probe buffers */
780 if (PreviousMode != KernelMode)
781 {
782 ASSERT(((RequiredAlign) == 1) || ((RequiredAlign) == 2) || ((RequiredAlign) == 4) || ((RequiredAlign) == 8) || ((RequiredAlign) == 16));
783
784 _SEH2_TRY
785 {
786 /* Probe out buffer for read */
787 if (JobInformationLength != 0)
788 {
789 ProbeForRead(JobInformation, JobInformationLength, RequiredAlign);
790 }
791 }
792 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
793 {
794 _SEH2_YIELD(return _SEH2_GetExceptionCode());
795 }
796 _SEH2_END;
797 }
798
799 /* Validate input size */
800 if (JobInformationLength != RequiredLength)
801 {
802 return STATUS_INFO_LENGTH_MISMATCH;
803 }
804
805 /* Open the given job */
806 DesiredAccess = JOB_OBJECT_SET_ATTRIBUTES;
807 if (JobInformationClass == JobObjectSecurityLimitInformation)
808 {
809 DesiredAccess |= JOB_OBJECT_SET_SECURITY_ATTRIBUTES;
810 }
811 Status = ObReferenceObjectByHandle(JobHandle,
812 DesiredAccess,
813 PsJobType,
814 PreviousMode,
815 (PVOID*)&Job,
816 NULL);
817 if (!NT_SUCCESS(Status))
818 {
819 return Status;
820 }
821
822 /* And set the information */
823 KeEnterGuardedRegionThread(CurrentThread);
824 switch (JobInformationClass)
825 {
826 case JobObjectExtendedLimitInformation:
827 DPRINT1("Class JobObjectExtendedLimitInformation not implemented\n");
828 Status = STATUS_SUCCESS;
829 break;
830
831 default:
832 DPRINT1("Class %d not implemented\n", JobInformationClass);
833 Status = STATUS_NOT_IMPLEMENTED;
834 break;
835 }
836 KeLeaveGuardedRegionThread(CurrentThread);
837
838 ObfDereferenceObject(Job);
839
840 return Status;
841 }
842
843
844 /*
845 * @unimplemented
846 */
847 NTSTATUS
848 NTAPI
849 NtTerminateJobObject (
850 HANDLE JobHandle,
851 NTSTATUS ExitStatus )
852 {
853 KPROCESSOR_MODE PreviousMode;
854 PEJOB Job;
855 NTSTATUS Status;
856
857 PAGED_CODE();
858
859 PreviousMode = ExGetPreviousMode();
860
861 Status = ObReferenceObjectByHandle(
862 JobHandle,
863 JOB_OBJECT_TERMINATE,
864 PsJobType,
865 PreviousMode,
866 (PVOID*)&Job,
867 NULL);
868 if(NT_SUCCESS(Status))
869 {
870 Status = PspTerminateJobObject(
871 Job,
872 PreviousMode,
873 ExitStatus);
874 ObDereferenceObject(Job);
875 }
876
877 return Status;
878 }
879
880
881 /*
882 * @implemented
883 */
884 PVOID
885 NTAPI
886 PsGetJobLock ( PEJOB Job )
887 {
888 ASSERT(Job);
889 return (PVOID)&Job->JobLock;
890 }
891
892
893 /*
894 * @implemented
895 */
896 ULONG
897 NTAPI
898 PsGetJobSessionId ( PEJOB Job )
899 {
900 ASSERT(Job);
901 return Job->SessionId;
902 }
903
904
905 /*
906 * @implemented
907 */
908 ULONG
909 NTAPI
910 PsGetJobUIRestrictionsClass ( PEJOB Job )
911 {
912 ASSERT(Job);
913 return Job->UIRestrictionsClass;
914 }
915
916
917 /*
918 * @unimplemented
919 */
920 VOID
921 NTAPI
922 PsSetJobUIRestrictionsClass(PEJOB Job,
923 ULONG UIRestrictionsClass)
924 {
925 ASSERT(Job);
926 (void)InterlockedExchangeUL(&Job->UIRestrictionsClass, UIRestrictionsClass);
927 /* FIXME - walk through the job process list and update the restrictions? */
928 }
929
930 /* EOF */