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