xref: /reactos/ntoskrnl/ob/obname.c (revision 682f85ad)
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 */
1420