[SHELL32]
[reactos.git] / ntoskrnl / ob / obname.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/obname.c
5 * PURPOSE: Manages all functions related to the Object Manager name-
6 * space, such as finding objects or querying their names.
7 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
8 * Eric Kohl
9 * Thomas Weidenmueller (w3seek@reactos.org)
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <debug.h>
17
18 BOOLEAN ObpCaseInsensitive = TRUE;
19 POBJECT_DIRECTORY ObpRootDirectoryObject;
20 POBJECT_DIRECTORY ObpTypeDirectoryObject;
21
22 /* DOS Device Prefix \??\ and \?? */
23 ALIGNEDNAME ObpDosDevicesShortNamePrefix = {{L'\\',L'?',L'?',L'\\'}};
24 ALIGNEDNAME ObpDosDevicesShortNameRoot = {{L'\\',L'?',L'?',L'\0'}};
25 UNICODE_STRING ObpDosDevicesShortName =
26 {
27 sizeof(ObpDosDevicesShortNamePrefix),
28 sizeof(ObpDosDevicesShortNamePrefix),
29 (PWSTR)&ObpDosDevicesShortNamePrefix
30 };
31
32 /* PRIVATE FUNCTIONS *********************************************************/
33
34 NTSTATUS
35 NTAPI
36 INIT_FUNCTION
37 ObpCreateDosDevicesDirectory(VOID)
38 {
39 OBJECT_ATTRIBUTES ObjectAttributes;
40 UNICODE_STRING RootName, TargetName, LinkName;
41 HANDLE Handle, SymHandle;
42 NTSTATUS Status;
43
44 /* Create the global DosDevices directory \?? */
45 RtlInitUnicodeString(&RootName, L"\\GLOBAL??");
46 InitializeObjectAttributes(&ObjectAttributes,
47 &RootName,
48 OBJ_PERMANENT,
49 NULL,
50 NULL);
51 Status = NtCreateDirectoryObject(&Handle,
52 DIRECTORY_ALL_ACCESS,
53 &ObjectAttributes);
54 if (!NT_SUCCESS(Status)) return Status;
55
56 /*********************************************\
57 |*** HACK until we support device mappings ***|
58 |*** Add a symlink \??\ <--> \GLOBAL??\ ***|
59 \*********************************************/
60 RtlInitUnicodeString(&LinkName, L"\\??");
61 InitializeObjectAttributes(&ObjectAttributes,
62 &LinkName,
63 OBJ_PERMANENT,
64 NULL,
65 NULL);
66 Status = NtCreateSymbolicLinkObject(&SymHandle,
67 SYMBOLIC_LINK_ALL_ACCESS,
68 &ObjectAttributes,
69 &RootName);
70 if (NT_SUCCESS(Status)) NtClose(SymHandle);
71 /*********************************************\
72 \*********************************************/
73
74 // FIXME: Create a device mapping for the global \?? directory
75
76 /*
77 * Initialize the \??\GLOBALROOT symbolic link
78 * pointing to the root directory \ .
79 */
80 RtlInitUnicodeString(&LinkName, L"GLOBALROOT");
81 RtlInitUnicodeString(&TargetName, L"");
82 InitializeObjectAttributes(&ObjectAttributes,
83 &LinkName,
84 OBJ_PERMANENT,
85 Handle,
86 NULL);
87 Status = NtCreateSymbolicLinkObject(&SymHandle,
88 SYMBOLIC_LINK_ALL_ACCESS,
89 &ObjectAttributes,
90 &TargetName);
91 if (NT_SUCCESS(Status)) NtClose(SymHandle);
92
93 /*
94 * Initialize the \??\Global symbolic link pointing to the global
95 * DosDevices directory \?? . It is used to access the global \??
96 * by user-mode components which, by default, use a per-session
97 * DosDevices directory.
98 */
99 RtlInitUnicodeString(&LinkName, L"Global");
100 InitializeObjectAttributes(&ObjectAttributes,
101 &LinkName,
102 OBJ_PERMANENT,
103 Handle,
104 NULL);
105 Status = NtCreateSymbolicLinkObject(&SymHandle,
106 SYMBOLIC_LINK_ALL_ACCESS,
107 &ObjectAttributes,
108 &RootName);
109 if (NT_SUCCESS(Status)) NtClose(SymHandle);
110
111 /* Close the directory handle */
112 NtClose(Handle);
113 if (!NT_SUCCESS(Status)) return Status;
114
115 /*
116 * Initialize the \DosDevices symbolic link pointing to the global
117 * DosDevices directory \?? , for backward compatibility with
118 * Windows NT-2000 systems.
119 */
120 RtlCreateUnicodeString(&LinkName, L"\\DosDevices");
121 RtlInitUnicodeString(&RootName, (PCWSTR)&ObpDosDevicesShortNameRoot);
122 InitializeObjectAttributes(&ObjectAttributes,
123 &LinkName,
124 OBJ_PERMANENT,
125 NULL,
126 NULL);
127 Status = NtCreateSymbolicLinkObject(&SymHandle,
128 SYMBOLIC_LINK_ALL_ACCESS,
129 &ObjectAttributes,
130 &RootName);
131 if (NT_SUCCESS(Status)) NtClose(SymHandle);
132
133 /* FIXME: Hack Hack! */
134 ObSystemDeviceMap = ExAllocatePoolWithTag(NonPagedPool,
135 sizeof(*ObSystemDeviceMap),
136 'mDbO');
137 if (!ObSystemDeviceMap) return STATUS_INSUFFICIENT_RESOURCES;
138 RtlZeroMemory(ObSystemDeviceMap, sizeof(*ObSystemDeviceMap));
139
140 /* Return status */
141 return Status;
142 }
143
144 VOID
145 NTAPI
146 ObDereferenceDeviceMap(IN PEPROCESS Process)
147 {
148 PDEVICE_MAP DeviceMap;
149
150 /* Get the pointer to this process devicemap and reset it
151 holding devicemap lock */
152 KeAcquireGuardedMutex(&ObpDeviceMapLock);
153 DeviceMap = Process->DeviceMap;
154 Process->DeviceMap = NULL;
155 KeReleaseGuardedMutex(&ObpDeviceMapLock);
156
157 /* Continue only if there is a devicemap to dereference */
158 if (DeviceMap)
159 {
160 KeAcquireGuardedMutex(&ObpDeviceMapLock);
161
162 /* Delete the device map link and dereference it */
163 if (--DeviceMap->ReferenceCount)
164 {
165 /* Nobody is referencing it anymore, unlink the DOS directory */
166 DeviceMap->DosDevicesDirectory->DeviceMap = NULL;
167
168 /* Release the devicemap lock */
169 KeReleaseGuardedMutex(&ObpDeviceMapLock);
170
171 /* Dereference the DOS Devices Directory and free the Device Map */
172 ObDereferenceObject(DeviceMap->DosDevicesDirectory);
173 ExFreePool(DeviceMap);
174 }
175 else
176 {
177 /* Release the devicemap lock */
178 KeReleaseGuardedMutex(&ObpDeviceMapLock);
179 }
180 }
181 }
182
183 VOID
184 NTAPI
185 ObInheritDeviceMap(IN PEPROCESS Parent,
186 IN PEPROCESS Process)
187 {
188 /* FIXME: Devicemap Support */
189 }
190
191 /*++
192 * @name ObpDeleteNameCheck
193 *
194 * The ObpDeleteNameCheck routine checks if a named object should be
195 * removed from the object directory namespace.
196 *
197 * @param Object
198 * Pointer to the object to check for possible removal.
199 *
200 * @return None.
201 *
202 * @remarks An object is removed if the following 4 criteria are met:
203 * 1) The object has 0 handles open
204 * 2) The object is in the directory namespace and has a name
205 * 3) The object is not permanent
206 *
207 *--*/
208 VOID
209 NTAPI
210 ObpDeleteNameCheck(IN PVOID Object)
211 {
212 POBJECT_HEADER ObjectHeader;
213 OBP_LOOKUP_CONTEXT Context;
214 POBJECT_HEADER_NAME_INFO ObjectNameInfo;
215 POBJECT_TYPE ObjectType;
216 PVOID Directory = NULL;
217
218 /* Get object structures */
219 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
220 ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader);
221 ObjectType = ObjectHeader->Type;
222
223 /*
224 * Check if the handle count is 0, if the object is named,
225 * and if the object isn't a permanent object.
226 */
227 if (!(ObjectHeader->HandleCount) &&
228 (ObjectNameInfo) &&
229 (ObjectNameInfo->Name.Length) &&
230 (ObjectNameInfo->Directory) &&
231 !(ObjectHeader->Flags & OB_FLAG_PERMANENT))
232 {
233 /* Setup a lookup context */
234 ObpInitializeLookupContext(&Context);
235
236 /* Lock the directory */
237 ObpAcquireDirectoryLockExclusive(ObjectNameInfo->Directory, &Context);
238
239 /* Do the lookup */
240 Object = ObpLookupEntryDirectory(ObjectNameInfo->Directory,
241 &ObjectNameInfo->Name,
242 0,
243 FALSE,
244 &Context);
245 if (Object)
246 {
247 /* Lock the object */
248 ObpAcquireObjectLock(ObjectHeader);
249
250 /* Make sure we can still delete the object */
251 if (!(ObjectHeader->HandleCount) &&
252 !(ObjectHeader->Flags & OB_FLAG_PERMANENT))
253 {
254 /* First delete it from the directory */
255 ObpDeleteEntryDirectory(&Context);
256
257 /* Check if this is a symbolic link */
258 if (ObjectType == ObSymbolicLinkType)
259 {
260 /* Remove internal name */
261 ObpDeleteSymbolicLinkName(Object);
262 }
263
264 /* Check if the magic protection flag is set */
265 ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
266 if ((ObjectNameInfo) &&
267 (ObjectNameInfo->QueryReferences & 0x40000000))
268 {
269 /* Remove protection flag */
270 InterlockedExchangeAdd((PLONG)&ObjectNameInfo->QueryReferences,
271 -0x40000000);
272 }
273
274 /* Get the directory */
275 Directory = ObjectNameInfo->Directory;
276 }
277
278 /* Release the lock */
279 ObpReleaseObjectLock(ObjectHeader);
280 }
281
282 /* Cleanup after lookup */
283 ObpReleaseLookupContext(&Context);
284
285 /* Remove another query reference since we added one on top */
286 ObpDereferenceNameInfo(ObjectNameInfo);
287
288 /* Check if we were inserted in a directory */
289 if (Directory)
290 {
291 /* We were, so first remove the extra reference we had added */
292 ObpDereferenceNameInfo(ObjectNameInfo);
293
294 /* Now dereference the object as well */
295 ObDereferenceObject(Object);
296 }
297 }
298 else
299 {
300 /* Remove the reference we added */
301 ObpDereferenceNameInfo(ObjectNameInfo);
302 }
303 }
304
305 NTSTATUS
306 NTAPI
307 ObpLookupObjectName(IN HANDLE RootHandle OPTIONAL,
308 IN PUNICODE_STRING ObjectName,
309 IN ULONG Attributes,
310 IN POBJECT_TYPE ObjectType,
311 IN KPROCESSOR_MODE AccessMode,
312 IN OUT PVOID ParseContext,
313 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
314 IN PVOID InsertObject OPTIONAL,
315 IN OUT PACCESS_STATE AccessState,
316 OUT POBP_LOOKUP_CONTEXT LookupContext,
317 OUT PVOID *FoundObject)
318 {
319 PVOID Object;
320 POBJECT_HEADER ObjectHeader;
321 UNICODE_STRING ComponentName, RemainingName;
322 BOOLEAN Reparse = FALSE, SymLink = FALSE;
323 POBJECT_DIRECTORY Directory = NULL, ParentDirectory = NULL, RootDirectory;
324 POBJECT_DIRECTORY ReferencedDirectory = NULL, ReferencedParentDirectory = NULL;
325 KIRQL CalloutIrql;
326 OB_PARSE_METHOD ParseRoutine;
327 NTSTATUS Status;
328 KPROCESSOR_MODE AccessCheckMode;
329 PWCHAR NewName;
330 POBJECT_HEADER_NAME_INFO ObjectNameInfo;
331 ULONG MaxReparse = 30;
332 PAGED_CODE();
333 OBTRACE(OB_NAMESPACE_DEBUG,
334 "%s - Finding Object: %wZ. Expecting: %p\n",
335 __FUNCTION__,
336 ObjectName,
337 InsertObject);
338
339 /* Initialize starting state */
340 ObpInitializeLookupContext(LookupContext);
341 *FoundObject = NULL;
342 Status = STATUS_SUCCESS;
343 Object = NULL;
344
345 /* Check if case-insensitivity is checked */
346 if (ObpCaseInsensitive)
347 {
348 /* Check if the object type requests this */
349 if (!(ObjectType) || (ObjectType->TypeInfo.CaseInsensitive))
350 {
351 /* Add the flag to disable case sensitivity */
352 Attributes |= OBJ_CASE_INSENSITIVE;
353 }
354 }
355
356 /* Check if this is a access checks are being forced */
357 AccessCheckMode = (Attributes & OBJ_FORCE_ACCESS_CHECK) ?
358 UserMode : AccessMode;
359
360 /* Check if we got a Root Directory */
361 if (RootHandle)
362 {
363 /* We did. Reference it */
364 Status = ObReferenceObjectByHandle(RootHandle,
365 0,
366 NULL,
367 AccessMode,
368 (PVOID*)&RootDirectory,
369 NULL);
370 if (!NT_SUCCESS(Status)) return Status;
371
372 /* Get the header */
373 ObjectHeader = OBJECT_TO_OBJECT_HEADER(RootDirectory);
374
375 /* The name cannot start with a separator, unless this is a file */
376 if ((ObjectName->Buffer) &&
377 (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) &&
378 (ObjectHeader->Type != IoFileObjectType))
379 {
380 /* The syntax is bad, so fail this request */
381 ObDereferenceObject(RootDirectory);
382 return STATUS_OBJECT_PATH_SYNTAX_BAD;
383 }
384
385 /* Don't parse a Directory */
386 if (ObjectHeader->Type != ObDirectoryType)
387 {
388 /* Make sure the Object Type has a parse routine */
389 ParseRoutine = ObjectHeader->Type->TypeInfo.ParseProcedure;
390 if (!ParseRoutine)
391 {
392 /* We can't parse a name if we don't have a parse routine */
393 ObDereferenceObject(RootDirectory);
394 return STATUS_INVALID_HANDLE;
395 }
396
397 /* Set default parse count */
398 MaxReparse = 30;
399
400 /* Now parse */
401 while (TRUE)
402 {
403 /* Start with the full name */
404 RemainingName = *ObjectName;
405
406 /* Call the Parse Procedure */
407 ObpCalloutStart(&CalloutIrql);
408 Status = ParseRoutine(RootDirectory,
409 ObjectType,
410 AccessState,
411 AccessCheckMode,
412 Attributes,
413 ObjectName,
414 &RemainingName,
415 ParseContext,
416 SecurityQos,
417 &Object);
418 ObpCalloutEnd(CalloutIrql, "Parse", ObjectHeader->Type, Object);
419
420 /* Check for success or failure, so not reparse */
421 if ((Status != STATUS_REPARSE) &&
422 (Status != STATUS_REPARSE_OBJECT))
423 {
424 /* Check for failure */
425 if (!NT_SUCCESS(Status))
426 {
427 /* Parse routine might not have cleared this, do it */
428 Object = NULL;
429 }
430 else if (!Object)
431 {
432 /* Modify status to reflect failure inside Ob */
433 Status = STATUS_OBJECT_NAME_NOT_FOUND;
434 }
435
436 /* We're done, return the status and object */
437 *FoundObject = Object;
438 ObDereferenceObject(RootDirectory);
439 return Status;
440 }
441 else if ((!ObjectName->Length) ||
442 (!ObjectName->Buffer) ||
443 (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
444 {
445 /* Reparsed to the root directory, so start over */
446 ObDereferenceObject(RootDirectory);
447 RootDirectory = ObpRootDirectoryObject;
448
449 /* Don't use this anymore, since we're starting at root */
450 RootHandle = NULL;
451 goto ParseFromRoot;
452 }
453 else if (--MaxReparse)
454 {
455 /* Try reparsing again */
456 continue;
457 }
458 else
459 {
460 /* Reparsed too many times */
461 ObDereferenceObject(RootDirectory);
462
463 /* Return the object and normalized status */
464 *FoundObject = Object;
465 if (!Object) Status = STATUS_OBJECT_NAME_NOT_FOUND;
466 return Status;
467 }
468 }
469 }
470 else if (!(ObjectName->Length) || !(ObjectName->Buffer))
471 {
472 /* Just return the Root Directory if we didn't get a name */
473 Status = ObReferenceObjectByPointer(RootDirectory,
474 0,
475 ObjectType,
476 AccessMode);
477 if (NT_SUCCESS(Status)) Object = RootDirectory;
478
479 /* Remove the first reference we added and return the object */
480 ObDereferenceObject(RootDirectory);
481 *FoundObject = Object;
482 return Status;
483 }
484 }
485 else
486 {
487 /* We did not get a Root Directory, so use the root */
488 RootDirectory = ObpRootDirectoryObject;
489
490 /* It must start with a path separator */
491 if (!(ObjectName->Length) ||
492 !(ObjectName->Buffer) ||
493 (ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR))
494 {
495 /* This name is invalid, so fail */
496 return STATUS_OBJECT_PATH_SYNTAX_BAD;
497 }
498
499 /* Check if the name is only the path separator */
500 if (ObjectName->Length == sizeof(OBJ_NAME_PATH_SEPARATOR))
501 {
502 /* So the caller only wants the root directory; do we have one? */
503 if (!RootDirectory)
504 {
505 /* This must be the first time we're creating it... right? */
506 if (InsertObject)
507 {
508 /* Yes, so return it to ObInsert so that it can create it */
509 Status = ObReferenceObjectByPointer(InsertObject,
510 0,
511 ObjectType,
512 AccessMode);
513 if (NT_SUCCESS(Status)) *FoundObject = InsertObject;
514 return Status;
515 }
516 else
517 {
518 /* This should never really happen */
519 ASSERT(FALSE);
520 return STATUS_INVALID_PARAMETER;
521 }
522 }
523 else
524 {
525 /* We do have the root directory, so just return it */
526 Status = ObReferenceObjectByPointer(RootDirectory,
527 0,
528 ObjectType,
529 AccessMode);
530 if (NT_SUCCESS(Status)) *FoundObject = RootDirectory;
531 return Status;
532 }
533 }
534 else
535 {
536 ParseFromRoot:
537 /* FIXME: Check if we have a device map */
538
539 /* Check if this is a possible DOS name */
540 if (!((ULONG_PTR)(ObjectName->Buffer) & 7))
541 {
542 /*
543 * This could be one. Does it match the prefix?
544 * Note that as an optimization, the match is done as 64-bit
545 * compare since the prefix is "\??\" which is exactly 8 bytes.
546 *
547 * In the second branch, we test for "\??" which is also valid.
548 * This time, we use a 32-bit compare followed by a Unicode
549 * character compare (16-bit), since the sum is 6 bytes.
550 */
551 if ((ObjectName->Length >= ObpDosDevicesShortName.Length) &&
552 (*(PULONGLONG)(ObjectName->Buffer) ==
553 ObpDosDevicesShortNamePrefix.Alignment.QuadPart))
554 {
555 /* FIXME! */
556 }
557 else if ((ObjectName->Length == ObpDosDevicesShortName.Length -
558 sizeof(WCHAR)) &&
559 (*(PULONG)(ObjectName->Buffer) ==
560 ObpDosDevicesShortNameRoot.Alignment.LowPart) &&
561 (*((PWCHAR)(ObjectName->Buffer) + 2) ==
562 (WCHAR)(ObpDosDevicesShortNameRoot.Alignment.HighPart)))
563 {
564 /* FIXME! */
565 }
566 }
567 }
568 }
569
570 /* Check if we were reparsing a symbolic link */
571 if (!SymLink)
572 {
573 /* Allow reparse */
574 Reparse = TRUE;
575 MaxReparse = 30;
576 }
577
578 /* Reparse */
579 while (Reparse && MaxReparse)
580 {
581 /* Get the name */
582 RemainingName = *ObjectName;
583
584 /* Disable reparsing again */
585 Reparse = FALSE;
586
587 /* Start parse loop */
588 while (TRUE)
589 {
590 /* Clear object */
591 Object = NULL;
592
593 /* Check if the name starts with a path separator */
594 if ((RemainingName.Length) &&
595 (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
596 {
597 /* Skip the path separator */
598 RemainingName.Buffer++;
599 RemainingName.Length -= sizeof(OBJ_NAME_PATH_SEPARATOR);
600 }
601
602 /* Find the next Part Name */
603 ComponentName = RemainingName;
604 while (RemainingName.Length)
605 {
606 /* Break if we found the \ ending */
607 if (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) break;
608
609 /* Move on */
610 RemainingName.Buffer++;
611 RemainingName.Length -= sizeof(OBJ_NAME_PATH_SEPARATOR);
612 }
613
614 /* Get its size and make sure it's valid */
615 ComponentName.Length -= RemainingName.Length;
616 if (!ComponentName.Length)
617 {
618 /* Invalid size, fail */
619 Status = STATUS_OBJECT_NAME_INVALID;
620 break;
621 }
622
623 /* Check if we're in the root */
624 if (!Directory) Directory = RootDirectory;
625
626 /* Check if this is a user-mode call that needs to traverse */
627 if ((AccessCheckMode != KernelMode) &&
628 !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE))
629 {
630 /* We shouldn't have referenced a directory yet */
631 ASSERT(ReferencedDirectory == NULL);
632
633 /* Reference the directory */
634 ObReferenceObject(Directory);
635 ReferencedDirectory = Directory;
636
637 /* Check if we have a parent directory */
638 if (ParentDirectory)
639 {
640 /* Check for traverse access */
641 if (!ObpCheckTraverseAccess(ParentDirectory,
642 DIRECTORY_TRAVERSE,
643 AccessState,
644 FALSE,
645 AccessCheckMode,
646 &Status))
647 {
648 /* We don't have it, fail */
649 break;
650 }
651 }
652 }
653
654 /* Check if we don't have a remaining name yet */
655 if (!RemainingName.Length)
656 {
657 /* Check if we don't have a referenced directory yet */
658 if (!ReferencedDirectory)
659 {
660 /* Reference it */
661 ObReferenceObject(Directory);
662 ReferencedDirectory = Directory;
663 }
664
665 /* Check if we are inserting an object */
666 if (InsertObject)
667 {
668 /* Lock the directory */
669 ObpAcquireDirectoryLockExclusive(Directory, LookupContext);
670 }
671 }
672
673 /* Do the lookup */
674 Object = ObpLookupEntryDirectory(Directory,
675 &ComponentName,
676 Attributes,
677 InsertObject ? FALSE : TRUE,
678 LookupContext);
679 if (!Object)
680 {
681 /* We didn't find it... do we still have a path? */
682 if (RemainingName.Length)
683 {
684 /* Then tell the caller the path wasn't found */
685 Status = STATUS_OBJECT_PATH_NOT_FOUND;
686 break;
687 }
688 else if (!InsertObject)
689 {
690 /* Otherwise, we have a path, but the name isn't valid */
691 Status = STATUS_OBJECT_NAME_NOT_FOUND;
692 break;
693 }
694
695 /* Check create access for the object */
696 if (!ObCheckCreateObjectAccess(Directory,
697 ObjectType == ObDirectoryType ?
698 DIRECTORY_CREATE_SUBDIRECTORY :
699 DIRECTORY_CREATE_OBJECT,
700 AccessState,
701 &ComponentName,
702 FALSE,
703 AccessCheckMode,
704 &Status))
705 {
706 /* We don't have create access, fail */
707 break;
708 }
709
710 /* Get the object header */
711 ObjectHeader = OBJECT_TO_OBJECT_HEADER(InsertObject);
712
713 /* FIXME: Check if this is a Section Object or Sym Link */
714 /* FIXME: If it is, then check if this isn't session 0 */
715 /* FIXME: If it isn't, check for SeCreateGlobalPrivilege */
716 /* FIXME: If privilege isn't there, check for unsecure name */
717 /* FIXME: If it isn't a known unsecure name, then fail */
718
719 /* Create Object Name */
720 NewName = ExAllocatePoolWithTag(PagedPool,
721 ComponentName.Length,
722 OB_NAME_TAG);
723 if (!(NewName) ||
724 !(ObpInsertEntryDirectory(Directory,
725 LookupContext,
726 ObjectHeader)))
727 {
728 /* Either couldn't allocate the name, or insert failed */
729 if (NewName) ExFreePoolWithTag(NewName, OB_NAME_TAG);
730
731 /* Fail due to memory reasons */
732 Status = STATUS_INSUFFICIENT_RESOURCES;
733 break;
734 }
735
736 /* Reference newly to be inserted object */
737 ObReferenceObject(InsertObject);
738
739 /* Get the name information */
740 ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
741
742 /* Reference the directory */
743 ObReferenceObject(Directory);
744
745 /* Copy the Name */
746 RtlCopyMemory(NewName,
747 ComponentName.Buffer,
748 ComponentName.Length);
749
750 /* Check if we had an old name */
751 if (ObjectNameInfo->Name.Buffer)
752 {
753 /* Free it */
754 ExFreePoolWithTag(ObjectNameInfo->Name.Buffer, OB_NAME_TAG);
755 }
756
757 /* Write new one */
758 ObjectNameInfo->Name.Buffer = NewName;
759 ObjectNameInfo->Name.Length = ComponentName.Length;
760 ObjectNameInfo->Name.MaximumLength = ComponentName.Length;
761
762 /* Return Status and the Expected Object */
763 Status = STATUS_SUCCESS;
764 Object = InsertObject;
765
766 /* Get out of here */
767 break;
768 }
769
770 ReparseObject:
771 /* We found it, so now get its header */
772 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
773
774 /*
775 * Check for a parse Procedure, but don't bother to parse for an insert
776 * unless it's a Symbolic Link, in which case we MUST parse
777 */
778 ParseRoutine = ObjectHeader->Type->TypeInfo.ParseProcedure;
779 if ((ParseRoutine) &&
780 (!(InsertObject) || (ParseRoutine == ObpParseSymbolicLink)))
781 {
782 /* Use the Root Directory next time */
783 Directory = NULL;
784
785 /* Increment the pointer count */
786 InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1);
787
788 /* Cleanup from the first lookup */
789 ObpReleaseLookupContext(LookupContext);
790
791 /* Check if we have a referenced directory */
792 if (ReferencedDirectory)
793 {
794 /* We do, dereference it */
795 ObDereferenceObject(ReferencedDirectory);
796 ReferencedDirectory = NULL;
797 }
798
799 /* Check if we have a referenced parent directory */
800 if (ReferencedParentDirectory)
801 {
802 /* We do, dereference it */
803 ObDereferenceObject(ReferencedParentDirectory);
804 ReferencedParentDirectory = NULL;
805 }
806
807 /* Call the Parse Procedure */
808 ObpCalloutStart(&CalloutIrql);
809 Status = ParseRoutine(Object,
810 ObjectType,
811 AccessState,
812 AccessCheckMode,
813 Attributes,
814 ObjectName,
815 &RemainingName,
816 ParseContext,
817 SecurityQos,
818 &Object);
819 ObpCalloutEnd(CalloutIrql, "Parse", ObjectHeader->Type, Object);
820
821 /* Remove our extra reference */
822 ObDereferenceObject(&ObjectHeader->Body);
823
824 /* Check if we have to reparse */
825 if ((Status == STATUS_REPARSE) ||
826 (Status == STATUS_REPARSE_OBJECT))
827 {
828 /* Reparse again */
829 Reparse = TRUE;
830 --MaxReparse;
831 if (MaxReparse == 0)
832 {
833 Object = NULL;
834 break;
835 }
836
837 /* Start over from root if we got sent back there */
838 if ((Status == STATUS_REPARSE_OBJECT) ||
839 (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
840 {
841 /* Check if we got a root directory */
842 if (RootHandle)
843 {
844 /* Stop using it, because we have a new directory now */
845 ObDereferenceObject(RootDirectory);
846 RootHandle = NULL;
847 }
848
849 /* Start at Root */
850 ParentDirectory = NULL;
851 RootDirectory = ObpRootDirectoryObject;
852
853 /* Check for reparse status */
854 if (Status == STATUS_REPARSE_OBJECT)
855 {
856 /* Don't reparse again */
857 Reparse = FALSE;
858
859 /* Did we actually get an object to which to reparse? */
860 if (!Object)
861 {
862 /* We didn't, so set a failure status */
863 Status = STATUS_OBJECT_NAME_NOT_FOUND;
864 }
865 else
866 {
867 /* We did, so we're free to parse the new object */
868 goto ReparseObject;
869 }
870 }
871 else
872 {
873 /* This is a symbolic link */
874 SymLink = TRUE;
875 goto ParseFromRoot;
876 }
877 }
878 else if (RootDirectory == ObpRootDirectoryObject)
879 {
880 /* We got STATUS_REPARSE but are at the Root Directory */
881 Object = NULL;
882 Status = STATUS_OBJECT_NAME_NOT_FOUND;
883 Reparse = FALSE;
884 }
885 }
886 else if (!NT_SUCCESS(Status))
887 {
888 /* Total failure */
889 Object = NULL;
890 }
891 else if (!Object)
892 {
893 /* We didn't reparse but we didn't find the Object Either */
894 Status = STATUS_OBJECT_NAME_NOT_FOUND;
895 }
896
897 /* Break out of the loop */
898 break;
899 }
900 else
901 {
902 /* No parse routine...do we still have a remaining name? */
903 if (!RemainingName.Length)
904 {
905 /* Are we creating an object? */
906 if (!InsertObject)
907 {
908 /* Check if this is a user-mode call that needs to traverse */
909 if ((AccessCheckMode != KernelMode) &&
910 !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE))
911 {
912 /* Check if we can get it */
913 if (!ObpCheckTraverseAccess(Directory,
914 DIRECTORY_TRAVERSE,
915 AccessState,
916 FALSE,
917 AccessCheckMode,
918 &Status))
919 {
920 /* We don't have access, fail */
921 Object = NULL;
922 break;
923 }
924 }
925
926 /* Reference the Object */
927 Status = ObReferenceObjectByPointer(Object,
928 0,
929 ObjectType,
930 AccessMode);
931 if (!NT_SUCCESS(Status)) Object = NULL;
932 }
933
934 /* And get out of the reparse loop */
935 break;
936 }
937 else
938 {
939 /* We still have a name; check if this is a directory object */
940 if (ObjectHeader->Type == ObDirectoryType)
941 {
942 /* Check if we have a referenced parent directory */
943 if (ReferencedParentDirectory)
944 {
945 /* Dereference it */
946 ObDereferenceObject(ReferencedParentDirectory);
947 }
948
949 /* Restart the lookup from this directory */
950 ReferencedParentDirectory = ReferencedDirectory;
951 ParentDirectory = Directory;
952 Directory = Object;
953 ReferencedDirectory = NULL;
954 }
955 else
956 {
957 /* We still have a name, but no parse routine for it */
958 Status = STATUS_OBJECT_TYPE_MISMATCH;
959 Object = NULL;
960 break;
961 }
962 }
963 }
964 }
965 }
966
967 /* Check if we failed */
968 if (!NT_SUCCESS(Status))
969 {
970 /* Cleanup after lookup */
971 ObpReleaseLookupContext(LookupContext);
972 }
973
974 /* Check if we have a device map and dereference it if so */
975 //if (DeviceMap) ObfDereferenceDeviceMap(DeviceMap);
976
977 /* Check if we have a referenced directory and dereference it if so */
978 if (ReferencedDirectory) ObDereferenceObject(ReferencedDirectory);
979
980 /* Check if we have a referenced parent directory */
981 if (ReferencedParentDirectory)
982 {
983 /* We do, dereference it */
984 ObDereferenceObject(ReferencedParentDirectory);
985 }
986
987 /* Set the found object and check if we got one */
988 *FoundObject = Object;
989 if (!Object)
990 {
991 /* Nothing was found. Did we reparse or get success? */
992 if ((Status == STATUS_REPARSE) || (NT_SUCCESS(Status)))
993 {
994 /* Set correct failure */
995 Status = STATUS_OBJECT_NAME_NOT_FOUND;
996 }
997 }
998
999 /* Check if we had a root directory */
1000 if (RootHandle) ObDereferenceObject(RootDirectory);
1001
1002 /* Return status to caller */
1003 OBTRACE(OB_NAMESPACE_DEBUG,
1004 "%s - Found Object: %p. Expected: %p\n",
1005 __FUNCTION__,
1006 *FoundObject,
1007 InsertObject);
1008 return Status;
1009 }
1010
1011 /* PUBLIC FUNCTIONS *********************************************************/
1012
1013 NTSTATUS
1014 NTAPI
1015 ObQueryNameString(IN PVOID Object,
1016 OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
1017 IN ULONG Length,
1018 OUT PULONG ReturnLength)
1019 {
1020 POBJECT_HEADER_NAME_INFO LocalInfo;
1021 POBJECT_HEADER ObjectHeader;
1022 POBJECT_DIRECTORY ParentDirectory;
1023 ULONG NameSize;
1024 PWCH ObjectName;
1025 BOOLEAN ObjectIsNamed;
1026 NTSTATUS Status = STATUS_SUCCESS;
1027
1028 /* Get the Kernel Meta-Structures */
1029 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
1030 LocalInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
1031
1032 /* Check if a Query Name Procedure is available */
1033 if (ObjectHeader->Type->TypeInfo.QueryNameProcedure)
1034 {
1035 /* Call the procedure inside SEH */
1036 ObjectIsNamed = ((LocalInfo) && (LocalInfo->Name.Length > 0));
1037
1038 _SEH2_TRY
1039 {
1040 Status = ObjectHeader->Type->TypeInfo.QueryNameProcedure(Object,
1041 ObjectIsNamed,
1042 ObjectNameInfo,
1043 Length,
1044 ReturnLength,
1045 KernelMode);
1046 }
1047 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1048 {
1049 /* Return the exception code */
1050 Status = _SEH2_GetExceptionCode();
1051 }
1052 _SEH2_END;
1053
1054 return Status;
1055 }
1056
1057 /* Check if the object doesn't even have a name */
1058 if (!(LocalInfo) || !(LocalInfo->Name.Buffer))
1059 {
1060 Status = STATUS_SUCCESS;
1061
1062 _SEH2_TRY
1063 {
1064 /* We're returning the name structure */
1065 *ReturnLength = sizeof(OBJECT_NAME_INFORMATION);
1066
1067 /* Check if we were given enough space */
1068 if (*ReturnLength > Length)
1069 {
1070 Status = STATUS_INFO_LENGTH_MISMATCH;
1071 }
1072 else
1073 {
1074 /* Return an empty buffer */
1075 RtlInitEmptyUnicodeString(&ObjectNameInfo->Name, NULL, 0);
1076 }
1077 }
1078 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1079 {
1080 /* Return the exception code */
1081 Status = _SEH2_GetExceptionCode();
1082 }
1083 _SEH2_END;
1084
1085 return Status;
1086 }
1087
1088 /*
1089 * Find the size needed for the name. We won't do
1090 * this during the Name Creation loop because we want
1091 * to let the caller know that the buffer isn't big
1092 * enough right at the beginning, not work our way through
1093 * and find out at the end
1094 */
1095 _SEH2_TRY
1096 {
1097 if (Object == ObpRootDirectoryObject)
1098 {
1099 /* Size of the '\' string */
1100 NameSize = sizeof(OBJ_NAME_PATH_SEPARATOR);
1101 }
1102 else
1103 {
1104 /* Get the Object Directory and add name of Object */
1105 ParentDirectory = LocalInfo->Directory;
1106 NameSize = sizeof(OBJ_NAME_PATH_SEPARATOR) + LocalInfo->Name.Length;
1107
1108 /* Loop inside the directory to get the top-most one (meaning root) */
1109 while ((ParentDirectory != ObpRootDirectoryObject) && (ParentDirectory))
1110 {
1111 /* Get the Name Information */
1112 LocalInfo = OBJECT_HEADER_TO_NAME_INFO(
1113 OBJECT_TO_OBJECT_HEADER(ParentDirectory));
1114
1115 /* Add the size of the Directory Name */
1116 if (LocalInfo && LocalInfo->Directory)
1117 {
1118 /* Size of the '\' string + Directory Name */
1119 NameSize += sizeof(OBJ_NAME_PATH_SEPARATOR) +
1120 LocalInfo->Name.Length;
1121
1122 /* Move to next parent Directory */
1123 ParentDirectory = LocalInfo->Directory;
1124 }
1125 else
1126 {
1127 /* Directory with no name. We append "...\" */
1128 NameSize += sizeof(L"...") + sizeof(OBJ_NAME_PATH_SEPARATOR);
1129 break;
1130 }
1131 }
1132 }
1133
1134 /* Finally, add the name of the structure and the null char */
1135 *ReturnLength = NameSize +
1136 sizeof(OBJECT_NAME_INFORMATION) +
1137 sizeof(UNICODE_NULL);
1138
1139 /* Check if we were given enough space */
1140 if (*ReturnLength > Length) _SEH2_YIELD(return STATUS_INFO_LENGTH_MISMATCH);
1141
1142 /*
1143 * Now we will actually create the name. We work backwards because
1144 * it's easier to start off from the Name we have and walk up the
1145 * parent directories. We use the same logic as Name Length calculation.
1146 */
1147 LocalInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
1148 ObjectName = (PWCH)((ULONG_PTR)ObjectNameInfo + *ReturnLength);
1149 *--ObjectName = UNICODE_NULL;
1150
1151 /* Check if the object is actually the Root directory */
1152 if (Object == ObpRootDirectoryObject)
1153 {
1154 /* This is already the Root Directory, return "\\" */
1155 *--ObjectName = OBJ_NAME_PATH_SEPARATOR;
1156 ObjectNameInfo->Name.Length = (USHORT)NameSize;
1157 ObjectNameInfo->Name.MaximumLength = (USHORT)(NameSize +
1158 sizeof(UNICODE_NULL));
1159 ObjectNameInfo->Name.Buffer = ObjectName;
1160 Status = STATUS_SUCCESS;
1161 }
1162 else
1163 {
1164 /* Start by adding the Object's Name */
1165 ObjectName = (PWCH)((ULONG_PTR)ObjectName -
1166 LocalInfo->Name.Length);
1167 RtlCopyMemory(ObjectName,
1168 LocalInfo->Name.Buffer,
1169 LocalInfo->Name.Length);
1170
1171 /* Now parse the Parent directories until we reach the top */
1172 ParentDirectory = LocalInfo->Directory;
1173 while ((ParentDirectory != ObpRootDirectoryObject) && (ParentDirectory))
1174 {
1175 /* Get the name information */
1176 LocalInfo = OBJECT_HEADER_TO_NAME_INFO(
1177 OBJECT_TO_OBJECT_HEADER(ParentDirectory));
1178
1179 /* Add the "\" */
1180 *(--ObjectName) = OBJ_NAME_PATH_SEPARATOR;
1181
1182 /* Add the Parent Directory's Name */
1183 if (LocalInfo && LocalInfo->Name.Buffer)
1184 {
1185 /* Add the name */
1186 ObjectName = (PWCH)((ULONG_PTR)ObjectName -
1187 LocalInfo->Name.Length);
1188 RtlCopyMemory(ObjectName,
1189 LocalInfo->Name.Buffer,
1190 LocalInfo->Name.Length);
1191
1192 /* Move to next parent */
1193 ParentDirectory = LocalInfo->Directory;
1194 }
1195 else
1196 {
1197 /* Directory without a name, we add "..." */
1198 ObjectName = (PWCH)((ULONG_PTR)ObjectName -
1199 sizeof(L"...") +
1200 sizeof(UNICODE_NULL));
1201 RtlCopyMemory(ObjectName, L"...", sizeof(L"..."));
1202 break;
1203 }
1204 }
1205
1206 /* Add Root Directory Name */
1207 *(--ObjectName) = OBJ_NAME_PATH_SEPARATOR;
1208 ObjectNameInfo->Name.Length = (USHORT)NameSize;
1209 ObjectNameInfo->Name.MaximumLength =
1210 (USHORT)(NameSize + sizeof(UNICODE_NULL));
1211 ObjectNameInfo->Name.Buffer = ObjectName;
1212 }
1213 }
1214 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1215 {
1216 /* Return the exception code */
1217 Status = _SEH2_GetExceptionCode();
1218 }
1219 _SEH2_END;
1220
1221 /* Return success */
1222 return Status;
1223 }
1224
1225 VOID
1226 NTAPI
1227 ObQueryDeviceMapInformation(IN PEPROCESS Process,
1228 IN PPROCESS_DEVICEMAP_INFORMATION DeviceMapInfo)
1229 {
1230 /*
1231 * FIXME: This is an ugly hack for now, to always return the System Device Map
1232 * instead of returning the Process Device Map. Not important yet since we don't use it
1233 */
1234
1235 KeAcquireGuardedMutex(&ObpDeviceMapLock);
1236
1237 /* Make a copy */
1238 DeviceMapInfo->Query.DriveMap = ObSystemDeviceMap->DriveMap;
1239 RtlCopyMemory(DeviceMapInfo->Query.DriveType,
1240 ObSystemDeviceMap->DriveType,
1241 sizeof(ObSystemDeviceMap->DriveType));
1242
1243 KeReleaseGuardedMutex(&ObpDeviceMapLock);
1244 }
1245
1246 NTSTATUS
1247 NTAPI
1248 ObIsDosDeviceLocallyMapped(
1249 IN ULONG Index,
1250 OUT PUCHAR DosDeviceState)
1251 {
1252 /* check parameters */
1253 if (Index < 1 || Index > 26)
1254 {
1255 /* invalid index */
1256 return STATUS_INVALID_PARAMETER;
1257 }
1258
1259 /* acquire lock */
1260 KeAcquireGuardedMutex(&ObpDeviceMapLock);
1261
1262 /* get drive mapping status */
1263 *DosDeviceState = (ObSystemDeviceMap->DriveMap & (1 << Index)) != 0;
1264
1265 /* release lock */
1266 KeReleaseGuardedMutex(&ObpDeviceMapLock);
1267
1268 /* done */
1269 return STATUS_SUCCESS;
1270 }
1271
1272 /* EOF */