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