[DRWTSN32] Print some extra exception info
[reactos.git] / ntoskrnl / ob / obname.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/obname.c
5 * PURPOSE: Manages all functions related to the Object Manager name-
6 * space, such as finding objects or querying their names.
7 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
8 * Eric Kohl
9 * Thomas Weidenmueller (w3seek@reactos.org)
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <debug.h>
17
18 BOOLEAN ObpCaseInsensitive = TRUE;
19 POBJECT_DIRECTORY ObpRootDirectoryObject;
20 POBJECT_DIRECTORY ObpTypeDirectoryObject;
21
22 /* DOS Device Prefix \??\ and \?? */
23 ALIGNEDNAME ObpDosDevicesShortNamePrefix = {{L'\\',L'?',L'?',L'\\'}};
24 ALIGNEDNAME ObpDosDevicesShortNameRoot = {{L'\\',L'?',L'?',L'\0'}};
25 UNICODE_STRING ObpDosDevicesShortName =
26 {
27 sizeof(ObpDosDevicesShortNamePrefix),
28 sizeof(ObpDosDevicesShortNamePrefix),
29 (PWSTR)&ObpDosDevicesShortNamePrefix
30 };
31
32 WCHAR ObpUnsecureGlobalNamesBuffer[128] = {0};
33 ULONG ObpUnsecureGlobalNamesLength = sizeof(ObpUnsecureGlobalNamesBuffer);
34
35 /* PRIVATE FUNCTIONS *********************************************************/
36
37 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 */