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