1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ob/oblink.c 5 * PURPOSE: Implements symbolic links 6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) 7 * David Welch (welch@mcmail.com) 8 */ 9 10 /* INCLUDES *****************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 /* GLOBALS ******************************************************************/ 17 18 POBJECT_TYPE ObpSymbolicLinkObjectType = NULL; 19 20 /* PRIVATE FUNCTIONS *********************************************************/ 21 22 VOID 23 ObpProcessDosDeviceSymbolicLink(IN POBJECT_SYMBOLIC_LINK SymbolicLink, 24 IN BOOLEAN DeleteLink) 25 { 26 PDEVICE_MAP DeviceMap; 27 UNICODE_STRING TargetPath, LocalTarget; 28 POBJECT_DIRECTORY NameDirectory, DirectoryObject; 29 ULONG MaxReparse; 30 OBP_LOOKUP_CONTEXT LookupContext; 31 ULONG DriveType; 32 POBJECT_HEADER ObjectHeader; 33 POBJECT_HEADER_NAME_INFO ObjectNameInfo; 34 BOOLEAN DirectoryLocked; 35 PVOID Object; 36 37 /* 38 * To prevent endless reparsing, setting an upper limit on the 39 * number of reparses. 40 */ 41 MaxReparse = 32; 42 NameDirectory = NULL; 43 44 /* Get header data */ 45 ObjectHeader = OBJECT_TO_OBJECT_HEADER(SymbolicLink); 46 ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader); 47 48 /* Check if we have a directory in our symlink to use */ 49 if (ObjectNameInfo != NULL) 50 { 51 NameDirectory = ObjectNameInfo->Directory; 52 } 53 54 /* Initialize lookup context */ 55 ObpInitializeLookupContext(&LookupContext); 56 57 /* 58 * If we have to create the link, locate the IoDeviceObject if any 59 * this symbolic link points to. 60 */ 61 if (SymbolicLink->LinkTargetObject != NULL || !DeleteLink) 62 { 63 /* Start the search from the root */ 64 DirectoryObject = ObpRootDirectoryObject; 65 66 /* Keep track of our progress while parsing the name */ 67 LocalTarget = SymbolicLink->LinkTarget; 68 69 /* If LUID mappings are enabled, use system map */ 70 if (ObpLUIDDeviceMapsEnabled != 0) 71 { 72 DeviceMap = ObSystemDeviceMap; 73 } 74 /* Otherwise, use the one in the process */ 75 else 76 { 77 DeviceMap = PsGetCurrentProcess()->DeviceMap; 78 } 79 80 ReparseTargetPath: 81 /* 82 * If we have a device map active, check whether we have a drive 83 * letter prefixed with ??, if so, chomp it 84 */ 85 if (DeviceMap != NULL) 86 { 87 if (!((ULONG_PTR)(LocalTarget.Buffer) & 7)) 88 { 89 if (DeviceMap->DosDevicesDirectory != NULL) 90 { 91 if (LocalTarget.Length >= ObpDosDevicesShortName.Length && 92 (*(PULONGLONG)LocalTarget.Buffer == 93 ObpDosDevicesShortNamePrefix.Alignment.QuadPart)) 94 { 95 DirectoryObject = DeviceMap->DosDevicesDirectory; 96 97 LocalTarget.Length -= ObpDosDevicesShortName.Length; 98 LocalTarget.Buffer += (ObpDosDevicesShortName.Length / sizeof(WCHAR)); 99 } 100 } 101 } 102 } 103 104 /* Try walking the target path and open each part of the path */ 105 while (TRUE) 106 { 107 if (LocalTarget.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) 108 { 109 ++LocalTarget.Buffer; 110 LocalTarget.Length -= sizeof(WCHAR); 111 } 112 113 /* Remember the current component of the target path */ 114 TargetPath = LocalTarget; 115 116 /* Move forward to the next component of the target path */ 117 if (LocalTarget.Length != 0) 118 { 119 do 120 { 121 if (LocalTarget.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) 122 { 123 break; 124 } 125 126 ++LocalTarget.Buffer; 127 LocalTarget.Length -= sizeof(WCHAR); 128 } 129 while (LocalTarget.Length != 0); 130 } 131 132 TargetPath.Length -= LocalTarget.Length; 133 134 /* 135 * Finished processing the entire path, stop 136 * That's a failure case, we quit here 137 */ 138 if (TargetPath.Length == 0) 139 { 140 ObpReleaseLookupContext(&LookupContext); 141 return; 142 } 143 144 145 /* 146 * Make sure a deadlock does not occur as an exclusive lock on a pushlock 147 * would have already taken one in ObpLookupObjectName() on the parent 148 * directory where the symlink is being created [ObInsertObject()]. 149 * Prevent recursive locking by faking lock state in the lookup context 150 * when the current directory is same as the parent directory where 151 * the symlink is being created. If the lock state is not faked, 152 * ObpLookupEntryDirectory() will try to get a recursive lock on the 153 * pushlock and hang. For e.g. this happens when a substed drive is pointed to 154 * another substed drive. 155 */ 156 if (DirectoryObject == NameDirectory) 157 { 158 DirectoryLocked = LookupContext.DirectoryLocked; 159 LookupContext.DirectoryLocked = TRUE; 160 } 161 else 162 { 163 DirectoryLocked = FALSE; 164 } 165 166 Object = ObpLookupEntryDirectory(DirectoryObject, 167 &TargetPath, 168 0, 169 FALSE, 170 &LookupContext); 171 172 /* Locking was faked, undo it now */ 173 if (DirectoryObject == NameDirectory) 174 { 175 LookupContext.DirectoryLocked = DirectoryLocked; 176 } 177 178 /* Lookup failed, stop */ 179 if (Object == NULL) 180 { 181 break; 182 } 183 184 /* If we don't have a directory object, we'll have to handle the object */ 185 if (OBJECT_TO_OBJECT_HEADER(Object)->Type != ObpDirectoryObjectType) 186 { 187 /* If that's not a symbolic link, stop here, nothing to do */ 188 if (OBJECT_TO_OBJECT_HEADER(Object)->Type != ObpSymbolicLinkObjectType || 189 (((POBJECT_SYMBOLIC_LINK)Object)->DosDeviceDriveIndex != 0)) 190 { 191 break; 192 } 193 194 /* We're out of reparse attempts */ 195 if (MaxReparse == 0) 196 { 197 Object = NULL; 198 break; 199 } 200 201 --MaxReparse; 202 203 /* Symlink points to another initialized symlink, ask caller to reparse */ 204 DirectoryObject = ObpRootDirectoryObject; 205 206 LocalTarget = ((POBJECT_SYMBOLIC_LINK)Object)->LinkTarget; 207 208 goto ReparseTargetPath; 209 } 210 211 /* Make this current directory, and continue search */ 212 DirectoryObject = Object; 213 } 214 } 215 216 DeviceMap = NULL; 217 /* That's a drive letter, find a suitable device map */ 218 if (SymbolicLink->DosDeviceDriveIndex != 0) 219 { 220 ObjectHeader = OBJECT_TO_OBJECT_HEADER(SymbolicLink); 221 ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader); 222 if (ObjectNameInfo != NULL) 223 { 224 if (ObjectNameInfo->Directory != NULL) 225 { 226 DeviceMap = ObjectNameInfo->Directory->DeviceMap; 227 } 228 229 ObpDereferenceNameInfo(ObjectNameInfo); 230 } 231 } 232 233 /* If we were asked to delete the symlink */ 234 if (DeleteLink) 235 { 236 /* Zero its target */ 237 RtlInitUnicodeString(&SymbolicLink->LinkTargetRemaining, NULL); 238 239 /* If we had a target objected, dereference it */ 240 if (SymbolicLink->LinkTargetObject != NULL) 241 { 242 ObDereferenceObject(SymbolicLink->LinkTargetObject); 243 SymbolicLink->LinkTargetObject = NULL; 244 } 245 246 /* If it was a drive letter */ 247 if (DeviceMap != NULL) 248 { 249 /* Acquire the device map lock */ 250 KeAcquireGuardedMutex(&ObpDeviceMapLock); 251 252 /* Remove the drive entry */ 253 DeviceMap->DriveType[SymbolicLink->DosDeviceDriveIndex - 1] = 254 DOSDEVICE_DRIVE_UNKNOWN; 255 DeviceMap->DriveMap &= 256 ~(1 << (SymbolicLink->DosDeviceDriveIndex - 1)); 257 258 /* Release the device map lock */ 259 KeReleaseGuardedMutex(&ObpDeviceMapLock); 260 261 /* Reset the drive index, valid drive index starts from 1 */ 262 SymbolicLink->DosDeviceDriveIndex = 0; 263 } 264 } 265 else 266 { 267 DriveType = DOSDEVICE_DRIVE_CALCULATE; 268 269 /* If we have a drive letter and a pointer device object */ 270 if (Object != NULL && SymbolicLink->DosDeviceDriveIndex != 0 && 271 OBJECT_TO_OBJECT_HEADER(Object)->Type == IoDeviceObjectType) 272 { 273 /* Calculate the drive type */ 274 switch(((PDEVICE_OBJECT)Object)->DeviceType) 275 { 276 case FILE_DEVICE_VIRTUAL_DISK: 277 DriveType = DOSDEVICE_DRIVE_RAMDISK; 278 break; 279 case FILE_DEVICE_CD_ROM: 280 case FILE_DEVICE_CD_ROM_FILE_SYSTEM: 281 DriveType = DOSDEVICE_DRIVE_CDROM; 282 break; 283 case FILE_DEVICE_DISK: 284 case FILE_DEVICE_DISK_FILE_SYSTEM: 285 case FILE_DEVICE_FILE_SYSTEM: 286 if (((PDEVICE_OBJECT)Object)->Characteristics & FILE_REMOVABLE_MEDIA) 287 DriveType = DOSDEVICE_DRIVE_REMOVABLE; 288 else 289 DriveType = DOSDEVICE_DRIVE_FIXED; 290 break; 291 case FILE_DEVICE_NETWORK: 292 case FILE_DEVICE_NETWORK_FILE_SYSTEM: 293 DriveType = DOSDEVICE_DRIVE_REMOTE; 294 break; 295 default: 296 DPRINT1("Device Type %lu for %wZ is not known or unhandled\n", 297 ((PDEVICE_OBJECT)Object)->DeviceType, 298 &SymbolicLink->LinkTarget); 299 DriveType = DOSDEVICE_DRIVE_UNKNOWN; 300 } 301 } 302 303 /* Add a new drive entry */ 304 if (DeviceMap != NULL) 305 { 306 /* Acquire the device map lock */ 307 KeAcquireGuardedMutex(&ObpDeviceMapLock); 308 309 DeviceMap->DriveType[SymbolicLink->DosDeviceDriveIndex - 1] = 310 (UCHAR)DriveType; 311 DeviceMap->DriveMap |= 312 1 << (SymbolicLink->DosDeviceDriveIndex - 1); 313 314 /* Release the device map lock */ 315 KeReleaseGuardedMutex(&ObpDeviceMapLock); 316 } 317 } 318 319 /* Cleanup */ 320 ObpReleaseLookupContext(&LookupContext); 321 } 322 323 VOID 324 NTAPI 325 ObpDeleteSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink) 326 { 327 /* Just call the helper */ 328 ObpProcessDosDeviceSymbolicLink(SymbolicLink, TRUE); 329 } 330 331 VOID 332 NTAPI 333 ObpCreateSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink) 334 { 335 WCHAR UpperDrive; 336 POBJECT_HEADER ObjectHeader; 337 POBJECT_HEADER_NAME_INFO ObjectNameInfo; 338 339 /* Get header data */ 340 ObjectHeader = OBJECT_TO_OBJECT_HEADER(SymbolicLink); 341 ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader); 342 343 /* No name info, nothing to create */ 344 if (ObjectNameInfo == NULL) 345 { 346 return; 347 } 348 349 /* If we have a device map, look for creating a letter based drive */ 350 if (ObjectNameInfo->Directory != NULL && 351 ObjectNameInfo->Directory->DeviceMap != NULL) 352 { 353 /* Is it a drive letter based name? */ 354 if (ObjectNameInfo->Name.Length == 2 * sizeof(WCHAR)) 355 { 356 if (ObjectNameInfo->Name.Buffer[1] == L':') 357 { 358 UpperDrive = RtlUpcaseUnicodeChar(ObjectNameInfo->Name.Buffer[0]); 359 if (UpperDrive >= L'A' && UpperDrive <= L'Z') 360 { 361 /* Compute its index (it's 1 based - 0 means no letter) */ 362 SymbolicLink->DosDeviceDriveIndex = UpperDrive - (L'A' - 1); 363 } 364 } 365 } 366 367 /* Call the helper */ 368 ObpProcessDosDeviceSymbolicLink(SymbolicLink, FALSE); 369 } 370 371 /* We're done */ 372 ObpDereferenceNameInfo(ObjectNameInfo); 373 } 374 375 /*++ 376 * @name ObpDeleteSymbolicLink 377 * 378 * The ObpDeleteSymbolicLink routine <FILLMEIN> 379 * 380 * @param ObjectBody 381 * <FILLMEIN> 382 * 383 * @return None. 384 * 385 * @remarks None. 386 * 387 *--*/ 388 VOID 389 NTAPI 390 ObpDeleteSymbolicLink(PVOID ObjectBody) 391 { 392 POBJECT_SYMBOLIC_LINK SymlinkObject = (POBJECT_SYMBOLIC_LINK)ObjectBody; 393 394 /* Make sure that the symbolic link has a name */ 395 if (SymlinkObject->LinkTarget.Buffer) 396 { 397 /* Free the name */ 398 ExFreePool(SymlinkObject->LinkTarget.Buffer); 399 SymlinkObject->LinkTarget.Buffer = NULL; 400 } 401 } 402 403 /*++ 404 * @name ObpParseSymbolicLink 405 * 406 * The ObpParseSymbolicLink routine <FILLMEIN> 407 * 408 * @param Object 409 * <FILLMEIN> 410 * 411 * @param NextObject 412 * <FILLMEIN> 413 * 414 * @param FullPath 415 * <FILLMEIN> 416 * 417 * @param RemainingPath 418 * <FILLMEIN> 419 * 420 * @param Attributes 421 * <FILLMEIN> 422 * 423 * @return STATUS_SUCCESS or appropriate error value. 424 * 425 * @remarks None. 426 * 427 *--*/ 428 NTSTATUS 429 NTAPI 430 ObpParseSymbolicLink(IN PVOID ParsedObject, 431 IN PVOID ObjectType, 432 IN OUT PACCESS_STATE AccessState, 433 IN KPROCESSOR_MODE AccessMode, 434 IN ULONG Attributes, 435 IN OUT PUNICODE_STRING FullPath, 436 IN OUT PUNICODE_STRING RemainingName, 437 IN OUT PVOID Context OPTIONAL, 438 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL, 439 OUT PVOID *NextObject) 440 { 441 POBJECT_SYMBOLIC_LINK SymlinkObject = (POBJECT_SYMBOLIC_LINK)ParsedObject; 442 PUNICODE_STRING TargetPath; 443 PWSTR NewTargetPath; 444 ULONG LengthUsed, MaximumLength, TempLength; 445 NTSTATUS Status; 446 PAGED_CODE(); 447 448 /* Assume failure */ 449 *NextObject = NULL; 450 451 /* Check if we're out of name to parse */ 452 if (!RemainingName->Length) 453 { 454 /* Check if we got an object type */ 455 if (ObjectType) 456 { 457 /* Reference the object only */ 458 Status = ObReferenceObjectByPointer(ParsedObject, 459 0, 460 ObjectType, 461 AccessMode); 462 if (NT_SUCCESS(Status)) 463 { 464 /* Return it */ 465 *NextObject = ParsedObject; 466 } 467 468 if ((NT_SUCCESS(Status)) || (Status != STATUS_OBJECT_TYPE_MISMATCH)) 469 { 470 /* Fail */ 471 return Status; 472 } 473 } 474 } 475 else if (RemainingName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR) 476 { 477 /* Symbolic links must start with a backslash */ 478 return STATUS_OBJECT_TYPE_MISMATCH; 479 } 480 481 /* Check if this symlink is bound to a specific object */ 482 if (SymlinkObject->LinkTargetObject) 483 { 484 UNIMPLEMENTED; 485 } 486 487 /* Set the target path and length */ 488 TargetPath = &SymlinkObject->LinkTarget; 489 TempLength = TargetPath->Length; 490 491 /* 492 * Strip off the extra trailing '\', if we don't do this we will end up 493 * adding a extra '\' between TargetPath and RemainingName 494 * causing caller's like ObpLookupObjectName() to fail. 495 */ 496 if (TempLength && RemainingName->Length) 497 { 498 /* The target and remaining names aren't empty, so check for slashes */ 499 if ((TargetPath->Buffer[TempLength / sizeof(WCHAR) - 1] == 500 OBJ_NAME_PATH_SEPARATOR) && 501 (RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) 502 { 503 /* Reduce the length by one to cut off the extra '\' */ 504 TempLength -= sizeof(OBJ_NAME_PATH_SEPARATOR); 505 } 506 } 507 508 /* Calculate the new length */ 509 LengthUsed = TempLength + RemainingName->Length; 510 511 /* Check if it's not too much */ 512 if (LengthUsed > 0xFFF0) 513 return STATUS_NAME_TOO_LONG; 514 515 /* Optimization: check if the new name is shorter */ 516 if (FullPath->MaximumLength <= LengthUsed) 517 { 518 /* It's not, allocate a new one */ 519 MaximumLength = LengthUsed + sizeof(WCHAR); 520 NewTargetPath = ExAllocatePoolWithTag(NonPagedPool, 521 MaximumLength, 522 OB_NAME_TAG); 523 if (!NewTargetPath) return STATUS_INSUFFICIENT_RESOURCES; 524 } 525 else 526 { 527 /* It is! Reuse the name... */ 528 MaximumLength = FullPath->MaximumLength; 529 NewTargetPath = FullPath->Buffer; 530 } 531 532 /* Make sure we have a length */ 533 if (RemainingName->Length) 534 { 535 /* Copy the new path */ 536 RtlMoveMemory((PVOID)((ULONG_PTR)NewTargetPath + TempLength), 537 RemainingName->Buffer, 538 RemainingName->Length); 539 } 540 541 /* Copy the target path and null-terminate it */ 542 RtlCopyMemory(NewTargetPath, TargetPath->Buffer, TempLength); 543 NewTargetPath[LengthUsed / sizeof(WCHAR)] = UNICODE_NULL; 544 545 /* If the optimization didn't work, free the old buffer */ 546 if (NewTargetPath != FullPath->Buffer) ExFreePool(FullPath->Buffer); 547 548 /* Update the path values */ 549 FullPath->Length = (USHORT)LengthUsed; 550 FullPath->MaximumLength = (USHORT)MaximumLength; 551 FullPath->Buffer = NewTargetPath; 552 553 /* Tell the parse routine to start reparsing */ 554 return STATUS_REPARSE; 555 } 556 557 /* PUBLIC FUNCTIONS **********************************************************/ 558 559 /*++ 560 * @name NtCreateSymbolicLinkObject 561 * @implemented NT4 562 * 563 * The NtCreateSymbolicLinkObject opens or creates a symbolic link object. 564 * 565 * @param LinkHandle 566 * Variable which receives the symlink handle. 567 * 568 * @param DesiredAccess 569 * Desired access to the symlink. 570 * 571 * @param ObjectAttributes 572 * Structure describing the symlink. 573 * 574 * @param LinkTarget 575 * Unicode string defining the symlink's target 576 * 577 * @return STATUS_SUCCESS or appropriate error value. 578 * 579 * @remarks None. 580 * 581 *--*/ 582 NTSTATUS 583 NTAPI 584 NtCreateSymbolicLinkObject(OUT PHANDLE LinkHandle, 585 IN ACCESS_MASK DesiredAccess, 586 IN POBJECT_ATTRIBUTES ObjectAttributes, 587 IN PUNICODE_STRING LinkTarget) 588 { 589 HANDLE hLink; 590 POBJECT_SYMBOLIC_LINK SymbolicLink; 591 UNICODE_STRING CapturedLinkTarget; 592 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 593 NTSTATUS Status; 594 PAGED_CODE(); 595 596 /* Check if we need to probe parameters */ 597 if (PreviousMode != KernelMode) 598 { 599 _SEH2_TRY 600 { 601 /* Probe the target */ 602 CapturedLinkTarget = ProbeForReadUnicodeString(LinkTarget); 603 ProbeForRead(CapturedLinkTarget.Buffer, 604 CapturedLinkTarget.MaximumLength, 605 sizeof(WCHAR)); 606 607 /* Probe the return handle */ 608 ProbeForWriteHandle(LinkHandle); 609 } 610 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 611 { 612 /* Return the exception code */ 613 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 614 } 615 _SEH2_END; 616 } 617 else 618 { 619 /* No need to capture */ 620 CapturedLinkTarget = *LinkTarget; 621 } 622 623 /* Check if the maximum length is odd */ 624 if (CapturedLinkTarget.MaximumLength % sizeof(WCHAR)) 625 { 626 /* Round it down */ 627 CapturedLinkTarget.MaximumLength = 628 (USHORT)ALIGN_DOWN(CapturedLinkTarget.MaximumLength, WCHAR); 629 } 630 631 /* Fail if the length is odd, or if the maximum is smaller or 0 */ 632 if ((CapturedLinkTarget.Length % sizeof(WCHAR)) || 633 (CapturedLinkTarget.MaximumLength < CapturedLinkTarget.Length) || 634 !(CapturedLinkTarget.MaximumLength)) 635 { 636 /* This message is displayed on the debugger in Windows */ 637 DbgPrint("OB: Invalid symbolic link target - %wZ\n", 638 &CapturedLinkTarget); 639 return STATUS_INVALID_PARAMETER; 640 } 641 642 /* Create the object */ 643 Status = ObCreateObject(PreviousMode, 644 ObpSymbolicLinkObjectType, 645 ObjectAttributes, 646 PreviousMode, 647 NULL, 648 sizeof(OBJECT_SYMBOLIC_LINK), 649 0, 650 0, 651 (PVOID*)&SymbolicLink); 652 if (NT_SUCCESS(Status)) 653 { 654 /* Success! Fill in the creation time immediately */ 655 KeQuerySystemTime(&SymbolicLink->CreationTime); 656 657 /* Setup the target name */ 658 SymbolicLink->LinkTarget.Length = CapturedLinkTarget.Length; 659 SymbolicLink->LinkTarget.MaximumLength = CapturedLinkTarget.MaximumLength; 660 SymbolicLink->LinkTarget.Buffer = 661 ExAllocatePoolWithTag(PagedPool, 662 CapturedLinkTarget.MaximumLength, 663 TAG_SYMLINK_TARGET); 664 if (!SymbolicLink->LinkTarget.Buffer) 665 { 666 /* Dereference the symbolic link object and fail */ 667 ObDereferenceObject(SymbolicLink); 668 return STATUS_NO_MEMORY; 669 } 670 671 /* Copy it */ 672 _SEH2_TRY 673 { 674 RtlCopyMemory(SymbolicLink->LinkTarget.Buffer, 675 CapturedLinkTarget.Buffer, 676 CapturedLinkTarget.MaximumLength); 677 } 678 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 679 { 680 ObDereferenceObject(SymbolicLink); 681 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 682 } 683 _SEH2_END; 684 685 /* Initialize the remaining name, dos drive index and target object */ 686 SymbolicLink->LinkTargetObject = NULL; 687 SymbolicLink->DosDeviceDriveIndex = 0; 688 RtlInitUnicodeString(&SymbolicLink->LinkTargetRemaining, NULL); 689 690 /* Insert it into the object tree */ 691 Status = ObInsertObject(SymbolicLink, 692 NULL, 693 DesiredAccess, 694 0, 695 NULL, 696 &hLink); 697 if (NT_SUCCESS(Status)) 698 { 699 _SEH2_TRY 700 { 701 /* Return the handle to caller */ 702 *LinkHandle = hLink; 703 } 704 _SEH2_EXCEPT(ExSystemExceptionFilter()) 705 { 706 /* Get exception code */ 707 Status = _SEH2_GetExceptionCode(); 708 } 709 _SEH2_END; 710 } 711 } 712 713 /* Return status to caller */ 714 return Status; 715 } 716 717 /*++ 718 * @name NtOpenSymbolicLinkObject 719 * @implemented NT4 720 * 721 * The NtOpenSymbolicLinkObject opens a symbolic link object. 722 * 723 * @param LinkHandle 724 * Variable which receives the symlink handle. 725 * 726 * @param DesiredAccess 727 * Desired access to the symlink. 728 * 729 * @param ObjectAttributes 730 * Structure describing the symlink. 731 * 732 * @return STATUS_SUCCESS or appropriate error value. 733 * 734 * @remarks None. 735 * 736 *--*/ 737 NTSTATUS 738 NTAPI 739 NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle, 740 IN ACCESS_MASK DesiredAccess, 741 IN POBJECT_ATTRIBUTES ObjectAttributes) 742 { 743 HANDLE hLink; 744 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 745 NTSTATUS Status; 746 PAGED_CODE(); 747 748 /* Check if we need to probe parameters */ 749 if (PreviousMode != KernelMode) 750 { 751 _SEH2_TRY 752 { 753 /* Probe the return handle */ 754 ProbeForWriteHandle(LinkHandle); 755 } 756 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 757 { 758 /* Return the exception code */ 759 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 760 } 761 _SEH2_END; 762 } 763 764 /* Open the object */ 765 Status = ObOpenObjectByName(ObjectAttributes, 766 ObpSymbolicLinkObjectType, 767 PreviousMode, 768 NULL, 769 DesiredAccess, 770 NULL, 771 &hLink); 772 773 _SEH2_TRY 774 { 775 /* Return the handle to caller */ 776 *LinkHandle = hLink; 777 } 778 _SEH2_EXCEPT(ExSystemExceptionFilter()) 779 { 780 /* Get exception code */ 781 Status = _SEH2_GetExceptionCode(); 782 } 783 _SEH2_END; 784 785 /* Return status to caller */ 786 return Status; 787 } 788 789 /*++ 790 * @name NtQuerySymbolicLinkObject 791 * @implemented NT4 792 * 793 * The NtQuerySymbolicLinkObject queries a symbolic link object. 794 * 795 * @param LinkHandle 796 * Symlink handle to query 797 * 798 * @param LinkTarget 799 * Unicode string defining the symlink's target 800 * 801 * @param ResultLength 802 * Caller supplied storage for the number of bytes written (or NULL). 803 * 804 * @return STATUS_SUCCESS or appropriate error value. 805 * 806 * @remarks None. 807 * 808 *--*/ 809 NTSTATUS 810 NTAPI 811 NtQuerySymbolicLinkObject(IN HANDLE LinkHandle, 812 OUT PUNICODE_STRING LinkTarget, 813 OUT PULONG ResultLength OPTIONAL) 814 { 815 UNICODE_STRING SafeLinkTarget = { 0, 0, NULL }; 816 POBJECT_SYMBOLIC_LINK SymlinkObject; 817 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 818 NTSTATUS Status; 819 ULONG LengthUsed; 820 PAGED_CODE(); 821 822 if (PreviousMode != KernelMode) 823 { 824 _SEH2_TRY 825 { 826 /* Probe the unicode string for read and write */ 827 ProbeForWriteUnicodeString(LinkTarget); 828 829 /* Probe the unicode string's buffer for write */ 830 SafeLinkTarget = *LinkTarget; 831 ProbeForWrite(SafeLinkTarget.Buffer, 832 SafeLinkTarget.MaximumLength, 833 sizeof(WCHAR)); 834 835 /* Probe the return length */ 836 if (ResultLength) ProbeForWriteUlong(ResultLength); 837 } 838 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 839 { 840 /* Return the exception code */ 841 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 842 } 843 _SEH2_END; 844 } 845 else 846 { 847 /* No need to probe */ 848 SafeLinkTarget = *LinkTarget; 849 } 850 851 /* Reference the object */ 852 Status = ObReferenceObjectByHandle(LinkHandle, 853 SYMBOLIC_LINK_QUERY, 854 ObpSymbolicLinkObjectType, 855 PreviousMode, 856 (PVOID *)&SymlinkObject, 857 NULL); 858 if (NT_SUCCESS(Status)) 859 { 860 /* Lock the object */ 861 ObpAcquireObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject)); 862 863 /* 864 * So here's the thing: If you specify a return length, then the 865 * implementation will use the maximum length. If you don't, then 866 * it will use the length. 867 */ 868 LengthUsed = ResultLength ? SymlinkObject->LinkTarget.MaximumLength : 869 SymlinkObject->LinkTarget.Length; 870 871 /* Enter SEH so we can safely copy */ 872 _SEH2_TRY 873 { 874 /* Make sure our buffer will fit */ 875 if (LengthUsed <= SafeLinkTarget.MaximumLength) 876 { 877 /* Copy the buffer */ 878 RtlCopyMemory(SafeLinkTarget.Buffer, 879 SymlinkObject->LinkTarget.Buffer, 880 LengthUsed); 881 882 /* Copy the new length */ 883 LinkTarget->Length = SymlinkObject->LinkTarget.Length; 884 } 885 else 886 { 887 /* Otherwise set the failure status */ 888 Status = STATUS_BUFFER_TOO_SMALL; 889 } 890 891 /* In both cases, check if the required length was requested */ 892 if (ResultLength) 893 { 894 /* Then return it */ 895 *ResultLength = SymlinkObject->LinkTarget.MaximumLength; 896 } 897 } 898 _SEH2_EXCEPT(ExSystemExceptionFilter()) 899 { 900 /* Get the error code */ 901 Status = _SEH2_GetExceptionCode(); 902 } 903 _SEH2_END; 904 905 /* Unlock and dereference the object */ 906 ObpReleaseObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject)); 907 ObDereferenceObject(SymlinkObject); 908 } 909 910 /* Return query status */ 911 return Status; 912 } 913 914 /* EOF */ 915