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