1 /* 2 * ReactOS kernel 3 * Copyright (C) 2011-2012 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 18 * 19 * COPYRIGHT: See COPYING in the top level directory 20 * PROJECT: ReactOS kernel 21 * FILE: drivers/filesystem/mountmgr/symlink.c 22 * PURPOSE: Mount Manager - Symbolic links functions 23 * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org) 24 */ 25 26 #include "mntmgr.h" 27 28 #define NDEBUG 29 #include <debug.h> 30 31 UNICODE_STRING DeviceMount = RTL_CONSTANT_STRING(MOUNTMGR_DEVICE_NAME); 32 UNICODE_STRING DosDevicesMount = RTL_CONSTANT_STRING(L"\\DosDevices\\MountPointManager"); 33 UNICODE_STRING DosDevices = RTL_CONSTANT_STRING(L"\\DosDevices\\"); 34 UNICODE_STRING DeviceFloppy = RTL_CONSTANT_STRING(L"\\Device\\Floppy"); 35 UNICODE_STRING DeviceCdRom = RTL_CONSTANT_STRING(L"\\Device\\CdRom"); 36 UNICODE_STRING DosGlobal = RTL_CONSTANT_STRING(L"\\GLOBAL??\\"); 37 UNICODE_STRING Global = RTL_CONSTANT_STRING(L"\\??\\"); 38 UNICODE_STRING SafeVolumes = RTL_CONSTANT_STRING(L"\\Device\\VolumesSafeForWriteAccess"); 39 UNICODE_STRING Volume = RTL_CONSTANT_STRING(L"\\??\\Volume"); 40 UNICODE_STRING ReparseIndex = RTL_CONSTANT_STRING(L"\\$Extend\\$Reparse:$R:$INDEX_ALLOCATION"); 41 42 /* 43 * @implemented 44 */ 45 NTSTATUS 46 CreateStringWithGlobal(IN PUNICODE_STRING DosName, 47 OUT PUNICODE_STRING GlobalString) 48 { 49 UNICODE_STRING IntGlobal; 50 51 if (RtlPrefixUnicodeString(&DosDevices, DosName, TRUE)) 52 { 53 /* DOS device - use DOS global */ 54 IntGlobal.Length = DosName->Length - DosDevices.Length + DosGlobal.Length; 55 IntGlobal.MaximumLength = IntGlobal.Length + sizeof(WCHAR); 56 IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength); 57 if (!IntGlobal.Buffer) 58 { 59 return STATUS_INSUFFICIENT_RESOURCES; 60 } 61 62 RtlCopyMemory(IntGlobal.Buffer, DosGlobal.Buffer, DosGlobal.Length); 63 RtlCopyMemory(IntGlobal.Buffer + (DosGlobal.Length / sizeof(WCHAR)), 64 DosName->Buffer + (DosDevices.Length / sizeof(WCHAR)), 65 DosName->Length - DosDevices.Length); 66 IntGlobal.Buffer[IntGlobal.Length / sizeof(WCHAR)] = UNICODE_NULL; 67 } 68 else if (RtlPrefixUnicodeString(&Global, DosName, TRUE)) 69 { 70 /* Switch to DOS global */ 71 IntGlobal.Length = DosName->Length - Global.Length + DosGlobal.Length; 72 IntGlobal.MaximumLength = IntGlobal.Length + sizeof(WCHAR); 73 IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength); 74 if (!IntGlobal.Buffer) 75 { 76 return STATUS_INSUFFICIENT_RESOURCES; 77 } 78 79 RtlCopyMemory(IntGlobal.Buffer, DosGlobal.Buffer, DosGlobal.Length); 80 RtlCopyMemory(IntGlobal.Buffer + (DosGlobal.Length / sizeof(WCHAR)), 81 DosName->Buffer + (Global.Length / sizeof(WCHAR)), 82 DosName->Length - Global.Length); 83 IntGlobal.Buffer[IntGlobal.Length / sizeof(WCHAR)] = UNICODE_NULL; 84 } 85 else 86 { 87 /* Simply duplicate string */ 88 IntGlobal.Length = DosName->Length; 89 IntGlobal.MaximumLength = DosName->MaximumLength; 90 IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength); 91 if (!IntGlobal.Buffer) 92 { 93 return STATUS_INSUFFICIENT_RESOURCES; 94 } 95 96 RtlCopyMemory(IntGlobal.Buffer, DosName->Buffer, IntGlobal.MaximumLength); 97 } 98 99 /* Return string */ 100 GlobalString->Length = IntGlobal.Length; 101 GlobalString->MaximumLength = IntGlobal.MaximumLength; 102 GlobalString->Buffer = IntGlobal.Buffer; 103 104 return STATUS_SUCCESS; 105 } 106 107 /* 108 * @implemented 109 */ 110 NTSTATUS 111 GlobalCreateSymbolicLink(IN PUNICODE_STRING DosName, 112 IN PUNICODE_STRING DeviceName) 113 { 114 NTSTATUS Status; 115 UNICODE_STRING GlobalName; 116 117 /* First create the global string */ 118 Status = CreateStringWithGlobal(DosName, &GlobalName); 119 if (!NT_SUCCESS(Status)) 120 { 121 return Status; 122 } 123 124 /* Then, create the symlink */ 125 Status = IoCreateSymbolicLink(&GlobalName, DeviceName); 126 127 FreePool(GlobalName.Buffer); 128 129 return Status; 130 } 131 132 /* 133 * @implemented 134 */ 135 NTSTATUS 136 GlobalDeleteSymbolicLink(IN PUNICODE_STRING DosName) 137 { 138 NTSTATUS Status; 139 UNICODE_STRING GlobalName; 140 141 /* Recreate the string (to find the link) */ 142 Status = CreateStringWithGlobal(DosName, &GlobalName); 143 if (!NT_SUCCESS(Status)) 144 { 145 return Status; 146 } 147 148 /* And delete the link */ 149 Status = IoDeleteSymbolicLink(&GlobalName); 150 151 FreePool(GlobalName.Buffer); 152 153 return Status; 154 } 155 156 /* 157 * @implemented 158 */ 159 VOID 160 SendLinkCreated(IN PUNICODE_STRING SymbolicName) 161 { 162 PIRP Irp; 163 KEVENT Event; 164 ULONG NameSize; 165 NTSTATUS Status; 166 PFILE_OBJECT FileObject; 167 PIO_STACK_LOCATION Stack; 168 PMOUNTDEV_NAME Name = NULL; 169 PDEVICE_OBJECT DeviceObject; 170 IO_STATUS_BLOCK IoStatusBlock; 171 172 /* Get the device associated with the name */ 173 Status = IoGetDeviceObjectPointer(SymbolicName, 174 FILE_READ_ATTRIBUTES, 175 &FileObject, 176 &DeviceObject); 177 if (!NT_SUCCESS(Status)) 178 { 179 return; 180 } 181 182 /* Get attached device (will notify it) */ 183 DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject); 184 185 /* NameSize is the size of the whole MOUNTDEV_NAME struct */ 186 NameSize = sizeof(USHORT) + SymbolicName->Length; 187 Name = AllocatePool(NameSize); 188 if (!Name) 189 { 190 goto Cleanup; 191 } 192 193 /* Initialize struct */ 194 Name->NameLength = SymbolicName->Length; 195 RtlCopyMemory(Name->Name, SymbolicName->Buffer, SymbolicName->Length); 196 197 KeInitializeEvent(&Event, NotificationEvent, FALSE); 198 /* Microsoft does it twice... Once with limited access, second with any 199 * So, first one here 200 */ 201 Irp = IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE, 4, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS), 202 DeviceObject, 203 Name, 204 NameSize, 205 NULL, 206 0, 207 FALSE, 208 &Event, 209 &IoStatusBlock); 210 /* This one can fail, no one matters */ 211 if (Irp) 212 { 213 Stack = IoGetNextIrpStackLocation(Irp); 214 Stack->FileObject = FileObject; 215 216 Status = IoCallDriver(DeviceObject, Irp); 217 if (Status == STATUS_PENDING) 218 { 219 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 220 } 221 } 222 223 /* Then, second one */ 224 KeInitializeEvent(&Event, NotificationEvent, FALSE); 225 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_CREATED, 226 DeviceObject, 227 Name, 228 NameSize, 229 NULL, 230 0, 231 FALSE, 232 &Event, 233 &IoStatusBlock); 234 if (!Irp) 235 { 236 goto Cleanup; 237 } 238 239 Stack = IoGetNextIrpStackLocation(Irp); 240 Stack->FileObject = FileObject; 241 242 /* Really notify */ 243 Status = IoCallDriver(DeviceObject, Irp); 244 if (Status == STATUS_PENDING) 245 { 246 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 247 } 248 249 Cleanup: 250 if (Name) 251 { 252 FreePool(Name); 253 } 254 255 ObDereferenceObject(DeviceObject); 256 ObDereferenceObject(FileObject); 257 258 return; 259 } 260 261 /* 262 * @implemented 263 */ 264 VOID 265 SendLinkDeleted(IN PUNICODE_STRING DeviceName, 266 IN PUNICODE_STRING SymbolicName) 267 { 268 PIRP Irp; 269 KEVENT Event; 270 ULONG NameSize; 271 NTSTATUS Status; 272 PFILE_OBJECT FileObject; 273 PIO_STACK_LOCATION Stack; 274 PMOUNTDEV_NAME Name = NULL; 275 PDEVICE_OBJECT DeviceObject; 276 IO_STATUS_BLOCK IoStatusBlock; 277 278 /* Get the device associated with the name */ 279 Status = IoGetDeviceObjectPointer(DeviceName, 280 FILE_READ_ATTRIBUTES, 281 &FileObject, 282 &DeviceObject); 283 if (!NT_SUCCESS(Status)) 284 { 285 return; 286 } 287 288 /* Get attached device (will notify it) */ 289 DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject); 290 291 /* NameSize is the size of the whole MOUNTDEV_NAME struct */ 292 NameSize = sizeof(USHORT) + SymbolicName->Length; 293 Name = AllocatePool(NameSize); 294 if (!Name) 295 { 296 goto Cleanup; 297 } 298 299 /* Initialize struct */ 300 Name->NameLength = SymbolicName->Length; 301 RtlCopyMemory(Name->Name, SymbolicName->Buffer, SymbolicName->Length); 302 303 KeInitializeEvent(&Event, NotificationEvent, FALSE); 304 /* Cf: SendLinkCreated comment */ 305 Irp = IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE, 5, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS), 306 DeviceObject, 307 Name, 308 NameSize, 309 NULL, 310 0, 311 FALSE, 312 &Event, 313 &IoStatusBlock); 314 /* This one can fail, no one matters */ 315 if (Irp) 316 { 317 Stack = IoGetNextIrpStackLocation(Irp); 318 Stack->FileObject = FileObject; 319 320 Status = IoCallDriver(DeviceObject, Irp); 321 if (Status == STATUS_PENDING) 322 { 323 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 324 } 325 } 326 327 /* Then, second one */ 328 KeInitializeEvent(&Event, NotificationEvent, FALSE); 329 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_DELETED, 330 DeviceObject, 331 Name, 332 NameSize, 333 NULL, 334 0, 335 FALSE, 336 &Event, 337 &IoStatusBlock); 338 if (!Irp) 339 { 340 goto Cleanup; 341 } 342 343 Stack = IoGetNextIrpStackLocation(Irp); 344 Stack->FileObject = FileObject; 345 346 /* Really notify */ 347 Status = IoCallDriver(DeviceObject, Irp); 348 if (Status == STATUS_PENDING) 349 { 350 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 351 } 352 353 Cleanup: 354 if (Name) 355 { 356 FreePool(Name); 357 } 358 359 ObDereferenceObject(DeviceObject); 360 ObDereferenceObject(FileObject); 361 362 return; 363 } 364 365 /* 366 * @implemented 367 */ 368 NTSTATUS 369 NTAPI 370 SymbolicLinkNamesFromUniqueIdCount(IN PWSTR ValueName, 371 IN ULONG ValueType, 372 IN PVOID ValueData, 373 IN ULONG ValueLength, 374 IN PVOID Context, 375 IN PVOID EntryContext) 376 { 377 UNICODE_STRING ValueNameString; 378 PMOUNTDEV_UNIQUE_ID UniqueId = Context; 379 380 if (ValueName[0] != L'#' || ValueType != REG_BINARY || 381 (UniqueId->UniqueIdLength != ValueLength)) 382 { 383 return STATUS_SUCCESS; 384 } 385 386 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength) 387 { 388 return STATUS_SUCCESS; 389 } 390 391 /* That one matched, increase count */ 392 RtlInitUnicodeString(&ValueNameString, ValueName); 393 if (ValueNameString.Length) 394 { 395 (*((PULONG)EntryContext))++; 396 } 397 398 return STATUS_SUCCESS; 399 } 400 401 /* 402 * @implemented 403 */ 404 NTSTATUS 405 NTAPI 406 SymbolicLinkNamesFromUniqueIdQuery(IN PWSTR ValueName, 407 IN ULONG ValueType, 408 IN PVOID ValueData, 409 IN ULONG ValueLength, 410 IN PVOID Context, 411 IN PVOID EntryContext) 412 { 413 UNICODE_STRING ValueNameString; 414 PMOUNTDEV_UNIQUE_ID UniqueId = Context; 415 /* Unicode strings table */ 416 PUNICODE_STRING ReturnString = EntryContext; 417 418 if (ValueName[0] != L'#' || ValueType != REG_BINARY || 419 (UniqueId->UniqueIdLength != ValueLength)) 420 { 421 return STATUS_SUCCESS; 422 } 423 424 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength) 425 { 426 return STATUS_SUCCESS; 427 } 428 429 /* Unique ID matches, let's put the symlink */ 430 RtlInitUnicodeString(&ValueNameString, ValueName); 431 if (!ValueNameString.Length) 432 { 433 return STATUS_SUCCESS; 434 } 435 436 /* Allocate string to copy */ 437 ValueNameString.Buffer = AllocatePool(ValueNameString.MaximumLength); 438 if (!ValueNameString.Buffer) 439 { 440 return STATUS_SUCCESS; 441 } 442 443 /* Copy */ 444 RtlCopyMemory(ValueNameString.Buffer, ValueName, ValueNameString.Length); 445 ValueNameString.Buffer[ValueNameString.Length / sizeof(WCHAR)] = UNICODE_NULL; 446 447 while (ReturnString->Length) 448 { 449 ReturnString++; 450 } 451 452 /* And return that string */ 453 *ReturnString = ValueNameString; 454 455 return STATUS_SUCCESS; 456 } 457 458 /* 459 * @implemented 460 */ 461 NTSTATUS 462 CreateNewVolumeName(OUT PUNICODE_STRING VolumeName, 463 IN PGUID VolumeGuid OPTIONAL) 464 { 465 GUID Guid; 466 NTSTATUS Status; 467 UNICODE_STRING GuidString; 468 469 /* If no GUID was provided, then create one */ 470 if (!VolumeGuid) 471 { 472 Status = ExUuidCreate(&Guid); 473 if (!NT_SUCCESS(Status)) 474 { 475 return Status; 476 } 477 } 478 else 479 { 480 RtlCopyMemory(&Guid, VolumeGuid, sizeof(GUID)); 481 } 482 483 /* Convert GUID to string */ 484 Status = RtlStringFromGUID(&Guid, &GuidString); 485 if (!NT_SUCCESS(Status)) 486 { 487 return Status; 488 } 489 490 /* Size for volume namespace, literal GUID, and null char */ 491 VolumeName->MaximumLength = 0x14 + 0x4C + sizeof(UNICODE_NULL); 492 VolumeName->Buffer = AllocatePool(0x14 + 0x4C + sizeof(UNICODE_NULL)); 493 if (!VolumeName->Buffer) 494 { 495 Status = STATUS_INSUFFICIENT_RESOURCES; 496 } 497 else 498 { 499 RtlCopyUnicodeString(VolumeName, &Volume); 500 RtlAppendUnicodeStringToString(VolumeName, &GuidString); 501 VolumeName->Buffer[VolumeName->Length / sizeof(WCHAR)] = UNICODE_NULL; 502 Status = STATUS_SUCCESS; 503 } 504 505 ExFreePoolWithTag(GuidString.Buffer, 0); 506 507 return Status; 508 } 509 510 /* 511 * @implemented 512 */ 513 NTSTATUS 514 QuerySymbolicLinkNamesFromStorage(IN PDEVICE_EXTENSION DeviceExtension, 515 IN PDEVICE_INFORMATION DeviceInformation, 516 IN PUNICODE_STRING SuggestedLinkName, 517 IN BOOLEAN UseOnlyIfThereAreNoOtherLinks, 518 OUT PUNICODE_STRING * SymLinks, 519 OUT PULONG SymLinkCount, 520 IN BOOLEAN HasGuid, 521 IN LPGUID Guid) 522 { 523 NTSTATUS Status; 524 BOOLEAN WriteNew; 525 RTL_QUERY_REGISTRY_TABLE QueryTable[2]; 526 527 UNREFERENCED_PARAMETER(DeviceExtension); 528 529 /* First of all, count links */ 530 RtlZeroMemory(QueryTable, sizeof(QueryTable)); 531 QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount; 532 QueryTable[0].EntryContext = SymLinkCount; 533 *SymLinkCount = 0; 534 535 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, 536 DatabasePath, 537 QueryTable, 538 DeviceInformation->UniqueId, 539 NULL); 540 if (!NT_SUCCESS(Status)) 541 { 542 *SymLinkCount = 0; 543 } 544 545 /* Check if we have to write a new one first */ 546 if (SuggestedLinkName && !IsDriveLetter(SuggestedLinkName) && 547 UseOnlyIfThereAreNoOtherLinks && *SymLinkCount == 0) 548 { 549 WriteNew = TRUE; 550 } 551 else 552 { 553 WriteNew = FALSE; 554 } 555 556 /* If has GUID, it makes one more link */ 557 if (HasGuid) 558 { 559 (*SymLinkCount)++; 560 } 561 562 if (WriteNew) 563 { 564 /* Write link */ 565 RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, 566 DatabasePath, 567 SuggestedLinkName->Buffer, 568 REG_BINARY, 569 DeviceInformation->UniqueId->UniqueId, 570 DeviceInformation->UniqueId->UniqueIdLength); 571 572 /* And recount all the needed links */ 573 RtlZeroMemory(QueryTable, sizeof(QueryTable)); 574 QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount; 575 QueryTable[0].EntryContext = SymLinkCount; 576 *SymLinkCount = 0; 577 578 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, 579 DatabasePath, 580 QueryTable, 581 DeviceInformation->UniqueId, 582 NULL); 583 if (!NT_SUCCESS(Status)) 584 { 585 return STATUS_NOT_FOUND; 586 } 587 } 588 589 /* Not links found? */ 590 if (!*SymLinkCount) 591 { 592 return STATUS_NOT_FOUND; 593 } 594 595 /* Allocate a buffer big enough to hold symlinks (table of unicode strings) */ 596 *SymLinks = AllocatePool(*SymLinkCount * sizeof(UNICODE_STRING)); 597 if (!*SymLinks) 598 { 599 return STATUS_INSUFFICIENT_RESOURCES; 600 } 601 602 /* Prepare to query links */ 603 RtlZeroMemory(*SymLinks, *SymLinkCount * sizeof(UNICODE_STRING)); 604 RtlZeroMemory(QueryTable, sizeof(QueryTable)); 605 QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdQuery; 606 607 /* No GUID? Keep it that way */ 608 if (!HasGuid) 609 { 610 QueryTable[0].EntryContext = *SymLinks; 611 } 612 /* Otherwise, first create volume name */ 613 else 614 { 615 Status = CreateNewVolumeName(SymLinks[0], Guid); 616 if (!NT_SUCCESS(Status)) 617 { 618 FreePool(*SymLinks); 619 return Status; 620 } 621 622 /* Skip first link (ours) */ 623 QueryTable[0].EntryContext = *SymLinks + 1; 624 } 625 626 /* Now, query */ 627 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, 628 DatabasePath, 629 QueryTable, 630 DeviceInformation->UniqueId, 631 NULL); 632 633 return STATUS_SUCCESS; 634 } 635 636 /* 637 * @implemented 638 */ 639 PSAVED_LINK_INFORMATION 640 RemoveSavedLinks(IN PDEVICE_EXTENSION DeviceExtension, 641 IN PMOUNTDEV_UNIQUE_ID UniqueId) 642 { 643 PLIST_ENTRY NextEntry; 644 PSAVED_LINK_INFORMATION SavedLinkInformation; 645 646 /* No saved links? Easy! */ 647 if (IsListEmpty(&(DeviceExtension->SavedLinksListHead))) 648 { 649 return NULL; 650 } 651 652 /* Now, browse saved links */ 653 for (NextEntry = DeviceExtension->SavedLinksListHead.Flink; 654 NextEntry != &(DeviceExtension->SavedLinksListHead); 655 NextEntry = NextEntry->Flink) 656 { 657 SavedLinkInformation = CONTAINING_RECORD(NextEntry, 658 SAVED_LINK_INFORMATION, 659 SavedLinksListEntry); 660 661 /* Find the one that matches */ 662 if (SavedLinkInformation->UniqueId->UniqueIdLength == UniqueId->UniqueIdLength) 663 { 664 if (RtlCompareMemory(SavedLinkInformation->UniqueId->UniqueId, 665 UniqueId->UniqueId, 666 UniqueId->UniqueIdLength) == 667 UniqueId->UniqueIdLength) 668 { 669 /* Remove it and return it */ 670 RemoveEntryList(&(SavedLinkInformation->SavedLinksListEntry)); 671 return SavedLinkInformation; 672 } 673 } 674 } 675 676 /* None found (none removed) */ 677 return NULL; 678 } 679 680 /* 681 * @implemented 682 */ 683 NTSTATUS 684 QuerySuggestedLinkName(IN PUNICODE_STRING SymbolicName, 685 OUT PUNICODE_STRING SuggestedLinkName, 686 OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks) 687 { 688 PIRP Irp; 689 KEVENT Event; 690 NTSTATUS Status; 691 USHORT NameLength; 692 PFILE_OBJECT FileObject; 693 PDEVICE_OBJECT DeviceObject; 694 IO_STATUS_BLOCK IoStatusBlock; 695 PIO_STACK_LOCATION IoStackLocation; 696 PMOUNTDEV_SUGGESTED_LINK_NAME IoCtlSuggested; 697 698 /* First, get device */ 699 Status = IoGetDeviceObjectPointer(SymbolicName, 700 FILE_READ_ATTRIBUTES, 701 &FileObject, 702 &DeviceObject); 703 if (!NT_SUCCESS(Status)) 704 { 705 return Status; 706 } 707 708 /* Then, get attached device */ 709 DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject); 710 711 /* Then, prepare buffer to query suggested name */ 712 IoCtlSuggested = AllocatePool(sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)); 713 if (!IoCtlSuggested) 714 { 715 Status = STATUS_INSUFFICIENT_RESOURCES; 716 goto Dereference; 717 } 718 719 /* Prepare request */ 720 KeInitializeEvent(&Event, NotificationEvent, FALSE); 721 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME, 722 DeviceObject, 723 NULL, 724 0, 725 IoCtlSuggested, 726 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME), 727 FALSE, 728 &Event, 729 &IoStatusBlock); 730 if (!Irp) 731 { 732 Status = STATUS_INSUFFICIENT_RESOURCES; 733 goto Release; 734 } 735 736 IoStackLocation = IoGetNextIrpStackLocation(Irp); 737 IoStackLocation->FileObject = FileObject; 738 739 /* And ask */ 740 Status = IoCallDriver(DeviceObject, Irp); 741 if (Status == STATUS_PENDING) 742 { 743 KeWaitForSingleObject(&Event, Executive, KernelMode, 744 FALSE, NULL); 745 Status = IoStatusBlock.Status; 746 } 747 748 /* Overflow? Normal */ 749 if (Status == STATUS_BUFFER_OVERFLOW) 750 { 751 /* Reallocate big enough buffer */ 752 NameLength = IoCtlSuggested->NameLength + sizeof(MOUNTDEV_SUGGESTED_LINK_NAME); 753 FreePool(IoCtlSuggested); 754 755 IoCtlSuggested = AllocatePool(NameLength); 756 if (!IoCtlSuggested) 757 { 758 Status = STATUS_INSUFFICIENT_RESOURCES; 759 goto Dereference; 760 } 761 762 /* And reask */ 763 KeInitializeEvent(&Event, NotificationEvent, FALSE); 764 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME, 765 DeviceObject, 766 NULL, 767 0, 768 IoCtlSuggested, 769 NameLength, 770 FALSE, 771 &Event, 772 &IoStatusBlock); 773 if (!Irp) 774 { 775 Status = STATUS_INSUFFICIENT_RESOURCES; 776 goto Release; 777 } 778 779 IoStackLocation = IoGetNextIrpStackLocation(Irp); 780 IoStackLocation->FileObject = FileObject; 781 782 Status = IoCallDriver(DeviceObject, Irp); 783 if (Status == STATUS_PENDING) 784 { 785 KeWaitForSingleObject(&Event, Executive, KernelMode, 786 FALSE, NULL); 787 Status = IoStatusBlock.Status; 788 } 789 } 790 791 if (!NT_SUCCESS(Status)) 792 { 793 goto Release; 794 } 795 796 /* Now we have suggested name, copy it */ 797 SuggestedLinkName->Length = IoCtlSuggested->NameLength; 798 SuggestedLinkName->MaximumLength = IoCtlSuggested->NameLength + sizeof(UNICODE_NULL); 799 SuggestedLinkName->Buffer = AllocatePool(IoCtlSuggested->NameLength + sizeof(UNICODE_NULL)); 800 if (!SuggestedLinkName->Buffer) 801 { 802 Status = STATUS_INSUFFICIENT_RESOURCES; 803 } 804 else 805 { 806 RtlCopyMemory(SuggestedLinkName->Buffer, IoCtlSuggested->Name, IoCtlSuggested->NameLength); 807 SuggestedLinkName->Buffer[SuggestedLinkName->Length / sizeof(WCHAR)] = UNICODE_NULL; 808 } 809 810 /* Also return its priority */ 811 *UseOnlyIfThereAreNoOtherLinks = IoCtlSuggested->UseOnlyIfThereAreNoOtherLinks; 812 813 Release: 814 FreePool(IoCtlSuggested); 815 816 Dereference: 817 ObDereferenceObject(DeviceObject); 818 ObDereferenceObject(FileObject); 819 820 return Status; 821 } 822 823 /* 824 * @implemented 825 */ 826 BOOLEAN 827 RedirectSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation, 828 IN PUNICODE_STRING DosName, 829 IN PUNICODE_STRING NewLink) 830 { 831 PLIST_ENTRY NextEntry; 832 PSYMLINK_INFORMATION SymlinkInformation; 833 834 /* Find the link */ 835 for (NextEntry = SavedLinkInformation->SymbolicLinksListHead.Flink; 836 NextEntry != &(SavedLinkInformation->SymbolicLinksListHead); 837 NextEntry = NextEntry->Flink) 838 { 839 SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); 840 841 if (!RtlEqualUnicodeString(DosName, &(SymlinkInformation->Name), TRUE)) 842 { 843 /* Delete old link */ 844 GlobalDeleteSymbolicLink(DosName); 845 /* Set its new location */ 846 GlobalCreateSymbolicLink(DosName, NewLink); 847 848 /* And remove it from the list (not valid any more) */ 849 RemoveEntryList(&(SymlinkInformation->SymbolicLinksListEntry)); 850 FreePool(SymlinkInformation->Name.Buffer); 851 FreePool(SymlinkInformation); 852 853 return TRUE; 854 } 855 } 856 857 return FALSE; 858 } 859 860 /* 861 * @implemented 862 */ 863 VOID 864 DeleteSymbolicLinkNameFromMemory(IN PDEVICE_EXTENSION DeviceExtension, 865 IN PUNICODE_STRING SymbolicLink, 866 IN BOOLEAN MarkOffline) 867 { 868 PLIST_ENTRY DeviceEntry, SymbolEntry; 869 PDEVICE_INFORMATION DeviceInformation; 870 PSYMLINK_INFORMATION SymlinkInformation; 871 872 /* First of all, ensure we have devices */ 873 if (IsListEmpty(&(DeviceExtension->DeviceListHead))) 874 { 875 return; 876 } 877 878 /* Then, look for the symbolic name */ 879 for (DeviceEntry = DeviceExtension->DeviceListHead.Flink; 880 DeviceEntry != &(DeviceExtension->DeviceListHead); 881 DeviceEntry = DeviceEntry->Flink) 882 { 883 DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry); 884 885 for (SymbolEntry = DeviceInformation->SymbolicLinksListHead.Flink; 886 SymbolEntry != &(DeviceInformation->SymbolicLinksListHead); 887 SymbolEntry = SymbolEntry->Flink) 888 { 889 SymlinkInformation = CONTAINING_RECORD(SymbolEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); 890 891 /* One we have found it */ 892 if (RtlCompareUnicodeString(SymbolicLink, &(SymlinkInformation->Name), TRUE) == 0) 893 { 894 /* Check if caller just want it to be offline */ 895 if (MarkOffline) 896 { 897 SymlinkInformation->Online = FALSE; 898 } 899 else 900 { 901 /* If not, delete it & notify */ 902 SendLinkDeleted(&(DeviceInformation->SymbolicName), SymbolicLink); 903 RemoveEntryList(&(SymlinkInformation->SymbolicLinksListEntry)); 904 905 FreePool(SymlinkInformation->Name.Buffer); 906 FreePool(SymlinkInformation); 907 } 908 909 /* No need to go farther */ 910 return; 911 } 912 } 913 } 914 915 return; 916 } 917 918 /* 919 * @implemented 920 */ 921 BOOLEAN 922 IsDriveLetter(PUNICODE_STRING SymbolicName) 923 { 924 WCHAR Letter, Colon; 925 926 /* We must have a precise length */ 927 if (SymbolicName->Length != DosDevices.Length + 2 * sizeof(WCHAR)) 928 { 929 return FALSE; 930 } 931 932 /* Must start with the DosDevices prefix */ 933 if (!RtlPrefixUnicodeString(&DosDevices, SymbolicName, TRUE)) 934 { 935 return FALSE; 936 } 937 938 /* Check if letter is correct */ 939 Letter = SymbolicName->Buffer[DosDevices.Length / sizeof(WCHAR)]; 940 if ((Letter < L'A' || Letter > L'Z') && Letter != (WCHAR)-1) 941 { 942 return FALSE; 943 } 944 945 /* And finally it must end with a colon */ 946 Colon = SymbolicName->Buffer[DosDevices.Length / sizeof(WCHAR) + 1]; 947 if (Colon != L':') 948 { 949 return FALSE; 950 } 951 952 return TRUE; 953 } 954 955 /* 956 * @implemented 957 */ 958 NTSTATUS 959 MountMgrQuerySymbolicLink(IN PUNICODE_STRING SymbolicName, 960 IN OUT PUNICODE_STRING LinkTarget) 961 { 962 NTSTATUS Status; 963 HANDLE LinkHandle; 964 OBJECT_ATTRIBUTES ObjectAttributes; 965 966 /* Open the symbolic link */ 967 InitializeObjectAttributes(&ObjectAttributes, 968 SymbolicName, 969 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 970 NULL, 971 NULL); 972 973 Status = ZwOpenSymbolicLinkObject(&LinkHandle, 974 GENERIC_READ, 975 &ObjectAttributes); 976 if (!NT_SUCCESS(Status)) 977 { 978 return Status; 979 } 980 981 /* Query its target */ 982 Status = ZwQuerySymbolicLinkObject(LinkHandle, 983 LinkTarget, 984 NULL); 985 986 ZwClose(LinkHandle); 987 988 if (!NT_SUCCESS(Status)) 989 { 990 return Status; 991 } 992 993 if (LinkTarget->Length <= sizeof(WCHAR)) 994 { 995 return Status; 996 } 997 998 /* If it's not finished by \, just return */ 999 if (LinkTarget->Buffer[LinkTarget->Length / sizeof(WCHAR) - 1] != L'\\') 1000 { 1001 return Status; 1002 } 1003 1004 /* Otherwise, ensure to drop the tailing \ */ 1005 LinkTarget->Length -= sizeof(WCHAR); 1006 LinkTarget->Buffer[LinkTarget->Length / sizeof(WCHAR)] = UNICODE_NULL; 1007 1008 return Status; 1009 } 1010