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