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