1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: dll/win32/kernel32/client/file/mntpoint.c 5 * PURPOSE: File volume mount point functions 6 * PROGRAMMER: Pierre Schweitzer (pierre@reactos.org) 7 */ 8 9 #include <k32.h> 10 #define NDEBUG 11 #include <debug.h> 12 13 /* 14 * @implemented 15 */ 16 static BOOL 17 GetVolumeNameForRoot(IN LPCWSTR lpszRootPath, 18 OUT LPWSTR lpszVolumeName, 19 IN DWORD cchBufferLength) 20 { 21 BOOL Ret; 22 NTSTATUS Status; 23 PWSTR FoundVolume; 24 DWORD BytesReturned; 25 UNICODE_STRING NtPathName; 26 IO_STATUS_BLOCK IoStatusBlock; 27 PMOUNTMGR_MOUNT_POINT MountPoint; 28 ULONG CurrentMntPt, FoundVolumeLen; 29 PMOUNTMGR_MOUNT_POINTS MountPoints; 30 OBJECT_ATTRIBUTES ObjectAttributes; 31 HANDLE VolumeHandle, MountMgrHandle; 32 struct 33 { 34 MOUNTDEV_NAME; 35 WCHAR Buffer[MAX_PATH]; 36 } MountDevName; 37 38 /* It makes no sense on a non-local drive */ 39 if (GetDriveTypeW(lpszRootPath) == DRIVE_REMOTE) 40 { 41 SetLastError(ERROR_PATH_NOT_FOUND); 42 return FALSE; 43 } 44 45 /* Get the NT path */ 46 if (!RtlDosPathNameToNtPathName_U(lpszRootPath, &NtPathName, NULL, NULL)) 47 { 48 SetLastError(ERROR_PATH_NOT_FOUND); 49 return FALSE; 50 } 51 52 /* If it's a root path - likely - drop backslash to open volume */ 53 if (NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] == L'\\') 54 { 55 NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] = UNICODE_NULL; 56 NtPathName.Length -= sizeof(WCHAR); 57 } 58 59 /* If that's a DOS volume, upper case the letter */ 60 if (NtPathName.Length >= 2 * sizeof(WCHAR)) 61 { 62 if (NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] == L':') 63 { 64 NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 2] = _toupper(NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 2]); 65 } 66 } 67 68 /* Attempt to open the volume */ 69 InitializeObjectAttributes(&ObjectAttributes, &NtPathName, 70 OBJ_CASE_INSENSITIVE, NULL, NULL); 71 Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE | FILE_READ_ATTRIBUTES, 72 &ObjectAttributes, &IoStatusBlock, 73 FILE_SHARE_READ | FILE_SHARE_WRITE, 74 FILE_SYNCHRONOUS_IO_ALERT); 75 if (!NT_SUCCESS(Status)) 76 { 77 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 78 BaseSetLastNTError(Status); 79 return FALSE; 80 } 81 82 /* Query the device name - that's what we'll translate */ 83 if (!DeviceIoControl(VolumeHandle, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 84 0, &MountDevName, sizeof(MountDevName), &BytesReturned, 85 NULL)) 86 { 87 NtClose(VolumeHandle); 88 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 89 return FALSE; 90 } 91 92 /* No longer need the volume */ 93 NtClose(VolumeHandle); 94 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 95 96 /* We'll keep the device name for later usage */ 97 NtPathName.Length = MountDevName.NameLength; 98 NtPathName.MaximumLength = MountDevName.NameLength + sizeof(UNICODE_NULL); 99 NtPathName.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NtPathName.MaximumLength); 100 if (NtPathName.Buffer == NULL) 101 { 102 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 103 return FALSE; 104 } 105 106 RtlCopyMemory(NtPathName.Buffer, MountDevName.Name, NtPathName.Length); 107 NtPathName.Buffer[NtPathName.Length / sizeof(WCHAR)] = UNICODE_NULL; 108 109 /* Allocate the structure for querying the mount mgr */ 110 MountPoint = RtlAllocateHeap(RtlGetProcessHeap(), 0, 111 NtPathName.Length + sizeof(MOUNTMGR_MOUNT_POINT)); 112 if (MountPoint == NULL) 113 { 114 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 115 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 116 return FALSE; 117 } 118 119 /* 0 everything, we provide a device name */ 120 RtlZeroMemory(MountPoint, sizeof(MOUNTMGR_MOUNT_POINT)); 121 MountPoint->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT); 122 MountPoint->DeviceNameLength = NtPathName.Length; 123 RtlCopyMemory((PVOID)((ULONG_PTR)MountPoint + sizeof(MOUNTMGR_MOUNT_POINT)), NtPathName.Buffer, NtPathName.Length); 124 125 /* Allocate a dummy output buffer to probe for size */ 126 MountPoints = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(MOUNTMGR_MOUNT_POINTS)); 127 if (MountPoints == NULL) 128 { 129 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 130 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint); 131 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 132 return FALSE; 133 } 134 135 /* Open a handle to the mount manager */ 136 MountMgrHandle = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0, 137 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 138 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 139 INVALID_HANDLE_VALUE); 140 if (MountMgrHandle == INVALID_HANDLE_VALUE) 141 { 142 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 143 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints); 144 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint); 145 return FALSE; 146 } 147 148 /* Query the names associated to our device name */ 149 Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_POINTS, 150 MountPoint, NtPathName.Length + sizeof(MOUNTMGR_MOUNT_POINT), 151 MountPoints, sizeof(MOUNTMGR_MOUNT_POINTS), &BytesReturned, 152 NULL); 153 /* As long as the buffer is too small, keep looping */ 154 while (!Ret && GetLastError() == ERROR_MORE_DATA) 155 { 156 ULONG BufferSize; 157 158 /* Get the size we've to allocate */ 159 BufferSize = MountPoints->Size; 160 /* Reallocate the buffer with enough room */ 161 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints); 162 MountPoints = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferSize); 163 if (MountPoints == NULL) 164 { 165 CloseHandle(MountMgrHandle); 166 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 167 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint); 168 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 169 return FALSE; 170 } 171 172 /* Reissue the request, it should work now! */ 173 Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_POINTS, 174 MountPoint, NtPathName.Length + sizeof(MOUNTMGR_MOUNT_POINT), 175 MountPoints, BufferSize, &BytesReturned, NULL); 176 } 177 178 /* We're done, no longer need the mount manager */ 179 CloseHandle(MountMgrHandle); 180 /* Nor our input buffer */ 181 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint); 182 183 /* If the mount manager failed, just quit */ 184 if (!Ret) 185 { 186 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 187 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints); 188 SetLastError(ERROR_INVALID_PARAMETER); 189 return FALSE; 190 } 191 192 CurrentMntPt = 0; 193 /* If there were no associated mount points, we'll return the device name */ 194 if (MountPoints->NumberOfMountPoints == 0) 195 { 196 FoundVolume = NtPathName.Buffer; 197 FoundVolumeLen = NtPathName.Length; 198 } 199 /* Otherwise, find one which is matching */ 200 else 201 { 202 for (; CurrentMntPt < MountPoints->NumberOfMountPoints; ++CurrentMntPt) 203 { 204 UNICODE_STRING SymbolicLink; 205 206 /* Make a string of it, to easy the checks */ 207 SymbolicLink.Length = MountPoints->MountPoints[CurrentMntPt].SymbolicLinkNameLength; 208 SymbolicLink.MaximumLength = SymbolicLink.Length; 209 SymbolicLink.Buffer = (PVOID)((ULONG_PTR)&MountPoints->MountPoints[CurrentMntPt] + MountPoints->MountPoints[CurrentMntPt].SymbolicLinkNameOffset); 210 /* If that's a NT volume name (GUID form), keep it! */ 211 if (MOUNTMGR_IS_NT_VOLUME_NAME(&SymbolicLink)) 212 { 213 FoundVolume = SymbolicLink.Buffer; 214 FoundVolumeLen = SymbolicLink.Length; 215 216 break; 217 } 218 } 219 } 220 221 /* We couldn't find anything matching, return an error */ 222 if (CurrentMntPt == MountPoints->NumberOfMountPoints) 223 { 224 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 225 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints); 226 SetLastError(ERROR_INVALID_PARAMETER); 227 return FALSE; 228 } 229 230 /* We found a matching volume, have we enough memory to return it? */ 231 if (cchBufferLength * sizeof(WCHAR) < FoundVolumeLen + 2 * sizeof(WCHAR)) 232 { 233 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 234 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints); 235 SetLastError(ERROR_FILENAME_EXCED_RANGE); 236 return FALSE; 237 } 238 239 /* Copy it back! */ 240 RtlCopyMemory(lpszVolumeName, FoundVolume, FoundVolumeLen); 241 /* Make it compliant */ 242 lpszVolumeName[1] = L'\\'; 243 /* And transform it as root path */ 244 lpszVolumeName[FoundVolumeLen / sizeof(WCHAR)] = L'\\'; 245 lpszVolumeName[FoundVolumeLen / sizeof(WCHAR) + 1] = UNICODE_NULL; 246 247 /* We're done! */ 248 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); 249 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints); 250 return TRUE; 251 } 252 253 /* 254 * @implemented 255 */ 256 BOOL 257 BasepGetVolumeNameFromReparsePoint(IN LPCWSTR lpszMountPoint, 258 OUT LPWSTR lpszVolumeName, 259 IN DWORD cchBufferLength, 260 OUT LPBOOL IsAMountPoint) 261 { 262 WCHAR Old; 263 DWORD BytesReturned; 264 HANDLE ReparseHandle; 265 UNICODE_STRING SubstituteName; 266 PREPARSE_DATA_BUFFER ReparseBuffer; 267 268 /* Try to open the reparse point */ 269 ReparseHandle = CreateFileW(lpszMountPoint, 0, 270 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 271 OPEN_EXISTING, 272 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, 273 INVALID_HANDLE_VALUE); 274 /* It failed! */ 275 if (ReparseHandle == INVALID_HANDLE_VALUE) 276 { 277 /* Report it's not a mount point (it's not a reparse point) */ 278 if (IsAMountPoint != NULL) 279 { 280 *IsAMountPoint = FALSE; 281 } 282 283 /* And zero output */ 284 if (lpszVolumeName != NULL && cchBufferLength >= 1) 285 { 286 lpszVolumeName[0] = UNICODE_NULL; 287 } 288 289 return FALSE; 290 } 291 292 /* This is a mount point! */ 293 if (IsAMountPoint != NULL) 294 { 295 *IsAMountPoint = TRUE; 296 } 297 298 /* Prepare a buffer big enough to read its data */ 299 ReparseBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); 300 if (ReparseBuffer == NULL) 301 { 302 CloseHandle(ReparseHandle); 303 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 304 305 /* Zero output */ 306 if (lpszVolumeName != NULL && cchBufferLength >= 1) 307 { 308 lpszVolumeName[0] = UNICODE_NULL; 309 } 310 311 return FALSE; 312 } 313 314 /* Dump the reparse point data */ 315 if (!DeviceIoControl(ReparseHandle, FSCTL_GET_REPARSE_POINT, NULL, 0, 316 ReparseBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &BytesReturned, 317 NULL)) 318 { 319 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer); 320 CloseHandle(ReparseHandle); 321 322 /* Zero output */ 323 if (lpszVolumeName != NULL && cchBufferLength >= 1) 324 { 325 lpszVolumeName[0] = UNICODE_NULL; 326 } 327 328 return FALSE; 329 } 330 331 /* We no longer need the reparse point */ 332 CloseHandle(ReparseHandle); 333 334 /* We only handle mount points */ 335 if (ReparseBuffer->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) 336 { 337 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer); 338 339 /* Zero output */ 340 if (lpszVolumeName != NULL && cchBufferLength >= 1) 341 { 342 lpszVolumeName[0] = UNICODE_NULL; 343 } 344 345 return FALSE; 346 } 347 348 /* Do we have enough room for copying substitue name? */ 349 if ((ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL)) > cchBufferLength * sizeof(WCHAR)) 350 { 351 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer); 352 SetLastError(ERROR_FILENAME_EXCED_RANGE); 353 354 /* Zero output */ 355 if (lpszVolumeName != NULL && cchBufferLength >= 1) 356 { 357 lpszVolumeName[0] = UNICODE_NULL; 358 } 359 360 return FALSE; 361 } 362 363 /* Copy the link target */ 364 RtlCopyMemory(lpszVolumeName, 365 &ReparseBuffer->MountPointReparseBuffer.PathBuffer[ReparseBuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], 366 ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength); 367 /* Make it DOS valid */ 368 Old = lpszVolumeName[1]; 369 /* We want a root path */ 370 lpszVolumeName[1] = L'\\'; 371 /* And null terminate obviously */ 372 lpszVolumeName[ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)] = UNICODE_NULL; 373 374 /* Make it a string to easily check it */ 375 SubstituteName.Length = ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength; 376 SubstituteName.MaximumLength = SubstituteName.Length; 377 SubstituteName.Buffer = lpszVolumeName; 378 379 /* No longer need the data? */ 380 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer); 381 382 /* Is that a dos volume name with backslash? */ 383 if (MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&SubstituteName)) 384 { 385 return TRUE; 386 } 387 388 /* No, so restore previous name and return to the caller */ 389 lpszVolumeName[1] = Old; 390 SetLastError(ERROR_INVALID_PARAMETER); 391 return FALSE; 392 } 393 394 /* 395 * @implemented 396 */ 397 BOOL 398 BasepGetVolumeNameForVolumeMountPoint(IN LPCWSTR lpszMountPoint, 399 OUT LPWSTR lpszVolumeName, 400 IN DWORD cchBufferLength, 401 OUT LPBOOL IsAMountPoint) 402 { 403 BOOL Ret; 404 UNICODE_STRING MountPoint; 405 406 /* Assume it's a mount point (likely for non reparse points) */ 407 if (IsAMountPoint != NULL) 408 { 409 *IsAMountPoint = 1; 410 } 411 412 /* Make a string with the mount point name */ 413 RtlInitUnicodeString(&MountPoint, lpszMountPoint); 414 /* Not a root path? */ 415 if (MountPoint.Buffer[(MountPoint.Length / sizeof(WCHAR)) - 1] != L'\\') 416 { 417 BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID); 418 /* Zero output */ 419 if (lpszVolumeName != NULL && cchBufferLength >= 1) 420 { 421 lpszVolumeName[0] = UNICODE_NULL; 422 } 423 424 return FALSE; 425 } 426 427 /* Does it look like <letter>:\? */ 428 if (MountPoint.Length == 3 * sizeof(WCHAR)) 429 { 430 /* Try to get volume name for root path */ 431 Ret = GetVolumeNameForRoot(lpszMountPoint, lpszVolumeName, cchBufferLength); 432 /* It failed? */ 433 if (!Ret) 434 { 435 /* If wasn't a drive letter, so maybe a reparse point? */ 436 if (MountPoint.Buffer[1] != ':') 437 { 438 Ret = BasepGetVolumeNameFromReparsePoint(lpszMountPoint, lpszVolumeName, cchBufferLength, IsAMountPoint); 439 } 440 /* It was, so zero output */ 441 else if (lpszVolumeName != NULL && cchBufferLength >= 1) 442 { 443 lpszVolumeName[0] = UNICODE_NULL; 444 } 445 } 446 } 447 else 448 { 449 /* Try to get volume name for root path */ 450 Ret = GetVolumeNameForRoot(lpszMountPoint, lpszVolumeName, cchBufferLength); 451 /* It failed? */ 452 if (!Ret) 453 { 454 /* It was a DOS volume as UNC name, so fail and zero output */ 455 if (MountPoint.Length == 14 && MountPoint.Buffer[0] == '\\' && MountPoint.Buffer[1] == '\\' && 456 (MountPoint.Buffer[2] == '.' || MountPoint.Buffer[2] == '?') && MountPoint.Buffer[3] == L'\\' && 457 MountPoint.Buffer[5] == ':') 458 { 459 if (lpszVolumeName != NULL && cchBufferLength >= 1) 460 { 461 lpszVolumeName[0] = UNICODE_NULL; 462 } 463 } 464 /* Maybe it's a reparse point? */ 465 else 466 { 467 Ret = BasepGetVolumeNameFromReparsePoint(lpszMountPoint, lpszVolumeName, cchBufferLength, IsAMountPoint); 468 } 469 } 470 } 471 472 return Ret; 473 } 474 475 /** 476 * @name GetVolumeNameForVolumeMountPointW 477 * @implemented 478 * 479 * Return an unique volume name for a drive root or mount point. 480 * 481 * @param VolumeMountPoint 482 * Pointer to string that contains either root drive name or 483 * mount point name. 484 * @param VolumeName 485 * Pointer to buffer that is filled with resulting unique 486 * volume name on success. 487 * @param VolumeNameLength 488 * Size of VolumeName buffer in TCHARs. 489 * 490 * @return 491 * TRUE when the function succeeds and the VolumeName buffer is filled, 492 * FALSE otherwise. 493 */ 494 BOOL 495 WINAPI 496 GetVolumeNameForVolumeMountPointW(IN LPCWSTR VolumeMountPoint, 497 OUT LPWSTR VolumeName, 498 IN DWORD VolumeNameLength) 499 { 500 BOOL Ret; 501 502 /* Just query our internal function */ 503 Ret = BasepGetVolumeNameForVolumeMountPoint(VolumeMountPoint, VolumeName, 504 VolumeNameLength, NULL); 505 if (!Ret && VolumeName != NULL && VolumeNameLength >= 1) 506 { 507 VolumeName[0] = UNICODE_NULL; 508 } 509 510 return Ret; 511 } 512 513 /* 514 * @implemented 515 */ 516 BOOL 517 WINAPI 518 GetVolumeNameForVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint, 519 IN LPSTR lpszVolumeName, 520 IN DWORD cchBufferLength) 521 { 522 BOOL Ret; 523 ANSI_STRING VolumeName; 524 UNICODE_STRING VolumeNameU; 525 PUNICODE_STRING VolumeMountPointU; 526 527 /* Convert mount point to unicode */ 528 VolumeMountPointU = Basep8BitStringToStaticUnicodeString(lpszVolumeMountPoint); 529 if (VolumeMountPointU == NULL) 530 { 531 return FALSE; 532 } 533 534 /* Initialize the strings we'll use for convention */ 535 VolumeName.Buffer = lpszVolumeName; 536 VolumeName.Length = 0; 537 VolumeName.MaximumLength = cchBufferLength - 1; 538 539 VolumeNameU.Length = 0; 540 VolumeNameU.MaximumLength = (cchBufferLength - 1) * sizeof(WCHAR) + sizeof(UNICODE_NULL); 541 /* Allocate a buffer big enough to contain the returned name */ 542 VolumeNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeNameU.MaximumLength); 543 if (VolumeNameU.Buffer == NULL) 544 { 545 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 546 return FALSE; 547 } 548 549 /* Query -W */ 550 Ret = GetVolumeNameForVolumeMountPointW(VolumeMountPointU->Buffer, VolumeNameU.Buffer, cchBufferLength); 551 /* If it succeed, perform -A conversion */ 552 if (Ret) 553 { 554 NTSTATUS Status; 555 556 /* Reinit our string for length */ 557 RtlInitUnicodeString(&VolumeNameU, VolumeNameU.Buffer); 558 /* Convert to ANSI */ 559 Status = RtlUnicodeStringToAnsiString(&VolumeName, &VolumeNameU, FALSE); 560 /* If conversion failed, force failure, otherwise, just null terminate */ 561 if (!NT_SUCCESS(Status)) 562 { 563 Ret = FALSE; 564 BaseSetLastNTError(Status); 565 } 566 else 567 { 568 VolumeName.Buffer[VolumeName.Length] = ANSI_NULL; 569 } 570 } 571 572 /* Internal buffer no longer needed */ 573 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameU.Buffer); 574 575 return Ret; 576 } 577 578 /* 579 * @unimplemented 580 */ 581 BOOL 582 WINAPI 583 SetVolumeMountPointW(IN LPCWSTR lpszVolumeMountPoint, 584 IN LPCWSTR lpszVolumeName) 585 { 586 STUB; 587 return 0; 588 } 589 590 /* 591 * @unimplemented 592 */ 593 BOOL 594 WINAPI 595 SetVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint, 596 IN LPCSTR lpszVolumeName) 597 { 598 STUB; 599 return 0; 600 } 601 602 /* 603 * @unimplemented 604 */ 605 BOOL 606 WINAPI 607 DeleteVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint) 608 { 609 STUB; 610 return 0; 611 } 612 613 /* 614 * @unimplemented 615 */ 616 BOOL 617 WINAPI 618 DeleteVolumeMountPointW(IN LPCWSTR lpszVolumeMountPoint) 619 { 620 STUB; 621 return 0; 622 } 623 624 /* 625 * @unimplemented 626 */ 627 HANDLE 628 WINAPI 629 FindFirstVolumeMountPointW(IN LPCWSTR lpszRootPathName, 630 IN LPWSTR lpszVolumeMountPoint, 631 IN DWORD cchBufferLength) 632 { 633 STUB; 634 return 0; 635 } 636 637 /* 638 * @unimplemented 639 */ 640 HANDLE 641 WINAPI 642 FindFirstVolumeMountPointA(IN LPCSTR lpszRootPathName, 643 IN LPSTR lpszVolumeMountPoint, 644 IN DWORD cchBufferLength) 645 { 646 STUB; 647 return 0; 648 } 649 650 /* 651 * @unimplemented 652 */ 653 BOOL 654 WINAPI 655 FindNextVolumeMountPointA(IN HANDLE hFindVolumeMountPoint, 656 IN LPSTR lpszVolumeMountPoint, 657 DWORD cchBufferLength) 658 { 659 STUB; 660 return 0; 661 } 662 663 /* 664 * @unimplemented 665 */ 666 BOOL 667 WINAPI 668 FindNextVolumeMountPointW(IN HANDLE hFindVolumeMountPoint, 669 IN LPWSTR lpszVolumeMountPoint, 670 DWORD cchBufferLength) 671 { 672 STUB; 673 return 0; 674 } 675 676 /* 677 * @unimplemented 678 */ 679 BOOL 680 WINAPI 681 FindVolumeMountPointClose(IN HANDLE hFindVolumeMountPoint) 682 { 683 STUB; 684 return 0; 685 } 686 687 /* EOF */ 688