[NTOSKRNL] Quickly implement the !defwrites in KDBG
[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 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16
17 /* GLOBALS *******************************************************************/
18
19 POBJECT_TYPE PsJobType = NULL;
20
21 LIST_ENTRY PsJobListHead;
22 static FAST_MUTEX PsJobListLock;
23
24 BOOLEAN PspUseJobSchedulingClasses;
25
26 CHAR PspJobSchedulingClasses[PSP_JOB_SCHEDULING_CLASSES] =
27 {
28 1 * 6,
29 2 * 6,
30 3 * 6,
31 4 * 6,
32 5 * 6,
33 6 * 6,
34 7 * 6,
35 8 * 6,
36 9 * 6,
37 10 * 6
38 };
39
40 GENERIC_MAPPING PspJobMapping =
41 {
42 STANDARD_RIGHTS_READ | JOB_OBJECT_QUERY,
43
44 STANDARD_RIGHTS_WRITE | JOB_OBJECT_ASSIGN_PROCESS |
45 JOB_OBJECT_SET_ATTRIBUTES | JOB_OBJECT_TERMINATE,
46
47 STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
48
49 STANDARD_RIGHTS_ALL | THREAD_ALL_ACCESS // bug fixed only in vista
50 };
51
52 /* FUNCTIONS *****************************************************************/
53
54 VOID
55 NTAPI
56 PspDeleteJob ( PVOID ObjectBody )
57 {
58 PEJOB Job = (PEJOB)ObjectBody;
59
60 /* remove the reference to the completion port if associated */
61 if(Job->CompletionPort != NULL)
62 {
63 ObDereferenceObject(Job->CompletionPort);
64 }
65
66 /* unlink the job object */
67 if(Job->JobLinks.Flink != NULL)
68 {
69 ExAcquireFastMutex(&PsJobListLock);
70 RemoveEntryList(&Job->JobLinks);
71 ExReleaseFastMutex(&PsJobListLock);
72 }
73
74 ExDeleteResource(&Job->JobLock);
75 }
76
77 VOID
78 NTAPI
79 INIT_FUNCTION
80 PspInitializeJobStructures(VOID)
81 {
82 InitializeListHead(&PsJobListHead);
83 ExInitializeFastMutex(&PsJobListLock);
84 }
85
86 NTSTATUS
87 NTAPI
88 PspAssignProcessToJob(PEPROCESS Process,
89 PEJOB Job)
90 {
91 DPRINT("PspAssignProcessToJob() is unimplemented!\n");
92 return STATUS_NOT_IMPLEMENTED;
93 }
94
95 NTSTATUS
96 NTAPI
97 PspTerminateJobObject(PEJOB Job,
98 KPROCESSOR_MODE AccessMode,
99 NTSTATUS ExitStatus )
100 {
101 DPRINT("PspTerminateJobObject() is unimplemented!\n");
102 return STATUS_NOT_IMPLEMENTED;
103 }
104
105 VOID
106 NTAPI
107 PspRemoveProcessFromJob(IN PEPROCESS Process,
108 IN PEJOB Job)
109 {
110 /* FIXME */
111 }
112
113 VOID
114 NTAPI
115 PspExitProcessFromJob(IN PEJOB Job,
116 IN PEPROCESS Process)
117 {
118 /* FIXME */
119 }
120
121 /*
122 * @unimplemented
123 */
124 NTSTATUS
125 NTAPI
126 NtAssignProcessToJobObject (
127 HANDLE JobHandle,
128 HANDLE ProcessHandle)
129 {
130 PEPROCESS Process;
131 KPROCESSOR_MODE PreviousMode;
132 NTSTATUS Status;
133
134 PAGED_CODE();
135
136 PreviousMode = ExGetPreviousMode();
137
138 /* make sure we're having a handle with enough rights, especially the to
139 terminate the process. otherwise one could abuse the job objects to
140 terminate processes without having rights granted to do so! The reason
141 I open the process handle before the job handle is that a simple test showed
142 that it first complains about a invalid process handle! The other way around
143 would be simpler though... */
144 Status = ObReferenceObjectByHandle(
145 ProcessHandle,
146 PROCESS_TERMINATE,
147 PsProcessType,
148 PreviousMode,
149 (PVOID*)&Process,
150 NULL);
151 if(NT_SUCCESS(Status))
152 {
153 if(Process->Job == NULL)
154 {
155 PEJOB Job;
156
157 Status = ObReferenceObjectByHandle(
158 JobHandle,
159 JOB_OBJECT_ASSIGN_PROCESS,
160 PsJobType,
161 PreviousMode,
162 (PVOID*)&Job,
163 NULL);
164 if(NT_SUCCESS(Status))
165 {
166 /* lock the process so we can safely assign the process. Note that in the
167 meanwhile another thread could have assigned this process to a job! */
168
169 if(ExAcquireRundownProtection(&Process->RundownProtect))
170 {
171 if(Process->Job == NULL && PsGetProcessSessionId(Process) == Job->SessionId)
172 {
173 /* Just store the pointer to the job object in the process, we'll
174 assign it later. The reason we can't do this here is that locking
175 the job object might require it to wait, which is a bad thing
176 while holding the process lock! */
177 Process->Job = Job;
178 ObReferenceObject(Job);
179 }
180 else
181 {
182 /* process is already assigned to a job or session id differs! */
183 Status = STATUS_ACCESS_DENIED;
184 }
185 ExReleaseRundownProtection(&Process->RundownProtect);
186
187 if(NT_SUCCESS(Status))
188 {
189 /* let's actually assign the process to the job as we're not holding
190 the process lock anymore! */
191 Status = PspAssignProcessToJob(Process, Job);
192 }
193 }
194
195 ObDereferenceObject(Job);
196 }
197 }
198 else
199 {
200 /* process is already assigned to a job or session id differs! */
201 Status = STATUS_ACCESS_DENIED;
202 }
203
204 ObDereferenceObject(Process);
205 }
206
207 return Status;
208 }
209
210 NTSTATUS
211 NTAPI
212 NtCreateJobSet(IN ULONG NumJob,
213 IN PJOB_SET_ARRAY UserJobSet,
214 IN ULONG Flags)
215 {
216 UNIMPLEMENTED;
217 return STATUS_NOT_IMPLEMENTED;
218 }
219
220 /*
221 * @unimplemented
222 */
223 NTSTATUS
224 NTAPI
225 NtCreateJobObject (
226 PHANDLE JobHandle,
227 ACCESS_MASK DesiredAccess,
228 POBJECT_ATTRIBUTES ObjectAttributes )
229 {
230 HANDLE hJob;
231 PEJOB Job;
232 KPROCESSOR_MODE PreviousMode;
233 PEPROCESS CurrentProcess;
234 NTSTATUS Status;
235
236 PAGED_CODE();
237
238 PreviousMode = ExGetPreviousMode();
239 CurrentProcess = PsGetCurrentProcess();
240
241 /* check for valid buffers */
242 if (PreviousMode != KernelMode)
243 {
244 _SEH2_TRY
245 {
246 ProbeForWriteHandle(JobHandle);
247 }
248 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
249 {
250 _SEH2_YIELD(return _SEH2_GetExceptionCode());
251 }
252 _SEH2_END;
253 }
254
255 Status = ObCreateObject(PreviousMode,
256 PsJobType,
257 ObjectAttributes,
258 PreviousMode,
259 NULL,
260 sizeof(EJOB),
261 0,
262 0,
263 (PVOID*)&Job);
264
265 if(NT_SUCCESS(Status))
266 {
267 /* FIXME - Zero all fields as we don't yet implement all of them */
268 RtlZeroMemory(Job, sizeof(EJOB));
269
270 /* make sure that early destruction doesn't attempt to remove the object from
271 the list before it even gets added! */
272 Job->JobLinks.Flink = NULL;
273
274 /* setup the job object - FIXME: More to do! */
275 InitializeListHead(&Job->JobSetLinks);
276 InitializeListHead(&Job->ProcessListHead);
277
278 /* inherit the session id from the caller */
279 Job->SessionId = PsGetProcessSessionId(CurrentProcess);
280
281 Status = ExInitializeResource(&Job->JobLock);
282 if(!NT_SUCCESS(Status))
283 {
284 DPRINT1("Failed to initialize job lock!!!\n");
285 ObDereferenceObject(Job);
286 return Status;
287 }
288 KeInitializeEvent(&Job->Event, NotificationEvent, FALSE);
289
290 /* link the object into the global job list */
291 ExAcquireFastMutex(&PsJobListLock);
292 InsertTailList(&PsJobListHead, &Job->JobLinks);
293 ExReleaseFastMutex(&PsJobListLock);
294
295 Status = ObInsertObject(Job,
296 NULL,
297 DesiredAccess,
298 0,
299 NULL,
300 &hJob);
301 if(NT_SUCCESS(Status))
302 {
303 /* pass the handle back to the caller */
304 _SEH2_TRY
305 {
306 /* NOTE: if the caller passed invalid buffers to receive the handle it's his
307 own fault! the object will still be created and live... It's possible
308 to find the handle using ObFindHandleForObject()! */
309 *JobHandle = hJob;
310 }
311 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
312 {
313 Status = _SEH2_GetExceptionCode();
314 }
315 _SEH2_END;
316 }
317 }
318
319 return Status;
320 }
321
322
323 /*
324 * @implemented
325 */
326 NTSTATUS
327 NTAPI
328 NtIsProcessInJob (
329 IN HANDLE ProcessHandle,
330 IN HANDLE JobHandle OPTIONAL )
331 {
332 KPROCESSOR_MODE PreviousMode;
333 PEPROCESS Process;
334 NTSTATUS Status;
335
336 PreviousMode = ExGetPreviousMode();
337
338 PAGED_CODE();
339
340 Status = ObReferenceObjectByHandle(
341 ProcessHandle,
342 PROCESS_QUERY_INFORMATION,
343 PsProcessType,
344 PreviousMode,
345 (PVOID*)&Process,
346 NULL);
347 if(NT_SUCCESS(Status))
348 {
349 /* FIXME - make sure the job object doesn't get exchanged or deleted while trying to
350 reference it, e.g. by locking it somehow until it is referenced... */
351
352 PEJOB ProcessJob = Process->Job;
353
354 if(ProcessJob != NULL)
355 {
356 if(JobHandle == NULL)
357 {
358 /* the process is assigned to a job */
359 Status = STATUS_PROCESS_IN_JOB;
360 }
361 else /* JobHandle != NULL */
362 {
363 PEJOB JobObject;
364
365 /* get the job object and compare the object pointer with the one assigned to the process */
366 Status = ObReferenceObjectByHandle(JobHandle,
367 JOB_OBJECT_QUERY,
368 PsJobType,
369 PreviousMode,
370 (PVOID*)&JobObject,
371 NULL);
372 if(NT_SUCCESS(Status))
373 {
374 Status = ((ProcessJob == JobObject) ? STATUS_PROCESS_IN_JOB : STATUS_PROCESS_NOT_IN_JOB);
375 ObDereferenceObject(JobObject);
376 }
377 }
378 }
379 else
380 {
381 /* the process is not assigned to any job */
382 Status = STATUS_PROCESS_NOT_IN_JOB;
383 }
384 ObDereferenceObject(Process);
385 }
386
387 return Status;
388 }
389
390
391 /*
392 * @implemented
393 */
394 NTSTATUS
395 NTAPI
396 NtOpenJobObject (
397 PHANDLE JobHandle,
398 ACCESS_MASK DesiredAccess,
399 POBJECT_ATTRIBUTES ObjectAttributes)
400 {
401 KPROCESSOR_MODE PreviousMode;
402 HANDLE hJob;
403 NTSTATUS Status;
404
405 PAGED_CODE();
406
407 PreviousMode = ExGetPreviousMode();
408
409 /* check for valid buffers */
410 if (PreviousMode != KernelMode)
411 {
412 _SEH2_TRY
413 {
414 ProbeForWriteHandle(JobHandle);
415 }
416 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
417 {
418 _SEH2_YIELD(return _SEH2_GetExceptionCode());
419 }
420 _SEH2_END;
421 }
422
423 Status = ObOpenObjectByName(ObjectAttributes,
424 PsJobType,
425 PreviousMode,
426 NULL,
427 DesiredAccess,
428 NULL,
429 &hJob);
430 if(NT_SUCCESS(Status))
431 {
432 _SEH2_TRY
433 {
434 *JobHandle = hJob;
435 }
436 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
437 {
438 Status = _SEH2_GetExceptionCode();
439 }
440 _SEH2_END;
441 }
442
443 return Status;
444 }
445
446
447 /*
448 * @unimplemented
449 */
450 NTSTATUS
451 NTAPI
452 NtQueryInformationJobObject (
453 HANDLE JobHandle,
454 JOBOBJECTINFOCLASS JobInformationClass,
455 PVOID JobInformation,
456 ULONG JobInformationLength,
457 PULONG ReturnLength )
458 {
459 UNIMPLEMENTED;
460 return STATUS_NOT_IMPLEMENTED;
461 }
462
463
464 /*
465 * @unimplemented
466 */
467 NTSTATUS
468 NTAPI
469 NtSetInformationJobObject (
470 HANDLE JobHandle,
471 JOBOBJECTINFOCLASS JobInformationClass,
472 PVOID JobInformation,
473 ULONG JobInformationLength)
474 {
475 UNIMPLEMENTED;
476 return STATUS_NOT_IMPLEMENTED;
477 }
478
479
480 /*
481 * @unimplemented
482 */
483 NTSTATUS
484 NTAPI
485 NtTerminateJobObject (
486 HANDLE JobHandle,
487 NTSTATUS ExitStatus )
488 {
489 KPROCESSOR_MODE PreviousMode;
490 PEJOB Job;
491 NTSTATUS Status;
492
493 PAGED_CODE();
494
495 PreviousMode = ExGetPreviousMode();
496
497 Status = ObReferenceObjectByHandle(
498 JobHandle,
499 JOB_OBJECT_TERMINATE,
500 PsJobType,
501 PreviousMode,
502 (PVOID*)&Job,
503 NULL);
504 if(NT_SUCCESS(Status))
505 {
506 Status = PspTerminateJobObject(
507 Job,
508 PreviousMode,
509 ExitStatus);
510 ObDereferenceObject(Job);
511 }
512
513 return Status;
514 }
515
516
517 /*
518 * @implemented
519 */
520 PVOID
521 NTAPI
522 PsGetJobLock ( PEJOB Job )
523 {
524 ASSERT(Job);
525 return (PVOID)&Job->JobLock;
526 }
527
528
529 /*
530 * @implemented
531 */
532 ULONG
533 NTAPI
534 PsGetJobSessionId ( PEJOB Job )
535 {
536 ASSERT(Job);
537 return Job->SessionId;
538 }
539
540
541 /*
542 * @implemented
543 */
544 ULONG
545 NTAPI
546 PsGetJobUIRestrictionsClass ( PEJOB Job )
547 {
548 ASSERT(Job);
549 return Job->UIRestrictionsClass;
550 }
551
552
553 /*
554 * @unimplemented
555 */
556 VOID
557 NTAPI
558 PsSetJobUIRestrictionsClass(PEJOB Job,
559 ULONG UIRestrictionsClass)
560 {
561 ASSERT(Job);
562 (void)InterlockedExchangeUL(&Job->UIRestrictionsClass, UIRestrictionsClass);
563 /* FIXME - walk through the job process list and update the restrictions? */
564 }
565
566 /* EOF */