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