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 */ 932