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