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