[NTOSKRNL] Implement ObSetDirectoryDeviceMap
[reactos.git] / ntoskrnl / ob / devicemap.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/devicemap.c
5 * PURPOSE: Device map implementation
6 * PROGRAMMERS: Eric Kohl (eric.kohl@reactos.org)
7 * Alex Ionescu (alex.ionescu@reactos.org)
8 * Pierre Schweitzer (pierre@reactos.org)
9 */
10
11 /* INCLUDES ***************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16
17 ULONG ObpLUIDDeviceMapsDisabled;
18 ULONG ObpLUIDDeviceMapsEnabled;
19
20 /* PRIVATE FUNCTIONS ******************************************************/
21
22 NTSTATUS
23 NTAPI
24 ObSetDeviceMap(IN PEPROCESS Process,
25 IN HANDLE DirectoryHandle)
26 {
27 POBJECT_DIRECTORY DirectoryObject = NULL;
28 PDEVICE_MAP DeviceMap = NULL, NewDeviceMap = NULL, OldDeviceMap;
29 NTSTATUS Status;
30 PEPROCESS WorkProcess;
31 BOOLEAN MakePermanant = FALSE;
32
33 Status = ObReferenceObjectByHandle(DirectoryHandle,
34 DIRECTORY_TRAVERSE,
35 ObpDirectoryObjectType,
36 KeGetPreviousMode(),
37 (PVOID*)&DirectoryObject,
38 NULL);
39 if (!NT_SUCCESS(Status))
40 {
41 DPRINT("ObReferenceObjectByHandle() failed (Status 0x%08lx)\n", Status);
42 return Status;
43 }
44
45 /* Allocate and initialize a new device map */
46 DeviceMap = ExAllocatePoolWithTag(PagedPool,
47 sizeof(*DeviceMap),
48 'mDbO');
49 if (DeviceMap == NULL)
50 {
51 ObDereferenceObject(DirectoryObject);
52 return STATUS_INSUFFICIENT_RESOURCES;
53 }
54
55 /* Initialize the device map */
56 RtlZeroMemory(DeviceMap, sizeof(*DeviceMap));
57 DeviceMap->ReferenceCount = 1;
58 DeviceMap->DosDevicesDirectory = DirectoryObject;
59
60 /* Acquire the device map lock */
61 KeAcquireGuardedMutex(&ObpDeviceMapLock);
62
63 /* Attach the device map to the directory object */
64 if (DirectoryObject->DeviceMap == NULL)
65 {
66 DirectoryObject->DeviceMap = DeviceMap;
67 }
68 else
69 {
70 NewDeviceMap = DeviceMap;
71
72 /* There's already a device map,
73 so reuse it */
74 DeviceMap = DirectoryObject->DeviceMap;
75 ++DeviceMap->ReferenceCount;
76 }
77
78 /* Caller gave a process, use it */
79 if (Process != NULL)
80 {
81 WorkProcess = Process;
82 }
83 /* If no process given, use current and
84 * set system device map */
85 else
86 {
87 WorkProcess = PsGetCurrentProcess();
88 ObSystemDeviceMap = DeviceMap;
89 }
90
91 /* If current object isn't system one, save system one in current
92 * device map */
93 if (DirectoryObject != ObSystemDeviceMap->DosDevicesDirectory)
94 {
95 /* We also need to make the object permanant */
96 DeviceMap->GlobalDosDevicesDirectory = ObSystemDeviceMap->DosDevicesDirectory;
97 MakePermanant = TRUE;
98 }
99
100 /* Save old process device map */
101 OldDeviceMap = WorkProcess->DeviceMap;
102 /* Attach the device map to the process */
103 WorkProcess->DeviceMap = DeviceMap;
104
105 /* Release the device map lock */
106 KeReleaseGuardedMutex(&ObpDeviceMapLock);
107
108 /* If we have to make the object permamant, do it now */
109 if (MakePermanant)
110 {
111 POBJECT_HEADER ObjectHeader;
112 POBJECT_HEADER_NAME_INFO HeaderNameInfo;
113
114 ObjectHeader = OBJECT_TO_OBJECT_HEADER(DirectoryObject);
115 HeaderNameInfo = ObpReferenceNameInfo(ObjectHeader);
116
117 ObpEnterObjectTypeMutex(ObjectHeader->Type);
118 if (HeaderNameInfo != NULL && HeaderNameInfo->Directory != NULL)
119 {
120 ObjectHeader->Flags |= OB_FLAG_PERMANENT;
121 }
122 ObpLeaveObjectTypeMutex(ObjectHeader->Type);
123
124 if (HeaderNameInfo != NULL)
125 {
126 ObpDereferenceNameInfo(HeaderNameInfo);
127 }
128 }
129
130 /* Release useless device map if required */
131 if (NewDeviceMap != NULL)
132 {
133 ObfDereferenceObject(DirectoryObject);
134 ExFreePoolWithTag(NewDeviceMap, 'mDbO');
135 }
136
137 /* And dereference previous process device map */
138 if (OldDeviceMap != NULL)
139 {
140 ObfDereferenceDeviceMap(OldDeviceMap);
141 }
142
143 return STATUS_SUCCESS;
144 }
145
146
147 NTSTATUS
148 NTAPI
149 ObSetDirectoryDeviceMap(OUT PDEVICE_MAP * DeviceMap,
150 IN HANDLE DirectoryHandle)
151 {
152 POBJECT_DIRECTORY DirectoryObject = NULL;
153 PDEVICE_MAP LocalMap = NULL, NewDeviceMap = NULL;
154 NTSTATUS Status;
155 POBJECT_HEADER ObjectHeader;
156 POBJECT_HEADER_NAME_INFO HeaderNameInfo;
157
158 Status = ObReferenceObjectByHandle(DirectoryHandle,
159 DIRECTORY_TRAVERSE,
160 ObpDirectoryObjectType,
161 KernelMode,
162 (PVOID*)&DirectoryObject,
163 NULL);
164 if (!NT_SUCCESS(Status))
165 {
166 DPRINT("ObReferenceObjectByHandle() failed (Status 0x%08lx)\n", Status);
167 return Status;
168 }
169
170 /* Allocate and initialize a new device map */
171 LocalMap = ExAllocatePoolWithTag(PagedPool,
172 sizeof(*LocalMap),
173 'mDbO');
174 if (LocalMap == NULL)
175 {
176 ObDereferenceObject(DirectoryObject);
177 return STATUS_INSUFFICIENT_RESOURCES;
178 }
179
180 /* Initialize the device map */
181 RtlZeroMemory(LocalMap, sizeof(*LocalMap));
182 LocalMap->ReferenceCount = 1;
183 LocalMap->DosDevicesDirectory = DirectoryObject;
184
185 /* Acquire the device map lock */
186 KeAcquireGuardedMutex(&ObpDeviceMapLock);
187
188 /* Attach the device map to the directory object */
189 if (DirectoryObject->DeviceMap == NULL)
190 {
191 DirectoryObject->DeviceMap = LocalMap;
192 }
193 else
194 {
195 NewDeviceMap = LocalMap;
196
197 /* There's already a device map,
198 so reuse it */
199 LocalMap = DirectoryObject->DeviceMap;
200 ++LocalMap->ReferenceCount;
201 }
202
203 /* If current object isn't system one, save system one in current
204 * device map */
205 if (DirectoryObject != ObSystemDeviceMap->DosDevicesDirectory)
206 {
207 /* We also need to make the object permanant */
208 LocalMap->GlobalDosDevicesDirectory = ObSystemDeviceMap->DosDevicesDirectory;
209 }
210
211 /* Release the device map lock */
212 KeReleaseGuardedMutex(&ObpDeviceMapLock);
213
214 if (DeviceMap != NULL)
215 {
216 *DeviceMap = LocalMap;
217 }
218
219 /* Caller expects us to make the object permanant, so do it! */
220 ObjectHeader = OBJECT_TO_OBJECT_HEADER(DirectoryObject);
221 HeaderNameInfo = ObpReferenceNameInfo(ObjectHeader);
222
223 ObpEnterObjectTypeMutex(ObjectHeader->Type);
224 if (HeaderNameInfo != NULL && HeaderNameInfo->Directory != NULL)
225 {
226 ObjectHeader->Flags |= OB_FLAG_PERMANENT;
227 }
228 ObpLeaveObjectTypeMutex(ObjectHeader->Type);
229
230 if (HeaderNameInfo != NULL)
231 {
232 ObpDereferenceNameInfo(HeaderNameInfo);
233 }
234
235 /* Release useless device map if required */
236 if (NewDeviceMap != NULL)
237 {
238 ObfDereferenceObject(DirectoryObject);
239 ExFreePoolWithTag(NewDeviceMap, 'mDbO');
240 }
241
242 return Status;
243 }
244
245
246 NTSTATUS
247 NTAPI
248 ObpSetCurrentProcessDeviceMap(VOID)
249 {
250 PTOKEN Token;
251 LUID LogonId;
252 NTSTATUS Status;
253 PEPROCESS CurrentProcess;
254 LUID SystemLuid = SYSTEM_LUID;
255 PDEVICE_MAP DeviceMap, OldDeviceMap;
256
257 /* Get our impersonation token */
258 CurrentProcess = PsGetCurrentProcess();
259 Token = PsReferencePrimaryToken(CurrentProcess);
260 if (Token == NULL)
261 {
262 return STATUS_NO_TOKEN;
263 }
264
265 /* Query the Logon ID */
266 Status = SeQueryAuthenticationIdToken(Token, &LogonId);
267 if (!NT_SUCCESS(Status))
268 {
269 goto done;
270 }
271
272 /* If that's system, then use system device map */
273 if (RtlEqualLuid(&LogonId, &SystemLuid))
274 {
275 DeviceMap = ObSystemDeviceMap;
276 }
277 /* Otherwise ask Se for the device map */
278 else
279 {
280 Status = SeGetLogonIdDeviceMap(&LogonId, &DeviceMap);
281 if (!NT_SUCCESS(Status))
282 {
283 /* Normalize failure status */
284 Status = STATUS_OBJECT_PATH_INVALID;
285 goto done;
286 }
287 }
288
289 /* Fail if no device map */
290 if (DeviceMap == NULL)
291 {
292 Status = STATUS_OBJECT_PATH_INVALID;
293 goto done;
294 }
295
296 /* Acquire the device map lock */
297 KeAcquireGuardedMutex(&ObpDeviceMapLock);
298
299 /* Save old device map attached to the process */
300 OldDeviceMap = CurrentProcess->DeviceMap;
301
302 /* Set new device map & reference it */
303 ++DeviceMap->ReferenceCount;
304 CurrentProcess->DeviceMap = DeviceMap;
305
306 /* Release the device map lock */
307 KeReleaseGuardedMutex(&ObpDeviceMapLock);
308
309 /* If we had a device map, dereference it */
310 if (OldDeviceMap != NULL)
311 {
312 ObfDereferenceDeviceMap(OldDeviceMap);
313 }
314
315 done:
316 /* We're done with the token! */
317 ObDereferenceObject(Token);
318
319 return Status;
320 }
321
322
323 PDEVICE_MAP
324 NTAPI
325 ObpReferenceDeviceMap(VOID)
326 {
327 LUID LogonId;
328 NTSTATUS Status;
329 PTOKEN Token = NULL;
330 PDEVICE_MAP DeviceMap;
331 PETHREAD CurrentThread;
332 BOOLEAN LookingForSystem;
333 LUID SystemLuid = SYSTEM_LUID;
334 BOOLEAN CopyOnOpen, EffectiveOnly;
335 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
336
337 LookingForSystem = FALSE;
338
339 /* If LUID mapping is enable, try to get appropriate device map */
340 if (ObpLUIDDeviceMapsEnabled != 0)
341 {
342 /* In case of impersonation, we've got a bit of work to do */
343 CurrentThread = PsGetCurrentThread();
344 if (CurrentThread->ActiveImpersonationInfo)
345 {
346 /* Get impersonation token */
347 Token = PsReferenceImpersonationToken(CurrentThread,
348 &CopyOnOpen,
349 &EffectiveOnly,
350 &ImpersonationLevel);
351 /* Get logon LUID */
352 if (Token != NULL)
353 {
354 Status = SeQueryAuthenticationIdToken(Token, &LogonId);
355 }
356 else
357 {
358 /* Force failure */
359 Status = STATUS_NO_TOKEN;
360 }
361
362 /* If we got logon LUID */
363 if (NT_SUCCESS(Status))
364 {
365 /*
366 * Check it's not system, system is easy to handle,
367 * we just need to return ObSystemDeviceMap
368 */
369 if (!RtlEqualLuid(&LogonId, &SystemLuid))
370 {
371 /* Ask Se for the device map */
372 Status = SeGetLogonIdDeviceMap(&LogonId, &DeviceMap);
373 if (NT_SUCCESS(Status))
374 {
375 /* Acquire the device map lock */
376 KeAcquireGuardedMutex(&ObpDeviceMapLock);
377
378 /* Reference the device map if any */
379 if (DeviceMap != NULL)
380 {
381 ++DeviceMap->ReferenceCount;
382 }
383
384 /* Release the device map lock */
385 KeReleaseGuardedMutex(&ObpDeviceMapLock);
386
387 /* If we got the device map, we're done! */
388 if (DeviceMap != NULL)
389 {
390 ObDereferenceObject(Token);
391
392 return DeviceMap;
393 }
394 }
395 }
396 else
397 {
398 LookingForSystem = TRUE;
399 }
400 }
401 }
402
403 /*
404 * Fall back case of the LUID mapping, make sure there's a
405 * a device map attached to the current process
406 */
407 if (PsGetCurrentProcess()->DeviceMap == NULL &&
408 !NT_SUCCESS(ObpSetCurrentProcessDeviceMap()))
409 {
410 /* We may have failed after we got impersonation token */
411 if (Token != NULL)
412 {
413 ObDereferenceObject(Token);
414 }
415
416 return NULL;
417 }
418 }
419
420 /* Acquire the device map lock */
421 KeAcquireGuardedMutex(&ObpDeviceMapLock);
422
423 /* If we're looking for system map, use it */
424 if (LookingForSystem)
425 {
426 DeviceMap = ObSystemDeviceMap;
427 }
428 /* Otherwise, use current process device map */
429 else
430 {
431 DeviceMap = PsGetCurrentProcess()->DeviceMap;
432 }
433
434 /* If we got one, reference it */
435 if (DeviceMap != NULL)
436 {
437 ++DeviceMap->ReferenceCount;
438 }
439
440 /* Release the device map lock */
441 KeReleaseGuardedMutex(&ObpDeviceMapLock);
442
443 /* We may have impersonation token (if we failed in impersonation branch) */
444 if (Token != NULL)
445 {
446 ObDereferenceObject(Token);
447 }
448
449 /* Return the potentially found device map */
450 return DeviceMap;
451 }
452
453
454 VOID
455 NTAPI
456 ObDereferenceDeviceMap(IN PEPROCESS Process)
457 {
458 PDEVICE_MAP DeviceMap;
459
460 DPRINT("ObDereferenceDeviceMap()\n");
461
462 /* Get the pointer to this process devicemap and reset it
463 holding the device map lock */
464 KeAcquireGuardedMutex(&ObpDeviceMapLock);
465 DeviceMap = Process->DeviceMap;
466 Process->DeviceMap = NULL;
467 KeReleaseGuardedMutex(&ObpDeviceMapLock);
468
469 /* Continue only if there is a device map */
470 if (DeviceMap != NULL)
471 ObfDereferenceDeviceMap(DeviceMap);
472 }
473
474
475 VOID
476 FASTCALL
477 ObfDereferenceDeviceMap(IN PDEVICE_MAP DeviceMap)
478 {
479 DPRINT("ObfDereferenceDeviceMap()\n");
480
481 /* Acquire the device map lock */
482 KeAcquireGuardedMutex(&ObpDeviceMapLock);
483
484 /* Decrement the reference counter */
485 DeviceMap->ReferenceCount--;
486 DPRINT("ReferenceCount: %lu\n", DeviceMap->ReferenceCount);
487
488 /* Leave, if there are still references to this device map */
489 if (DeviceMap->ReferenceCount != 0)
490 {
491 /* Release the device map lock and leave */
492 KeReleaseGuardedMutex(&ObpDeviceMapLock);
493 return;
494 }
495
496 /* Nobody is referencing it anymore, unlink the DOS directory */
497 DeviceMap->DosDevicesDirectory->DeviceMap = NULL;
498
499 /* Release the devicemap lock */
500 KeReleaseGuardedMutex(&ObpDeviceMapLock);
501
502 /* Dereference the DOS Devices Directory and free the Device Map */
503 ObMakeTemporaryObject(DeviceMap->DosDevicesDirectory);
504 ObDereferenceObject(DeviceMap->DosDevicesDirectory);
505 ExFreePoolWithTag(DeviceMap, 'mDbO');
506 }
507
508
509 VOID
510 NTAPI
511 ObInheritDeviceMap(IN PEPROCESS Parent,
512 IN PEPROCESS Process)
513 {
514 PDEVICE_MAP DeviceMap;
515
516 DPRINT("ObInheritDeviceMap()\n");
517
518 /* Acquire the device map lock */
519 KeAcquireGuardedMutex(&ObpDeviceMapLock);
520
521 /* Get the parent process device map or the system device map */
522 DeviceMap = (Parent != NULL) ? Parent->DeviceMap : ObSystemDeviceMap;
523 if (DeviceMap != NULL)
524 {
525 /* Reference the device map and attach it to the new process */
526 DeviceMap->ReferenceCount++;
527 DPRINT("ReferenceCount: %lu\n", DeviceMap->ReferenceCount);
528
529 Process->DeviceMap = DeviceMap;
530 }
531
532 /* Release the device map lock */
533 KeReleaseGuardedMutex(&ObpDeviceMapLock);
534 }
535
536
537 NTSTATUS
538 NTAPI
539 ObQueryDeviceMapInformation(IN PEPROCESS Process,
540 IN PPROCESS_DEVICEMAP_INFORMATION DeviceMapInfo,
541 IN ULONG Flags)
542 {
543 PDEVICE_MAP DeviceMap = NULL, GlobalDeviceMap;
544 BOOLEAN Dereference;
545 PROCESS_DEVICEMAP_INFORMATION MapInfo;
546 ULONG BitMask, i;
547 BOOLEAN ReturnAny;
548 NTSTATUS Status;
549
550 /* Validate flags */
551 if (Flags & ~PROCESS_LUID_DOSDEVICES_ONLY)
552 {
553 return STATUS_INVALID_PARAMETER;
554 }
555
556 Dereference = FALSE;
557 /* Do we want to return anything? */
558 ReturnAny = ~Flags & PROCESS_LUID_DOSDEVICES_ONLY;
559
560 /* If LUID mappings are enabled... */
561 if (ObpLUIDDeviceMapsEnabled != 0)
562 {
563 /* Check for process parameter validness */
564 if (Process != NULL && Process != PsGetCurrentProcess())
565 {
566 return STATUS_INVALID_PARAMETER;
567 }
568
569 /* And get the device map */
570 DeviceMap = ObpReferenceDeviceMap();
571 }
572
573 /* Acquire the device map lock */
574 KeAcquireGuardedMutex(&ObpDeviceMapLock);
575
576 /*
577 * If we had a device map, if because of LUID mappings,
578 * we'll have to dereference it afterwards
579 */
580 if (DeviceMap != NULL)
581 {
582 Dereference = TRUE;
583 }
584 else
585 {
586 /* Get the process device map or the system device map */
587 DeviceMap = (Process != NULL) ? Process->DeviceMap : ObSystemDeviceMap;
588 }
589
590 /* Fail if no device map */
591 if (DeviceMap == NULL)
592 {
593 KeReleaseGuardedMutex(&ObpDeviceMapLock);
594 return STATUS_END_OF_FILE;
595 }
596
597 /* At that point, assume success */
598 Status = STATUS_SUCCESS;
599
600 /* Try to get the global device map if any */
601 GlobalDeviceMap = DeviceMap;
602 if (DeviceMap->GlobalDosDevicesDirectory != NULL)
603 {
604 if (DeviceMap->GlobalDosDevicesDirectory->DeviceMap != NULL)
605 {
606 GlobalDeviceMap = DeviceMap->GlobalDosDevicesDirectory->DeviceMap;
607 }
608 }
609
610 /* Now, setup our device map info, especially drive types */
611 MapInfo.Query.DriveMap = DeviceMap->DriveMap;
612 /* Browse every device */
613 for (i = 0, BitMask = 1; i < 32; ++i, BitMask *= 2)
614 {
615 /* Set the type given current device map */
616 MapInfo.Query.DriveType[i] = DeviceMap->DriveType[i];
617
618 /*
619 * If device is not existing and we're asked to return
620 * more than just LUID mapped, get the entry
621 * from global device map if not remote
622 */
623 if (!(MapInfo.Query.DriveMap & BitMask) && ReturnAny)
624 {
625 if (ObpLUIDDeviceMapsEnabled != 0 ||
626 (GlobalDeviceMap->DriveType[i] != DOSDEVICE_DRIVE_REMOTE &&
627 GlobalDeviceMap->DriveType[i] != DOSDEVICE_DRIVE_CALCULATE))
628 {
629 MapInfo.Query.DriveType[i] = GlobalDeviceMap->DriveType[i];
630 MapInfo.Query.DriveMap |= BitMask & GlobalDeviceMap->DriveMap;
631 }
632 }
633 }
634
635 /* Release the device map lock */
636 KeReleaseGuardedMutex(&ObpDeviceMapLock);
637
638 /* Dereference LUID device map */
639 if (Dereference)
640 {
641 ObfDereferenceDeviceMap(DeviceMap);
642 }
643
644 /* Copy back data */
645 _SEH2_TRY
646 {
647 RtlCopyMemory(DeviceMapInfo, &MapInfo, sizeof(PROCESS_DEVICEMAP_INFORMATION));
648 }
649 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
650 {
651 Status = _SEH2_GetExceptionCode();
652 }
653 _SEH2_END;
654
655 return Status;
656 }
657
658
659 ULONG
660 NTAPI
661 ObIsLUIDDeviceMapsEnabled(VOID)
662 {
663 return ObpLUIDDeviceMapsEnabled;
664 }
665
666
667 #if 0
668 NTSTATUS
669 NTAPI
670 ObIsDosDeviceLocallyMapped(
671 IN ULONG Index,
672 OUT PUCHAR DosDeviceState)
673 {
674 /* Check the index */
675 if (Index < 1 || Index > 26)
676 return STATUS_INVALID_PARAMETER;
677
678 /* Acquire the device map lock */
679 KeAcquireGuardedMutex(&ObpDeviceMapLock);
680
681 /* Get drive mapping status */
682 *DosDeviceState = (ObSystemDeviceMap->DriveMap & (1 << Index)) != 0;
683
684 /* Release the device map lock */
685 KeReleaseGuardedMutex(&ObpDeviceMapLock);
686
687 return STATUS_SUCCESS;
688 }
689 #endif
690
691 /* EOF */