1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: dll/win32/kernel32/client/file/volume.c 5 * PURPOSE: File volume functions 6 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl) 7 * Erik Bos, Alexandre Julliard : 8 * GetLogicalDriveStringsA, 9 * GetLogicalDriveStringsW, GetLogicalDrives 10 * Pierre Schweitzer (pierre@reactos.org) 11 * UPDATE HISTORY: 12 * Created 01/11/98 13 */ 14 //WINE copyright notice: 15 /* 16 * DOS drives handling functions 17 * 18 * Copyright 1993 Erik Bos 19 * Copyright 1996 Alexandre Julliard 20 */ 21 22 #include <k32.h> 23 #define NDEBUG 24 #include <debug.h> 25 26 27 /* 28 * @implemented 29 */ 30 BOOL 31 WINAPI 32 GetVolumeInformationA(IN LPCSTR lpRootPathName, 33 IN LPSTR lpVolumeNameBuffer, 34 IN DWORD nVolumeNameSize, 35 OUT LPDWORD lpVolumeSerialNumber OPTIONAL, 36 OUT LPDWORD lpMaximumComponentLength OPTIONAL, 37 OUT LPDWORD lpFileSystemFlags OPTIONAL, 38 OUT LPSTR lpFileSystemNameBuffer OPTIONAL, 39 IN DWORD nFileSystemNameSize) 40 { 41 BOOL Ret; 42 NTSTATUS Status; 43 PUNICODE_STRING RootPathNameU; 44 ANSI_STRING VolumeName, FileSystemName; 45 UNICODE_STRING VolumeNameU, FileSystemNameU; 46 47 /* If no root path provided, default to \ */ 48 if (lpRootPathName == NULL) 49 { 50 lpRootPathName = "\\"; 51 } 52 53 /* Convert root path to unicode */ 54 RootPathNameU = Basep8BitStringToStaticUnicodeString(lpRootPathName); 55 if (RootPathNameU == NULL) 56 { 57 return FALSE; 58 } 59 60 /* Init all our STRINGS (U/A) */ 61 VolumeNameU.Buffer = NULL; 62 VolumeNameU.MaximumLength = 0; 63 FileSystemNameU.Buffer = NULL; 64 FileSystemNameU.MaximumLength = 0; 65 66 VolumeName.Buffer = lpVolumeNameBuffer; 67 VolumeName.MaximumLength = nVolumeNameSize + 1; 68 FileSystemName.Buffer = lpFileSystemNameBuffer; 69 FileSystemName.MaximumLength = nFileSystemNameSize + 1; 70 71 /* Assume failure for now */ 72 Ret = FALSE; 73 74 /* If caller wants volume name, allocate a buffer to receive it */ 75 if (lpVolumeNameBuffer != NULL) 76 { 77 VolumeNameU.MaximumLength = sizeof(WCHAR) * (nVolumeNameSize + 1); 78 VolumeNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, 79 VolumeNameU.MaximumLength); 80 if (VolumeNameU.Buffer == NULL) 81 { 82 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 83 goto CleanAndQuit; 84 } 85 } 86 87 /* If caller wants file system name, allocate a buffer to receive it */ 88 if (lpFileSystemNameBuffer != NULL) 89 { 90 FileSystemNameU.MaximumLength = sizeof(WCHAR) * (nFileSystemNameSize + 1); 91 FileSystemNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, 92 FileSystemNameU.MaximumLength); 93 if (FileSystemNameU.Buffer == NULL) 94 { 95 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 96 goto CleanAndQuit; 97 } 98 } 99 100 /* Call W */ 101 Ret = GetVolumeInformationW(RootPathNameU->Buffer, VolumeNameU.Buffer, 102 nVolumeNameSize, lpVolumeSerialNumber, 103 lpMaximumComponentLength, lpFileSystemFlags, 104 FileSystemNameU.Buffer, nFileSystemNameSize); 105 /* If it succeed, convert back to ANSI */ 106 if (Ret) 107 { 108 if (lpVolumeNameBuffer != NULL) 109 { 110 RtlInitUnicodeString(&VolumeNameU, VolumeNameU.Buffer); 111 Status = RtlUnicodeStringToAnsiString(&VolumeName, &VolumeNameU, FALSE); 112 if (!NT_SUCCESS(Status)) 113 { 114 BaseSetLastNTError(Status); 115 Ret = FALSE; 116 117 goto CleanAndQuit; 118 } 119 } 120 121 if (lpFileSystemNameBuffer != NULL) 122 { 123 RtlInitUnicodeString(&FileSystemNameU, FileSystemNameU.Buffer); 124 Status = RtlUnicodeStringToAnsiString(&FileSystemName, &FileSystemNameU, FALSE); 125 if (!NT_SUCCESS(Status)) 126 { 127 BaseSetLastNTError(Status); 128 Ret = FALSE; 129 130 goto CleanAndQuit; 131 } 132 } 133 } 134 135 /* Clean and quit */ 136 CleanAndQuit: 137 if (VolumeNameU.Buffer != NULL) 138 { 139 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameU.Buffer); 140 } 141 142 if (FileSystemNameU.Buffer != NULL) 143 { 144 RtlFreeHeap(RtlGetProcessHeap(), 0, FileSystemNameU.Buffer); 145 } 146 147 return Ret; 148 } 149 150 /* 151 * @implemented 152 */ 153 BOOL 154 IsThisARootDirectory(IN HANDLE VolumeHandle, 155 IN PUNICODE_STRING NtPathName) 156 { 157 NTSTATUS Status; 158 IO_STATUS_BLOCK IoStatusBlock; 159 struct 160 { 161 FILE_NAME_INFORMATION; 162 WCHAR Buffer[MAX_PATH]; 163 } FileNameInfo; 164 165 /* If we have a handle, query the name */ 166 if (VolumeHandle) 167 { 168 Status = NtQueryInformationFile(VolumeHandle, &IoStatusBlock, &FileNameInfo, sizeof(FileNameInfo), FileNameInformation); 169 if (!NT_SUCCESS(Status)) 170 { 171 return FALSE; 172 } 173 174 /* Check we properly end with a \ */ 175 if (FileNameInfo.FileName[FileNameInfo.FileNameLength / sizeof(WCHAR) - 1] != L'\\') 176 { 177 return FALSE; 178 } 179 } 180 181 /* If we have a path */ 182 if (NtPathName != NULL) 183 { 184 HANDLE LinkHandle; 185 WCHAR Buffer[512]; 186 ULONG ReturnedLength; 187 UNICODE_STRING LinkTarget; 188 OBJECT_ATTRIBUTES ObjectAttributes; 189 190 NtPathName->Length -= sizeof(WCHAR); 191 192 InitializeObjectAttributes(&ObjectAttributes, NtPathName, 193 OBJ_CASE_INSENSITIVE, 194 NULL, NULL); 195 196 /* Try to see whether that's a symbolic name */ 197 Status = NtOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes); 198 NtPathName->Length += sizeof(WCHAR); 199 if (!NT_SUCCESS(Status)) 200 { 201 return FALSE; 202 } 203 204 /* If so, query the target */ 205 LinkTarget.Buffer = Buffer; 206 LinkTarget.Length = 0; 207 LinkTarget.MaximumLength = sizeof(Buffer); 208 209 Status = NtQuerySymbolicLinkObject(LinkHandle, &LinkTarget, &ReturnedLength); 210 NtClose(LinkHandle); 211 /* A root directory (NtName) is a symbolic link */ 212 if (!NT_SUCCESS(Status)) 213 { 214 return FALSE; 215 } 216 } 217 218 return TRUE; 219 } 220 221 /* 222 * @implemented 223 */ 224 BOOL 225 WINAPI 226 GetVolumeInformationW(IN LPCWSTR lpRootPathName, 227 IN LPWSTR lpVolumeNameBuffer, 228 IN DWORD nVolumeNameSize, 229 OUT LPDWORD lpVolumeSerialNumber OPTIONAL, 230 OUT LPDWORD lpMaximumComponentLength OPTIONAL, 231 OUT LPDWORD lpFileSystemFlags OPTIONAL, 232 OUT LPWSTR lpFileSystemNameBuffer OPTIONAL, 233 IN DWORD nFileSystemNameSize) 234 { 235 BOOL Ret; 236 NTSTATUS Status; 237 HANDLE VolumeHandle; 238 LPCWSTR RootPathName; 239 UNICODE_STRING NtPathName; 240 IO_STATUS_BLOCK IoStatusBlock; 241 OBJECT_ATTRIBUTES ObjectAttributes; 242 PFILE_FS_VOLUME_INFORMATION VolumeInfo; 243 PFILE_FS_ATTRIBUTE_INFORMATION VolumeAttr; 244 ULONG OldMode, VolumeInfoSize, VolumeAttrSize; 245 246 /* If no root path provided, default to \ */ 247 if (lpRootPathName == NULL) 248 { 249 RootPathName = L"\\"; 250 } 251 else 252 { 253 RootPathName = lpRootPathName; 254 } 255 256 /* Convert length to bytes */ 257 nVolumeNameSize *= sizeof(WCHAR); 258 nFileSystemNameSize *= sizeof(WCHAR); 259 260 /* Convert to NT name */ 261 if (!RtlDosPathNameToNtPathName_U(RootPathName, &NtPathName, NULL, NULL)) 262 { 263 SetLastError(ERROR_PATH_NOT_FOUND); 264 return FALSE; 265 } 266 267 /* Check we really end with a backslash */ 268 if (NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] != L'\\') 269 { 270 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 271 BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID); 272 return FALSE; 273 } 274 275 /* Try to open the received path */ 276 InitializeObjectAttributes(&ObjectAttributes, &NtPathName, 277 OBJ_CASE_INSENSITIVE, 278 NULL, NULL); 279 280 /* No errors to the user */ 281 RtlSetThreadErrorMode(RTL_SEM_FAILCRITICALERRORS, &OldMode); 282 Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, 0, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT); 283 RtlSetThreadErrorMode(OldMode, NULL); 284 if (!NT_SUCCESS(Status)) 285 { 286 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 287 BaseSetLastNTError(Status); 288 return FALSE; 289 } 290 291 /* Check whether that's a root directory */ 292 if (!IsThisARootDirectory(VolumeHandle, &NtPathName)) 293 { 294 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 295 NtClose(VolumeHandle); 296 SetLastError(ERROR_DIR_NOT_ROOT); 297 return FALSE; 298 } 299 300 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 301 302 /* Assume we don't need to query FileFsVolumeInformation */ 303 VolumeInfo = NULL; 304 /* If user wants volume name, allocate a buffer to query it */ 305 if (lpVolumeNameBuffer != NULL) 306 { 307 VolumeInfoSize = nVolumeNameSize + sizeof(FILE_FS_VOLUME_INFORMATION); 308 } 309 /* If user just wants the serial number, allocate a dummy buffer */ 310 else if (lpVolumeSerialNumber != NULL) 311 { 312 VolumeInfoSize = MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_VOLUME_INFORMATION); 313 } 314 /* Otherwise, nothing to query */ 315 else 316 { 317 VolumeInfoSize = 0; 318 } 319 320 /* If we're to query, allocate a big enough buffer */ 321 if (VolumeInfoSize != 0) 322 { 323 VolumeInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeInfoSize); 324 if (VolumeInfo == NULL) 325 { 326 NtClose(VolumeHandle); 327 BaseSetLastNTError(STATUS_NO_MEMORY); 328 return FALSE; 329 } 330 } 331 332 /* Assume we don't need to query FileFsAttributeInformation */ 333 VolumeAttr = NULL; 334 /* If user wants filesystem name, allocate a buffer to query it */ 335 if (lpFileSystemNameBuffer != NULL) 336 { 337 VolumeAttrSize = nFileSystemNameSize + sizeof(FILE_FS_ATTRIBUTE_INFORMATION); 338 } 339 /* If user just wants max compo len or flags, allocate a dummy buffer */ 340 else if (lpMaximumComponentLength != NULL || lpFileSystemFlags != NULL) 341 { 342 VolumeAttrSize = MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION); 343 } 344 else 345 { 346 VolumeAttrSize = 0; 347 } 348 349 /* If we're to query, allocate a big enough buffer */ 350 if (VolumeAttrSize != 0) 351 { 352 VolumeAttr = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeAttrSize); 353 if (VolumeAttr == NULL) 354 { 355 if (VolumeInfo != NULL) 356 { 357 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeInfo); 358 } 359 360 NtClose(VolumeHandle); 361 BaseSetLastNTError(STATUS_NO_MEMORY); 362 return FALSE; 363 } 364 } 365 366 /* Assume we'll fail */ 367 Ret = FALSE; 368 369 /* If we're to query FileFsVolumeInformation, do it now! */ 370 if (VolumeInfo != NULL) 371 { 372 Status = NtQueryVolumeInformationFile(VolumeHandle, &IoStatusBlock, VolumeInfo, VolumeInfoSize, FileFsVolumeInformation); 373 if (!NT_SUCCESS(Status)) 374 { 375 BaseSetLastNTError(Status); 376 goto CleanAndQuit; 377 } 378 } 379 380 /* If we're to query FileFsAttributeInformation, do it now! */ 381 if (VolumeAttr != NULL) 382 { 383 Status = NtQueryVolumeInformationFile(VolumeHandle, &IoStatusBlock, VolumeAttr, VolumeAttrSize, FileFsAttributeInformation); 384 if (!NT_SUCCESS(Status)) 385 { 386 BaseSetLastNTError(Status); 387 goto CleanAndQuit; 388 } 389 } 390 391 /* If user wants volume name */ 392 if (lpVolumeNameBuffer != NULL) 393 { 394 /* Check its buffer can hold it (+ 0) */ 395 if (VolumeInfo->VolumeLabelLength >= nVolumeNameSize) 396 { 397 SetLastError(ERROR_BAD_LENGTH); 398 goto CleanAndQuit; 399 } 400 401 /* Copy and zero */ 402 RtlCopyMemory(lpVolumeNameBuffer, VolumeInfo->VolumeLabel, VolumeInfo->VolumeLabelLength); 403 lpVolumeNameBuffer[VolumeInfo->VolumeLabelLength / sizeof(WCHAR)] = UNICODE_NULL; 404 } 405 406 /* If user wants wants serial number, return it */ 407 if (lpVolumeSerialNumber != NULL) 408 { 409 *lpVolumeSerialNumber = VolumeInfo->VolumeSerialNumber; 410 } 411 412 /* If user wants filesystem name */ 413 if (lpFileSystemNameBuffer != NULL) 414 { 415 /* Check its buffer can hold it (+ 0) */ 416 if (VolumeAttr->FileSystemNameLength >= nFileSystemNameSize) 417 { 418 SetLastError(ERROR_BAD_LENGTH); 419 goto CleanAndQuit; 420 } 421 422 /* Copy and zero */ 423 RtlCopyMemory(lpFileSystemNameBuffer, VolumeAttr->FileSystemName, VolumeAttr->FileSystemNameLength); 424 lpFileSystemNameBuffer[VolumeAttr->FileSystemNameLength / sizeof(WCHAR)] = UNICODE_NULL; 425 } 426 427 /* If user wants wants max compo len, return it */ 428 if (lpMaximumComponentLength != NULL) 429 { 430 *lpMaximumComponentLength = VolumeAttr->MaximumComponentNameLength; 431 } 432 433 /* If user wants wants FS flags, return them */ 434 if (lpFileSystemFlags != NULL) 435 { 436 *lpFileSystemFlags = VolumeAttr->FileSystemAttributes; 437 } 438 439 /* We did it! */ 440 Ret = TRUE; 441 442 CleanAndQuit: 443 NtClose(VolumeHandle); 444 445 if (VolumeInfo != NULL) 446 { 447 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeInfo); 448 } 449 450 if (VolumeAttr != NULL) 451 { 452 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeAttr); 453 } 454 455 return Ret; 456 } 457 458 /* 459 * @implemented 460 */ 461 BOOL 462 WINAPI 463 SetVolumeLabelA(IN LPCSTR lpRootPathName, 464 IN LPCSTR lpVolumeName OPTIONAL) /* NULL if deleting label */ 465 { 466 BOOL Ret; 467 UNICODE_STRING VolumeNameU; 468 PUNICODE_STRING RootPathNameU; 469 470 if (lpRootPathName == NULL) 471 { 472 lpRootPathName = "\\"; 473 } 474 475 RootPathNameU = Basep8BitStringToStaticUnicodeString(lpRootPathName); 476 if (RootPathNameU == NULL) 477 { 478 return FALSE; 479 } 480 481 if (lpVolumeName != NULL) 482 { 483 if (!Basep8BitStringToDynamicUnicodeString(&VolumeNameU, lpVolumeName)) 484 { 485 return FALSE; 486 } 487 } 488 else 489 { 490 VolumeNameU.Buffer = NULL; 491 } 492 493 Ret = SetVolumeLabelW(RootPathNameU->Buffer, VolumeNameU.Buffer); 494 RtlFreeUnicodeString(&VolumeNameU); 495 return Ret; 496 } 497 498 /* 499 * @implemented 500 */ 501 BOOL 502 WINAPI 503 SetVolumeLabelW(IN LPCWSTR lpRootPathName, 504 IN LPCWSTR lpVolumeName OPTIONAL) /* NULL if deleting label */ 505 { 506 BOOL Ret; 507 NTSTATUS Status; 508 PWSTR VolumeRoot; 509 HANDLE VolumeHandle; 510 WCHAR VolumeGuid[MAX_PATH]; 511 IO_STATUS_BLOCK IoStatusBlock; 512 OBJECT_ATTRIBUTES ObjectAttributes; 513 PFILE_FS_LABEL_INFORMATION FsLabelInfo; 514 UNICODE_STRING VolumeName, NtVolumeName; 515 516 /* If no root path provided, default to \ */ 517 VolumeRoot = L"\\"; 518 519 /* If user wants to set a label, make it a string */ 520 if (lpVolumeName != NULL) 521 { 522 RtlInitUnicodeString(&VolumeName, lpVolumeName); 523 } 524 else 525 { 526 VolumeName.Length = 0; 527 VolumeName.MaximumLength = 0; 528 VolumeName.Buffer = NULL; 529 } 530 531 /* If we received a volume, try to get its GUID name */ 532 if (lpRootPathName != NULL) 533 { 534 Ret = GetVolumeNameForVolumeMountPointW(lpRootPathName, VolumeGuid, MAX_PATH); 535 } 536 else 537 { 538 Ret = FALSE; 539 } 540 541 /* If we got the GUID name, use it */ 542 if (Ret) 543 { 544 VolumeRoot = VolumeGuid; 545 } 546 else 547 { 548 /* Otherwise, use the name provided by the caller */ 549 if (lpRootPathName != NULL) 550 { 551 VolumeRoot = (PWSTR)lpRootPathName; 552 } 553 } 554 555 /* Convert to a NT path */ 556 if (!RtlDosPathNameToNtPathName_U(VolumeRoot, &NtVolumeName, NULL, NULL)) 557 { 558 SetLastError(ERROR_PATH_NOT_FOUND); 559 return FALSE; 560 } 561 562 563 /* Check we really end with a backslash */ 564 if (NtVolumeName.Buffer[(NtVolumeName.Length / sizeof(WCHAR)) - 1] != L'\\') 565 { 566 RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer); 567 BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID); 568 return FALSE; 569 } 570 571 /* Try to open the root directory */ 572 InitializeObjectAttributes(&ObjectAttributes, &NtVolumeName, 573 OBJ_CASE_INSENSITIVE, NULL, NULL); 574 575 Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE | FILE_WRITE_DATA, 576 &ObjectAttributes, &IoStatusBlock, 577 FILE_SHARE_READ | FILE_SHARE_WRITE, 578 FILE_SYNCHRONOUS_IO_NONALERT); 579 if (!NT_SUCCESS(Status)) 580 { 581 RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer); 582 BaseSetLastNTError(Status); 583 return FALSE; 584 } 585 586 /* Validate it's really a root path */ 587 if (!IsThisARootDirectory(VolumeHandle, NULL)) 588 { 589 RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer); 590 NtClose(VolumeHandle); 591 SetLastError(ERROR_DIR_NOT_ROOT); 592 return FALSE; 593 } 594 595 /* Done */ 596 NtClose(VolumeHandle); 597 598 /* Now, open the volume to perform the label change */ 599 NtVolumeName.Length -= sizeof(WCHAR); 600 InitializeObjectAttributes(&ObjectAttributes, &NtVolumeName, 601 OBJ_CASE_INSENSITIVE, NULL, NULL); 602 603 Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE | FILE_WRITE_DATA, 604 &ObjectAttributes, &IoStatusBlock, 605 FILE_SHARE_READ | FILE_SHARE_WRITE, 606 FILE_SYNCHRONOUS_IO_NONALERT); 607 608 RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer); 609 610 if (!NT_SUCCESS(Status)) 611 { 612 BaseSetLastNTError(Status); 613 return FALSE; 614 } 615 616 /* Assume success */ 617 Ret = TRUE; 618 619 /* Allocate a buffer that can hold new label and its size */ 620 FsLabelInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(FILE_FS_LABEL_INFORMATION) + VolumeName.Length); 621 if (FsLabelInfo != NULL) 622 { 623 /* Copy name and set its size */ 624 RtlCopyMemory(FsLabelInfo->VolumeLabel, VolumeName.Buffer, VolumeName.Length); 625 FsLabelInfo->VolumeLabelLength = VolumeName.Length; 626 627 /* And finally, set new label */ 628 Status = NtSetVolumeInformationFile(VolumeHandle, &IoStatusBlock, FsLabelInfo, sizeof(FILE_FS_LABEL_INFORMATION) + VolumeName.Length, FileFsLabelInformation); 629 } 630 else 631 { 632 /* Allocation failed */ 633 Status = STATUS_NO_MEMORY; 634 } 635 636 /* In case of failure, set status and mark failure */ 637 if (!NT_SUCCESS(Status)) 638 { 639 BaseSetLastNTError(Status); 640 Ret = FALSE; 641 } 642 643 /* We're done */ 644 NtClose(VolumeHandle); 645 646 /* Free buffer if required */ 647 if (FsLabelInfo != NULL) 648 { 649 RtlFreeHeap(RtlGetProcessHeap(), 0, FsLabelInfo); 650 } 651 652 return Ret; 653 } 654 655 /* 656 * @implemented (Wine 13 sep 2008) 657 */ 658 HANDLE 659 WINAPI 660 FindFirstVolumeW(IN LPWSTR volume, 661 IN DWORD len) 662 { 663 DWORD size = 1024; 664 DWORD br; 665 HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, 666 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE ); 667 if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE; 668 669 for (;;) 670 { 671 MOUNTMGR_MOUNT_POINT input; 672 MOUNTMGR_MOUNT_POINTS *output; 673 674 if (!(output = RtlAllocateHeap( RtlGetProcessHeap(), 0, size ))) 675 { 676 SetLastError( ERROR_NOT_ENOUGH_MEMORY ); 677 break; 678 } 679 memset( &input, 0, sizeof(input) ); 680 681 if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input), 682 output, size, &br, NULL )) 683 { 684 if (GetLastError() != ERROR_MORE_DATA) break; 685 size = output->Size; 686 RtlFreeHeap( RtlGetProcessHeap(), 0, output ); 687 continue; 688 } 689 CloseHandle( mgr ); 690 /* abuse the Size field to store the current index */ 691 output->Size = 0; 692 if (!FindNextVolumeW( output, volume, len )) 693 { 694 RtlFreeHeap( RtlGetProcessHeap(), 0, output ); 695 return INVALID_HANDLE_VALUE; 696 } 697 return (HANDLE)output; 698 } 699 CloseHandle( mgr ); 700 return INVALID_HANDLE_VALUE; 701 } 702 703 /* 704 * @implemented (Wine 13 sep 2008) 705 */ 706 HANDLE 707 WINAPI 708 FindFirstVolumeA(IN LPSTR volume, 709 IN DWORD len) 710 { 711 WCHAR *buffer = NULL; 712 HANDLE handle; 713 714 buffer = RtlAllocateHeap( RtlGetProcessHeap(), 0, len * sizeof(WCHAR) ); 715 716 if (!buffer) 717 { 718 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 719 return INVALID_HANDLE_VALUE; 720 } 721 722 handle = FindFirstVolumeW( buffer, len ); 723 724 if (handle != INVALID_HANDLE_VALUE) 725 { 726 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) 727 { 728 FindVolumeClose( handle ); 729 handle = INVALID_HANDLE_VALUE; 730 } 731 } 732 RtlFreeHeap( RtlGetProcessHeap(), 0, buffer ); 733 return handle; 734 } 735 736 /* 737 * @implemented (Wine 13 sep 2008) 738 */ 739 BOOL 740 WINAPI 741 FindVolumeClose(IN HANDLE hFindVolume) 742 { 743 return RtlFreeHeap(RtlGetProcessHeap(), 0, hFindVolume); 744 } 745 746 /* 747 * @implemented 748 */ 749 BOOL 750 WINAPI 751 GetVolumePathNameA(IN LPCSTR lpszFileName, 752 IN LPSTR lpszVolumePathName, 753 IN DWORD cchBufferLength) 754 { 755 BOOL Ret; 756 PUNICODE_STRING FileNameU; 757 ANSI_STRING VolumePathName; 758 UNICODE_STRING VolumePathNameU; 759 760 /* Convert file name to unicode */ 761 FileNameU = Basep8BitStringToStaticUnicodeString(lpszFileName); 762 if (FileNameU == NULL) 763 { 764 return FALSE; 765 } 766 767 /* Initialize all the strings we'll need */ 768 VolumePathName.Buffer = lpszVolumePathName; 769 VolumePathName.Length = 0; 770 VolumePathName.MaximumLength = cchBufferLength - 1; 771 772 VolumePathNameU.Length = 0; 773 VolumePathNameU.MaximumLength = (cchBufferLength - 1) * sizeof(WCHAR) + sizeof(UNICODE_NULL); 774 /* Allocate a buffer for calling the -W */ 775 VolumePathNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumePathNameU.MaximumLength); 776 if (VolumePathNameU.Buffer == NULL) 777 { 778 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 779 return FALSE; 780 } 781 782 /* Call the -W implementation */ 783 Ret = GetVolumePathNameW(FileNameU->Buffer, VolumePathNameU.Buffer, cchBufferLength); 784 /* If it succeed */ 785 if (Ret) 786 { 787 NTSTATUS Status; 788 789 /* Convert back to ANSI */ 790 RtlInitUnicodeString(&VolumePathNameU, VolumePathNameU.Buffer); 791 Status = RtlUnicodeStringToAnsiString(&VolumePathName, &VolumePathNameU, FALSE); 792 /* If conversion failed, just set error code and fail the rest */ 793 if (!NT_SUCCESS(Status)) 794 { 795 BaseSetLastNTError(Status); 796 Ret = FALSE; 797 } 798 /* Otherwise, null terminate the string (it's OK, we computed -1) */ 799 else 800 { 801 VolumePathName.Buffer[VolumePathName.Length] = ANSI_NULL; 802 } 803 } 804 805 /* Free the buffer allocated for -W call */ 806 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNameU.Buffer); 807 return Ret; 808 } 809 810 /* 811 * @implemented 812 */ 813 BOOL 814 WINAPI 815 GetVolumePathNameW(IN LPCWSTR lpszFileName, 816 IN LPWSTR lpszVolumePathName, 817 IN DWORD cchBufferLength) 818 { 819 BOOL MountPoint; 820 DWORD FullPathLen; 821 WCHAR OldFilePart; 822 UNICODE_STRING FullPath; 823 PWSTR FullPathBuf, FilePart, VolumeNameBuf; 824 825 /* Probe for full path len */ 826 FullPathLen = GetFullPathNameW(lpszFileName, 0, NULL, NULL); 827 if (FullPathLen == 0) 828 { 829 return FALSE; 830 } 831 832 /* Allocate a big enough buffer to receive it */ 833 FullPathBuf = RtlAllocateHeap(RtlGetProcessHeap(), 0, (FullPathLen + 10) * sizeof(WCHAR)); 834 if (FullPathBuf == NULL) 835 { 836 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 837 return FALSE; 838 } 839 840 /* And get full path name */ 841 if (GetFullPathNameW(lpszFileName, FullPathLen + 10, FullPathBuf, &FilePart) == 0) 842 { 843 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf); 844 return FALSE; 845 } 846 847 /* Make a string out of it */ 848 RtlInitUnicodeString(&FullPath, FullPathBuf); 849 /* We will finish our string with '\', for ease of the parsing after */ 850 if (FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') 851 { 852 FullPath.Length += sizeof(WCHAR); 853 FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] = L'\\'; 854 FullPath.Buffer[FullPath.Length / sizeof(WCHAR)] = UNICODE_NULL; 855 } 856 857 /* Allocate a buffer big enough to receive our volume name */ 858 VolumeNameBuf = RtlAllocateHeap(RtlGetProcessHeap(), 0, 0x2000 * sizeof(WCHAR)); 859 if (VolumeNameBuf == NULL) 860 { 861 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf); 862 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 863 return FALSE; 864 } 865 866 /* We don't care about file part: we added an extra backslash, so there's no 867 * file, we're back at the dir level. 868 * We'll recompute file part afterwards 869 */ 870 FilePart = NULL; 871 /* Keep track of the letter we could drop to shorten the string */ 872 OldFilePart = UNICODE_NULL; 873 /* As long as querying volume name fails, keep looping */ 874 while (!BasepGetVolumeNameForVolumeMountPoint(FullPath.Buffer, VolumeNameBuf, 0x2000u, &MountPoint)) 875 { 876 USHORT LastSlash; 877 878 /* Not a mount point, but opening returning access denied? Assume it's one, just not 879 * a reparse backed one (classic mount point, a device)! 880 */ 881 if (!MountPoint && GetLastError() == ERROR_ACCESS_DENIED) 882 { 883 MountPoint = TRUE; 884 } 885 886 /* BasepGetVolumeNameForVolumeMountPoint failed, but returned a volume name. 887 * This can happen when we are given a reparse point where MountMgr could find associated 888 * volume name which is not a valid DOS volume 889 * A valid DOS name always starts with \\ 890 */ 891 if (VolumeNameBuf[0] != UNICODE_NULL && (FullPath.Buffer[0] != L'\\' || FullPath.Buffer[1] != L'\\')) 892 { 893 CHAR RootPathName[4]; 894 895 /* Construct a simple <letter>:\ string to get drive type */ 896 RootPathName[0] = FullPath.Buffer[0]; 897 RootPathName[1] = ':'; 898 RootPathName[2] = '\\'; 899 RootPathName[3] = ANSI_NULL; 900 901 /* If we weren't given a drive letter actually, or if that's not a remote drive 902 * Note: in this code path, we're recursive and stop fail loop 903 */ 904 if (FullPath.Buffer[1] != L':' || GetDriveTypeA(RootPathName) != DRIVE_REMOTE) 905 { 906 BOOL Ret; 907 908 /* We won't need the full path, we'll now work with the returned volume name */ 909 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf); 910 /* If it wasn't an NT name which was returned */ 911 if ((VolumeNameBuf[0] != L'\\') || (VolumeNameBuf[1] != L'?') || 912 (VolumeNameBuf[2] != L'?') || (VolumeNameBuf[3] != L'\\')) 913 { 914 PWSTR GlobalPath; 915 UNICODE_STRING GlobalRoot; 916 917 /* Create a new name in the NT namespace (from Win32) */ 918 RtlInitUnicodeString(&FullPath, VolumeNameBuf); 919 RtlInitUnicodeString(&GlobalRoot, L"\\\\?\\GLOBALROOT"); 920 921 /* We allocate a buffer than can contain both the namespace and the volume name */ 922 GlobalPath = RtlAllocateHeap(RtlGetProcessHeap(), 0, FullPath.Length + GlobalRoot.Length); 923 if (GlobalPath == NULL) 924 { 925 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameBuf); 926 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 927 return FALSE; 928 } 929 930 /* Fill in the new query name */ 931 RtlCopyMemory(GlobalPath, GlobalRoot.Buffer, GlobalRoot.Length); 932 RtlCopyMemory((PVOID)((ULONG_PTR)GlobalPath + GlobalRoot.Length), FullPath.Buffer, FullPath.Length); 933 GlobalPath[(FullPath.Length + GlobalRoot.Length) / sizeof(WCHAR)] = UNICODE_NULL; 934 935 /* Give it another try */ 936 Ret = GetVolumePathNameW(GlobalPath, lpszVolumePathName, cchBufferLength); 937 938 RtlFreeHeap(RtlGetProcessHeap(), 0, GlobalPath); 939 } 940 else 941 { 942 /* If we don't have a drive letter in the Win32 name space \\.\<letter>: */ 943 if ((VolumeNameBuf[4] != UNICODE_NULL) && (VolumeNameBuf[5] != L':')) 944 { 945 /* Shit our starting \\ */ 946 RtlInitUnicodeString(&FullPath, VolumeNameBuf); 947 RtlMoveMemory(VolumeNameBuf, (PVOID)((ULONG_PTR)VolumeNameBuf + (2 * sizeof(WCHAR))), FullPath.Length - (3 * sizeof(WCHAR))); 948 } 949 /* Otherwise, just make sure we're double \ at the being to query again with the 950 * proper namespace 951 */ 952 else 953 { 954 VolumeNameBuf[1] = L'\\'; 955 } 956 957 /* Give it another try */ 958 Ret = GetVolumePathNameW(VolumeNameBuf, lpszVolumePathName, cchBufferLength); 959 } 960 961 /* And done! */ 962 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameBuf); 963 return Ret; 964 } 965 } 966 967 /* No mount point but with a file part? Restore filepart and exit */ 968 if (!MountPoint && FilePart != NULL) 969 { 970 FilePart[0] = OldFilePart; 971 RtlInitUnicodeString(&FullPath, FullPathBuf); 972 break; 973 } 974 975 /* We cannot go down the path any longer, too small */ 976 if (FullPath.Length <= sizeof(WCHAR)) 977 { 978 break; 979 } 980 981 /* Prepare the next split */ 982 LastSlash = (FullPath.Length / sizeof(WCHAR)) - 2; 983 if (FullPath.Length / sizeof(WCHAR) != 2) 984 { 985 do 986 { 987 if (FullPath.Buffer[LastSlash] == L'\\') 988 { 989 break; 990 } 991 992 --LastSlash; 993 } while (LastSlash != 0); 994 } 995 996 /* We couldn't split path, quit */ 997 if (LastSlash == 0) 998 { 999 break; 1000 } 1001 1002 /* If that's a mount point, keep track of the directory name */ 1003 if (MountPoint) 1004 { 1005 FilePart = &FullPath.Buffer[LastSlash + 1]; 1006 OldFilePart = FilePart[0]; 1007 /* And null terminate the string */ 1008 FilePart[0] = UNICODE_NULL; 1009 } 1010 /* Otherwise, just null terminate the string */ 1011 else 1012 { 1013 FullPath.Buffer[LastSlash + 1] = UNICODE_NULL; 1014 } 1015 1016 /* We went down a bit in the path, fix the string and retry */ 1017 RtlInitUnicodeString(&FullPath, FullPathBuf); 1018 } 1019 1020 /* Once here, we'll return something from the full path buffer, so release 1021 * output buffer 1022 */ 1023 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameBuf); 1024 1025 /* Not a mount point, bail out */ 1026 if (!MountPoint && FilePart == NULL) 1027 { 1028 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf); 1029 return FALSE; 1030 } 1031 1032 /* Make sure we have enough room to copy our volume */ 1033 if ((cchBufferLength * sizeof(WCHAR)) < FullPath.Length + sizeof(UNICODE_NULL)) 1034 { 1035 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf); 1036 SetLastError(ERROR_FILENAME_EXCED_RANGE); 1037 return FALSE; 1038 } 1039 1040 /* Copy and null terminate */ 1041 RtlCopyMemory(lpszVolumePathName, FullPath.Buffer, FullPath.Length); 1042 lpszVolumePathName[FullPath.Length / sizeof(WCHAR)] = UNICODE_NULL; 1043 1044 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf); 1045 1046 /* Done! */ 1047 return TRUE; 1048 } 1049 1050 /* 1051 * @implemented 1052 */ 1053 BOOL 1054 WINAPI 1055 FindNextVolumeA(IN HANDLE handle, 1056 IN LPSTR volume, 1057 IN DWORD len) 1058 { 1059 WCHAR *buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, len * sizeof(WCHAR)); 1060 BOOL ret; 1061 1062 if (!buffer) 1063 { 1064 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 1065 return FALSE; 1066 } 1067 1068 if ((ret = FindNextVolumeW( handle, buffer, len ))) 1069 { 1070 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE; 1071 } 1072 1073 RtlFreeHeap(RtlGetProcessHeap(), 0, buffer); 1074 return ret; 1075 } 1076 1077 /* 1078 * @implemented 1079 */ 1080 BOOL 1081 WINAPI 1082 FindNextVolumeW(IN HANDLE handle, 1083 IN LPWSTR volume, 1084 IN DWORD len) 1085 { 1086 MOUNTMGR_MOUNT_POINTS *data = handle; 1087 1088 while (data->Size < data->NumberOfMountPoints) 1089 { 1090 static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',}; 1091 WCHAR *link = (WCHAR *)((char *)data + data->MountPoints[data->Size].SymbolicLinkNameOffset); 1092 DWORD size = data->MountPoints[data->Size].SymbolicLinkNameLength; 1093 data->Size++; 1094 /* skip non-volumes */ 1095 if (size < sizeof(volumeW) || memcmp( link, volumeW, sizeof(volumeW) )) continue; 1096 if (size + sizeof(WCHAR) >= len * sizeof(WCHAR)) 1097 { 1098 SetLastError( ERROR_FILENAME_EXCED_RANGE ); 1099 return FALSE; 1100 } 1101 memcpy( volume, link, size ); 1102 volume[1] = '\\'; /* map \??\ to \\?\ */ 1103 volume[size / sizeof(WCHAR)] = '\\'; /* Windows appends a backslash */ 1104 volume[size / sizeof(WCHAR) + 1] = 0; 1105 DPRINT( "returning entry %u %s\n", data->Size - 1, volume ); 1106 return TRUE; 1107 } 1108 SetLastError( ERROR_NO_MORE_FILES ); 1109 return FALSE; 1110 } 1111 1112 /* 1113 * @implemented 1114 */ 1115 BOOL 1116 WINAPI 1117 GetVolumePathNamesForVolumeNameA(IN LPCSTR lpszVolumeName, 1118 IN LPSTR lpszVolumePathNames, 1119 IN DWORD cchBufferLength, 1120 OUT PDWORD lpcchReturnLength) 1121 { 1122 BOOL Ret; 1123 NTSTATUS Status; 1124 DWORD cchReturnLength; 1125 ANSI_STRING VolumePathName; 1126 PUNICODE_STRING VolumeNameU; 1127 UNICODE_STRING VolumePathNamesU; 1128 1129 /* Convert volume name to unicode */ 1130 VolumeNameU = Basep8BitStringToStaticUnicodeString(lpszVolumeName); 1131 if (VolumeNameU == NULL) 1132 { 1133 return FALSE; 1134 } 1135 1136 /* Initialize the strings we'll use later on */ 1137 VolumePathName.Length = 0; 1138 VolumePathName.MaximumLength = cchBufferLength; 1139 VolumePathName.Buffer = lpszVolumePathNames; 1140 1141 VolumePathNamesU.Length = 0; 1142 VolumePathNamesU.MaximumLength = sizeof(WCHAR) * cchBufferLength; 1143 /* If caller provided a non 0 sized string, allocate a buffer for our unicode string */ 1144 if (VolumePathNamesU.MaximumLength != 0) 1145 { 1146 VolumePathNamesU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.MaximumLength); 1147 if (VolumePathNamesU.Buffer == NULL) 1148 { 1149 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 1150 return FALSE; 1151 } 1152 } 1153 else 1154 { 1155 VolumePathNamesU.Buffer = NULL; 1156 } 1157 1158 /* Call the -W implementation */ 1159 Ret = GetVolumePathNamesForVolumeNameW(VolumeNameU->Buffer, VolumePathNamesU.Buffer, 1160 cchBufferLength, &cchReturnLength); 1161 /* Call succeed, we'll return the total length */ 1162 if (Ret) 1163 { 1164 VolumePathNamesU.Length = sizeof(WCHAR) * cchReturnLength; 1165 } 1166 else 1167 { 1168 /* Else, if we fail for anything else than too small buffer, quit */ 1169 if (GetLastError() != ERROR_MORE_DATA) 1170 { 1171 if (VolumePathNamesU.Buffer != NULL) 1172 { 1173 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer); 1174 } 1175 1176 return FALSE; 1177 } 1178 1179 /* Otherwise, we'll just copy as much as we can */ 1180 VolumePathNamesU.Length = sizeof(WCHAR) * cchBufferLength; 1181 } 1182 1183 /* Convert our output string back to ANSI */ 1184 Status = RtlUnicodeStringToAnsiString(&VolumePathName, &VolumePathNamesU, FALSE); 1185 if (!NT_SUCCESS(Status)) 1186 { 1187 BaseSetLastNTError(Status); 1188 1189 if (VolumePathNamesU.Buffer != NULL) 1190 { 1191 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer); 1192 } 1193 1194 return FALSE; 1195 } 1196 1197 /* If caller wants return length, two cases... */ 1198 if (lpcchReturnLength != NULL) 1199 { 1200 /* We succeed: return the copied length */ 1201 if (Ret) 1202 { 1203 *lpcchReturnLength = VolumePathName.Length; 1204 } 1205 /* We failed, return the size we would have loved having! */ 1206 else 1207 { 1208 *lpcchReturnLength = sizeof(WCHAR) * cchReturnLength; 1209 } 1210 } 1211 1212 /* Release our buffer if allocated */ 1213 if (VolumePathNamesU.Buffer != NULL) 1214 { 1215 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer); 1216 } 1217 1218 return Ret; 1219 } 1220 1221 1222 /* 1223 * @implemented 1224 */ 1225 BOOL 1226 WINAPI 1227 GetVolumePathNamesForVolumeNameW(IN LPCWSTR lpszVolumeName, 1228 IN LPWSTR lpszVolumePathNames, 1229 IN DWORD cchBufferLength, 1230 OUT PDWORD lpcchReturnLength) 1231 { 1232 BOOL Ret; 1233 PWSTR MultiSz; 1234 DWORD BytesReturned; 1235 HANDLE MountMgrHandle; 1236 UNICODE_STRING VolumeName; 1237 PMOUNTMGR_TARGET_NAME TargetName; 1238 PMOUNTMGR_VOLUME_PATHS VolumePaths; 1239 ULONG BufferSize, CharsInMgr, CharsInOutput, Paths; 1240 1241 /* First look that our volume name looks somehow correct */ 1242 RtlInitUnicodeString(&VolumeName, lpszVolumeName); 1243 if (VolumeName.Buffer[(VolumeName.Length / sizeof(WCHAR)) - 1] != L'\\') 1244 { 1245 BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID); 1246 return FALSE; 1247 } 1248 1249 /* Validate it's a DOS volume name finishing with a backslash */ 1250 if (!MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&VolumeName)) 1251 { 1252 SetLastError(ERROR_INVALID_PARAMETER); 1253 return FALSE; 1254 } 1255 1256 /* Allocate an input MOUNTMGR_TARGET_NAME */ 1257 TargetName = RtlAllocateHeap(RtlGetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT)); 1258 if (TargetName == NULL) 1259 { 1260 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 1261 return FALSE; 1262 } 1263 1264 /* And fill it */ 1265 RtlZeroMemory(TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT)); 1266 TargetName->DeviceNameLength = VolumeName.Length - sizeof(WCHAR); 1267 RtlCopyMemory(TargetName->DeviceName, VolumeName.Buffer, TargetName->DeviceNameLength); 1268 TargetName->DeviceName[1] = L'?'; 1269 1270 /* Open the mount manager */ 1271 MountMgrHandle = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0, 1272 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 1273 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 1274 INVALID_HANDLE_VALUE); 1275 if (MountMgrHandle == INVALID_HANDLE_VALUE) 1276 { 1277 RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName); 1278 return FALSE; 1279 } 1280 1281 /* Allocate an initial output buffer, just to get length */ 1282 VolumePaths = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(MOUNTMGR_VOLUME_PATHS)); 1283 if (VolumePaths == NULL) 1284 { 1285 CloseHandle(MountMgrHandle); 1286 RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName); 1287 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 1288 return FALSE; 1289 } 1290 1291 /* Query the paths */ 1292 Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS, 1293 TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT), 1294 VolumePaths, sizeof(MOUNTMGR_VOLUME_PATHS), &BytesReturned, 1295 NULL); 1296 /* Loop until we can query everything */ 1297 while (!Ret) 1298 { 1299 /* If failed for another reason than too small buffer, fail */ 1300 if (GetLastError() != ERROR_MORE_DATA) 1301 { 1302 CloseHandle(MountMgrHandle); 1303 RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName); 1304 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths); 1305 return FALSE; 1306 } 1307 1308 /* Get the required length */ 1309 BufferSize = VolumePaths->MultiSzLength + sizeof(MOUNTMGR_VOLUME_PATHS); 1310 1311 /* And reallocate our output buffer (big enough this time) */ 1312 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths); 1313 VolumePaths = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferSize); 1314 if (VolumePaths == NULL) 1315 { 1316 CloseHandle(MountMgrHandle); 1317 RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName); 1318 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 1319 return FALSE; 1320 } 1321 1322 /* Query again the mount mgr */ 1323 Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS, 1324 TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT), 1325 VolumePaths, BufferSize, &BytesReturned, NULL); 1326 } 1327 1328 /* We're done, no need for input nor mount mgr any longer */ 1329 CloseHandle(MountMgrHandle); 1330 RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName); 1331 1332 /* Initialize: 1333 - Number of paths we saw (useful to count extra \) 1334 - Progress in mount mgr output 1335 - Progress in output buffer 1336 - Direct buffer to returned MultiSz 1337 */ 1338 Paths = 0; 1339 CharsInMgr = 0; 1340 CharsInOutput = 0; 1341 MultiSz = VolumePaths->MultiSz; 1342 1343 /* If we have an output buffer */ 1344 if (cchBufferLength != 0) 1345 { 1346 /* Loop on the output to recopy it back to the caller 1347 * Note that we loop until -1 not to handle last 0 (will be done later on) 1348 */ 1349 for (; (CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1) && (CharsInOutput < cchBufferLength); 1350 ++CharsInMgr, ++CharsInOutput) 1351 { 1352 /* When we reach the end of a path */ 1353 if (MultiSz[CharsInMgr] == UNICODE_NULL) 1354 { 1355 /* On path done (count), add an extra \ at the end */ 1356 ++Paths; 1357 lpszVolumePathNames[CharsInOutput] = L'\\'; 1358 ++CharsInOutput; 1359 /* Make sure we don't overflow */ 1360 if (CharsInOutput == cchBufferLength) 1361 { 1362 break; 1363 } 1364 } 1365 1366 /* Copy the char to the caller 1367 * So, in case we're in the end of a path, we wrote two chars to 1368 * the output buffer: \\ and \0 1369 */ 1370 lpszVolumePathNames[CharsInOutput] = MultiSz[CharsInMgr]; 1371 } 1372 } 1373 1374 /* If output buffer was too small (ie, we couldn't parse all the input buffer) */ 1375 if (CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1) 1376 { 1377 /* Keep looping on it, to count the number of extra \ that will be required 1378 * So that on the next call, caller can allocate enough space 1379 */ 1380 for (; CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1; ++CharsInMgr) 1381 { 1382 if (MultiSz[CharsInMgr] == UNICODE_NULL) 1383 { 1384 ++Paths; 1385 } 1386 } 1387 } 1388 1389 /* If we couldn't write as much as we wanted to the output buffer 1390 * This handles the case where we could write everything excepted the 1391 * terminating \0 for multi SZ 1392 */ 1393 if (CharsInOutput >= cchBufferLength) 1394 { 1395 /* Fail and set appropriate error code */ 1396 Ret = FALSE; 1397 SetLastError(ERROR_MORE_DATA); 1398 /* If caller wants to know how many chars to allocate, return it */ 1399 if (lpcchReturnLength != NULL) 1400 { 1401 /* It's amount of extra \ + number of chars in MultiSz (including double \0) */ 1402 *lpcchReturnLength = Paths + (VolumePaths->MultiSzLength / sizeof(WCHAR)); 1403 } 1404 } 1405 else 1406 { 1407 /* It succeed so terminate the multi SZ (second \0) */ 1408 lpszVolumePathNames[CharsInOutput] = UNICODE_NULL; 1409 Ret = TRUE; 1410 1411 /* If caller wants the amount of chars written, return it */ 1412 if (lpcchReturnLength != NULL) 1413 { 1414 /* Including the terminating \0 we just added */ 1415 *lpcchReturnLength = CharsInOutput + 1; 1416 } 1417 } 1418 1419 /* Free last bits */ 1420 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths); 1421 1422 /* And return */ 1423 return Ret; 1424 } 1425 1426 /* EOF */ 1427