/*++ Copyright (c) 1997 Microsoft Corporation Module Name: volmount.c Abstract: This file contains the implementation for the Volume Mount Point API. Author: Norbert P. Kusters (norbertk) 22-Dec-1997 Revision History: --*/ #include "basedll.h" #include "initguid.h" #include "mountmgr.h" // NOTE, this structure is here because it was not defined in NTIOAPI.H. // This should be taken out in the future. // This is stolen from NTFS.H typedef struct _REPARSE_INDEX_KEY { // // The tag of the reparse point. // ULONG FileReparseTag; // // The file record Id where the reparse point is set. // LARGE_INTEGER FileId; } REPARSE_INDEX_KEY, *PREPARSE_INDEX_KEY; HANDLE WINAPI FindFirstVolumeA( LPSTR lpszVolumeName, DWORD cchBufferLength ) { ANSI_STRING ansiVolumeName; UNICODE_STRING unicodeVolumeName; HANDLE h; NTSTATUS status; ansiVolumeName.Buffer = lpszVolumeName; ansiVolumeName.MaximumLength = (USHORT) (cchBufferLength - 1); unicodeVolumeName.Buffer = NULL; unicodeVolumeName.MaximumLength = 0; try { unicodeVolumeName.MaximumLength = (ansiVolumeName.MaximumLength + 1)* sizeof(WCHAR); unicodeVolumeName.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), unicodeVolumeName.MaximumLength); if (!unicodeVolumeName.Buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return INVALID_HANDLE_VALUE; } h = FindFirstVolumeW(unicodeVolumeName.Buffer, cchBufferLength); if (h != INVALID_HANDLE_VALUE) { RtlInitUnicodeString(&unicodeVolumeName, unicodeVolumeName.Buffer); status = BasepUnicodeStringTo8BitString(&ansiVolumeName, &unicodeVolumeName, FALSE); if (!NT_SUCCESS(status)) { BaseSetLastNTError(status); return INVALID_HANDLE_VALUE; } ansiVolumeName.Buffer[ansiVolumeName.Length] = 0; } } finally { if (unicodeVolumeName.Buffer) { RtlFreeHeap(RtlProcessHeap(), 0, unicodeVolumeName.Buffer); } } return h; } HANDLE WINAPI FindFirstVolumeW( LPWSTR lpszVolumeName, DWORD cchBufferLength ) /*++ Routine Description: This routine kicks off the enumeration of all volumes in the system. Arguments: lpszVolumeName - Returns the first volume name in the system. cchBufferLength - Supplies the size of the preceeding buffer. Return Value: A valid handle or INVALID_HANDLE_VALUE. --*/ { HANDLE h; MOUNTMGR_MOUNT_POINT point; PMOUNTMGR_MOUNT_POINTS points; BOOL b; DWORD bytes; h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { return INVALID_HANDLE_VALUE; } RtlZeroMemory(&point, sizeof(point)); points = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), sizeof(MOUNTMGR_MOUNT_POINTS)); if (!points) { CloseHandle(h); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return INVALID_HANDLE_VALUE; } b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, &point, sizeof(point), points, sizeof(MOUNTMGR_MOUNT_POINTS), &bytes, NULL); while (!b && GetLastError() == ERROR_MORE_DATA) { bytes = points->Size; RtlFreeHeap(RtlProcessHeap(), 0, points); points = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), bytes); if (!points) { CloseHandle(h); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return INVALID_HANDLE_VALUE; } b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, &point, sizeof(point), points, bytes, &bytes, NULL); } CloseHandle(h); if (!b) { RtlFreeHeap(RtlProcessHeap(), 0, points); return INVALID_HANDLE_VALUE; } b = FindNextVolumeW((HANDLE) points, lpszVolumeName, cchBufferLength); if (!b) { RtlFreeHeap(RtlProcessHeap(), 0, points); return INVALID_HANDLE_VALUE; } return (HANDLE) points; } BOOL WINAPI FindNextVolumeA( HANDLE hFindVolume, LPSTR lpszVolumeName, DWORD cchBufferLength ) { ANSI_STRING ansiVolumeName; UNICODE_STRING unicodeVolumeName; BOOL b; NTSTATUS status; ansiVolumeName.Buffer = lpszVolumeName; ansiVolumeName.MaximumLength = (USHORT) (cchBufferLength - 1); unicodeVolumeName.Buffer = NULL; unicodeVolumeName.MaximumLength = 0; try { unicodeVolumeName.MaximumLength = (ansiVolumeName.MaximumLength + 1)* sizeof(WCHAR); unicodeVolumeName.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), unicodeVolumeName.MaximumLength); if (!unicodeVolumeName.Buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } b = FindNextVolumeW(hFindVolume, unicodeVolumeName.Buffer, cchBufferLength); if (b) { RtlInitUnicodeString(&unicodeVolumeName, unicodeVolumeName.Buffer); status = BasepUnicodeStringTo8BitString(&ansiVolumeName, &unicodeVolumeName, FALSE); if (!NT_SUCCESS(status)) { BaseSetLastNTError(status); return FALSE; } ansiVolumeName.Buffer[ansiVolumeName.Length] = 0; } } finally { if (unicodeVolumeName.Buffer) { RtlFreeHeap(RtlProcessHeap(), 0, unicodeVolumeName.Buffer); } } return b; } BOOL WINAPI FindNextVolumeW( HANDLE hFindVolume, LPWSTR lpszVolumeName, DWORD cchBufferLength ) /*++ Routine Description: This routine continues the enumeration of all volumes in the system. Arguments: hFindVolume - Supplies the find volume handle. lpszVolumeName - Returns the first volume name in the system. cchBufferLength - Supplies the size of the preceeding buffer. Return Value: FALSE - Failure. The error code is returned in GetLastError(). TRUE - Success. --*/ { PMOUNTMGR_MOUNT_POINTS points = hFindVolume; DWORD i, j; PMOUNTMGR_MOUNT_POINT point, point2; UNICODE_STRING symName, symName2, devName, devName2; for (i = 0; i < points->NumberOfMountPoints; i++) { point = &points->MountPoints[i]; if (!point->SymbolicLinkNameOffset) { continue; } symName.Length = symName.MaximumLength = point->SymbolicLinkNameLength; symName.Buffer = (PWSTR) ((PCHAR) points + point->SymbolicLinkNameOffset); if (!MOUNTMGR_IS_NT_VOLUME_NAME(&symName)) { point->SymbolicLinkNameOffset = 0; continue; } devName.Length = devName.MaximumLength = point->DeviceNameLength; devName.Buffer = (PWSTR) ((PCHAR) points + point->DeviceNameOffset); for (j = i + 1; j < points->NumberOfMountPoints; j++) { point2 = &points->MountPoints[j]; if (!point2->SymbolicLinkNameOffset) { continue; } symName2.Length = symName2.MaximumLength = point2->SymbolicLinkNameLength; symName2.Buffer = (PWSTR) ((PCHAR) points + point2->SymbolicLinkNameOffset); if (!MOUNTMGR_IS_NT_VOLUME_NAME(&symName2)) { point2->SymbolicLinkNameOffset = 0; continue; } devName2.Length = devName2.MaximumLength = point2->DeviceNameLength; devName2.Buffer = (PWSTR) ((PCHAR) points + point2->DeviceNameOffset); if (RtlEqualUnicodeString(&devName, &devName2, TRUE)) { point2->SymbolicLinkNameOffset = 0; } } break; } if (i == points->NumberOfMountPoints) { SetLastError(ERROR_NO_MORE_FILES); return FALSE; } if (cchBufferLength*sizeof(WCHAR) < point->SymbolicLinkNameLength + 2*sizeof(WCHAR)) { SetLastError(ERROR_FILENAME_EXCED_RANGE); return FALSE; } RtlCopyMemory(lpszVolumeName, (PCHAR) points + point->SymbolicLinkNameOffset, point->SymbolicLinkNameLength); lpszVolumeName[1] = '\\'; lpszVolumeName[point->SymbolicLinkNameLength/sizeof(WCHAR)] = '\\'; lpszVolumeName[point->SymbolicLinkNameLength/sizeof(WCHAR) + 1] = 0; point->SymbolicLinkNameOffset = 0; return TRUE; } BOOL WINAPI FindVolumeClose( HANDLE hFindVolume ) { RtlFreeHeap(RtlProcessHeap(), 0, hFindVolume); return TRUE; } HANDLE WINAPI FindFirstVolumeMountPointA( LPCSTR lpszRootPathName, LPSTR lpszVolumeMountPoint, DWORD cchBufferLength ) { PUNICODE_STRING unicodeRootPathName; ANSI_STRING ansiVolumeMountPoint; UNICODE_STRING unicodeVolumeMountPoint; HANDLE h; NTSTATUS status; unicodeRootPathName = Basep8BitStringToStaticUnicodeString(lpszRootPathName); if (!unicodeRootPathName) { return INVALID_HANDLE_VALUE; } ansiVolumeMountPoint.Buffer = lpszVolumeMountPoint; ansiVolumeMountPoint.MaximumLength = (USHORT) (cchBufferLength - 1); unicodeVolumeMountPoint.Buffer = NULL; unicodeVolumeMountPoint.MaximumLength = 0; try { unicodeVolumeMountPoint.MaximumLength = (ansiVolumeMountPoint.MaximumLength + 1)*sizeof(WCHAR); unicodeVolumeMountPoint.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), unicodeVolumeMountPoint.MaximumLength); if (!unicodeVolumeMountPoint.Buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return INVALID_HANDLE_VALUE; } h = FindFirstVolumeMountPointW(unicodeRootPathName->Buffer, unicodeVolumeMountPoint.Buffer, cchBufferLength); if (h != INVALID_HANDLE_VALUE) { RtlInitUnicodeString(&unicodeVolumeMountPoint, unicodeVolumeMountPoint.Buffer); status = BasepUnicodeStringTo8BitString(&ansiVolumeMountPoint, &unicodeVolumeMountPoint, FALSE); if (!NT_SUCCESS(status)) { BaseSetLastNTError(status); return INVALID_HANDLE_VALUE; } ansiVolumeMountPoint.Buffer[ansiVolumeMountPoint.Length] = 0; } } finally { if (unicodeVolumeMountPoint.Buffer) { RtlFreeHeap(RtlProcessHeap(), 0, unicodeVolumeMountPoint.Buffer); } } return h; } BOOL FindNextVolumeMountPointHelper( HANDLE hFindVolumeMountPoint, LPWSTR lpszVolumeMountPoint, DWORD cchBufferLength, BOOL FirstTimeCalled ) /*++ Routine Description: This routine continues the enumeration of all volume mount point on the given volume. Arguments: hFindVolumeMountPoint - Supplies the handle for the enumeration. lpszVolumeMountPoint - Returns the volume mount point. cchBufferLength - Supplies the volume mount point buffer length. FirstTimeCalled - Supplies whether or not this is being called from FindFirst or from FindNext. Return Value: FALSE - Failure. TRUE - Success. --*/ { REPARSE_INDEX_KEY reparseKey; UNICODE_STRING reparseName; NTSTATUS status; IO_STATUS_BLOCK ioStatus; FILE_REPARSE_POINT_INFORMATION reparseInfo; UNICODE_STRING fileId; OBJECT_ATTRIBUTES oa; HANDLE h; PREPARSE_DATA_BUFFER reparse; BOOL b; DWORD bytes; UNICODE_STRING mountName; DWORD nameInfoSize; PFILE_NAME_INFORMATION nameInfo; for (;;) { if (FirstTimeCalled) { FirstTimeCalled = FALSE; RtlZeroMemory(&reparseKey, sizeof(reparseKey)); reparseKey.FileReparseTag = IO_REPARSE_TAG_MOUNT_POINT; reparseName.Length = reparseName.MaximumLength = sizeof(reparseKey); reparseName.Buffer = (PWCHAR) &reparseKey; status = NtQueryDirectoryFile(hFindVolumeMountPoint, NULL, NULL, NULL, &ioStatus, &reparseInfo, sizeof(reparseInfo), FileReparsePointInformation, TRUE, &reparseName, FALSE); } else { status = NtQueryDirectoryFile(hFindVolumeMountPoint, NULL, NULL, NULL, &ioStatus, &reparseInfo, sizeof(reparseInfo), FileReparsePointInformation, TRUE, NULL, FALSE); } if (!NT_SUCCESS(status)) { BaseSetLastNTError(status); return FALSE; } if (reparseInfo.Tag != IO_REPARSE_TAG_MOUNT_POINT) { SetLastError(ERROR_NO_MORE_FILES); return FALSE; } fileId.Length = sizeof(reparseInfo.FileReference); fileId.MaximumLength = fileId.Length; fileId.Buffer = (PWSTR) &reparseInfo.FileReference; InitializeObjectAttributes(&oa, &fileId, 0, hFindVolumeMountPoint, NULL); status = NtOpenFile(&h, FILE_GENERIC_READ, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_BY_FILE_ID | FILE_OPEN_REPARSE_POINT); if (!NT_SUCCESS(status)) { BaseSetLastNTError(status); return FALSE; } reparse = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), MAXIMUM_REPARSE_DATA_BUFFER_SIZE); if (!reparse) { CloseHandle(h); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } b = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, reparse, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytes, NULL); if (!b || reparse->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) { RtlFreeHeap(RtlProcessHeap(), 0, reparse); CloseHandle(h); return FALSE; } mountName.Length = mountName.MaximumLength = reparse->MountPointReparseBuffer.SubstituteNameLength; mountName.Buffer = (PWSTR) ((PCHAR) reparse->MountPointReparseBuffer.PathBuffer + reparse->MountPointReparseBuffer.SubstituteNameOffset); if (!MOUNTMGR_IS_NT_VOLUME_NAME_WB(&mountName)) { RtlFreeHeap(RtlProcessHeap(), 0, reparse); CloseHandle(h); continue; } RtlFreeHeap(RtlProcessHeap(), 0, reparse); nameInfoSize = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + (cchBufferLength - 1)*sizeof(WCHAR); nameInfo = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), nameInfoSize); if (!nameInfo) { CloseHandle(h); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } status = NtQueryInformationFile(h, &ioStatus, nameInfo, nameInfoSize, FileNameInformation); if (!NT_SUCCESS(status)) { RtlFreeHeap(RtlProcessHeap(), 0, nameInfo); CloseHandle(h); BaseSetLastNTError(status); return FALSE; } RtlCopyMemory(lpszVolumeMountPoint, &nameInfo->FileName[1], nameInfo->FileNameLength - sizeof(WCHAR)); lpszVolumeMountPoint[nameInfo->FileNameLength/sizeof(WCHAR) - 1] = '\\'; lpszVolumeMountPoint[nameInfo->FileNameLength/sizeof(WCHAR)] = 0; RtlFreeHeap(RtlProcessHeap(), 0, nameInfo); CloseHandle(h); break; } return TRUE; } HANDLE WINAPI FindFirstVolumeMountPointW( LPCWSTR lpszRootPathName, LPWSTR lpszVolumeMountPoint, DWORD cchBufferLength ) /*++ Routine Description: This routine kicks off the enumeration of all volume mount point on the given volume. Arguments: lpszRootPathName - Supplies the root path name. lpszVolumeMountPoint - Returns the volume mount point. cchBufferLength - Supplies the volume mount point buffer length. Return Value: A handle or INVALID_HANDLE_VALUE. --*/ { UNICODE_STRING unicodeRootPathName; UNICODE_STRING reparseSuffix, reparseName; HANDLE h; BOOL b; RtlInitUnicodeString(&unicodeRootPathName, lpszRootPathName); if (unicodeRootPathName.Buffer[ unicodeRootPathName.Length/sizeof(WCHAR) - 1] != '\\') { BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID); return INVALID_HANDLE_VALUE; } RtlInitUnicodeString(&reparseSuffix, L"$Extend\\$Reparse:$R:$INDEX_ALLOCATION"); reparseName.MaximumLength = unicodeRootPathName.Length + reparseSuffix.Length + sizeof(WCHAR); reparseName.Length = 0; reparseName.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), reparseName.MaximumLength); if (!reparseName.Buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return INVALID_HANDLE_VALUE; } RtlCopyUnicodeString(&reparseName, &unicodeRootPathName); RtlAppendUnicodeStringToString(&reparseName, &reparseSuffix); reparseName.Buffer[reparseName.Length/sizeof(WCHAR)] = 0; h = CreateFileW(reparseName.Buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | SECURITY_IMPERSONATION, NULL); RtlFreeHeap(RtlProcessHeap(), 0, reparseName.Buffer); if (h == INVALID_HANDLE_VALUE) { return INVALID_HANDLE_VALUE; } b = FindNextVolumeMountPointHelper(h, lpszVolumeMountPoint, cchBufferLength, TRUE); if (!b) { CloseHandle(h); return INVALID_HANDLE_VALUE; } return h; } BOOL WINAPI FindNextVolumeMountPointA( HANDLE hFindVolumeMountPoint, LPSTR lpszVolumeMountPoint, DWORD cchBufferLength ) { ANSI_STRING ansiVolumeMountPoint; UNICODE_STRING unicodeVolumeMountPoint; BOOL b; NTSTATUS status; ansiVolumeMountPoint.Buffer = lpszVolumeMountPoint; ansiVolumeMountPoint.MaximumLength = (USHORT) (cchBufferLength - 1); unicodeVolumeMountPoint.Buffer = NULL; unicodeVolumeMountPoint.MaximumLength = 0; try { unicodeVolumeMountPoint.MaximumLength = (ansiVolumeMountPoint.MaximumLength + 1)*sizeof(WCHAR); unicodeVolumeMountPoint.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), unicodeVolumeMountPoint.MaximumLength); if (!unicodeVolumeMountPoint.Buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } b = FindNextVolumeMountPointW(hFindVolumeMountPoint, unicodeVolumeMountPoint.Buffer, cchBufferLength); if (b) { RtlInitUnicodeString(&unicodeVolumeMountPoint, unicodeVolumeMountPoint.Buffer); status = BasepUnicodeStringTo8BitString(&ansiVolumeMountPoint, &unicodeVolumeMountPoint, FALSE); if (!NT_SUCCESS(status)) { BaseSetLastNTError(status); return FALSE; } ansiVolumeMountPoint.Buffer[ansiVolumeMountPoint.Length] = 0; } } finally { if (unicodeVolumeMountPoint.Buffer) { RtlFreeHeap(RtlProcessHeap(), 0, unicodeVolumeMountPoint.Buffer); } } return b; } BOOL IsThisAVolumeName( LPCWSTR Name, PBOOLEAN IsVolume ) /*++ Routine Description: This routine takes the given NT name and determines whether or not the name points to a volume. Arguments: Name - Supplies the name. IsVolume - Returns whether or not the given name is a volume. Return Value: FALSE - Failure. TRUE - Success. --*/ { UNICODE_STRING name; PMOUNTMGR_MOUNT_POINT point; MOUNTMGR_MOUNT_POINTS points; HANDLE h; BOOL b; DWORD bytes; RtlInitUnicodeString(&name, Name); if (name.Buffer[name.Length/sizeof(WCHAR) - 1] == '\\') { name.Length -= sizeof(WCHAR); } point = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), name.Length + sizeof(MOUNTMGR_MOUNT_POINT)); if (!point) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } RtlZeroMemory(point, sizeof(MOUNTMGR_MOUNT_POINT)); point->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT); point->DeviceNameLength = name.Length; RtlCopyMemory((PCHAR) point + point->DeviceNameOffset, name.Buffer, point->DeviceNameLength); if (name.Length >= 4 && name.Buffer[1] == '\\') { ((PWSTR) ((PCHAR) point + point->DeviceNameOffset))[1] = '?'; } h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { RtlFreeHeap(RtlProcessHeap(), 0, point); return FALSE; } b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, point, name.Length + sizeof(MOUNTMGR_MOUNT_POINT), &points, sizeof(MOUNTMGR_MOUNT_POINTS), &bytes, NULL); if (b) { if (points.NumberOfMountPoints) { *IsVolume = TRUE; } else { *IsVolume = FALSE; } } else { if (GetLastError() == ERROR_MORE_DATA) { *IsVolume = TRUE; } else { *IsVolume = FALSE; } } CloseHandle(h); RtlFreeHeap(RtlProcessHeap(), 0, point); return TRUE; } BOOL WINAPI FindNextVolumeMountPointW( HANDLE hFindVolumeMountPoint, LPWSTR lpszVolumeMountPoint, DWORD cchBufferLength ) /*++ Routine Description: This routine continues the enumeration of all volume mount point on the given volume. Arguments: hFindVolumeMountPoint - Supplies the handle for the enumeration. lpszVolumeMountPoint - Returns the volume mount point. cchBufferLength - Supplies the volume mount point buffer length. Return Value: FALSE - Failure. TRUE - Success. --*/ { return FindNextVolumeMountPointHelper(hFindVolumeMountPoint, lpszVolumeMountPoint, cchBufferLength, FALSE); } BOOL WINAPI FindVolumeMountPointClose( HANDLE hFindVolumeMountPoint ) { return CloseHandle(hFindVolumeMountPoint); } BOOL WINAPI GetVolumeNameForVolumeMountPointA( LPCSTR lpszVolumeMountPoint, LPSTR lpszVolumeName, DWORD cchBufferLength ) { PUNICODE_STRING unicodeVolumeMountPoint; ANSI_STRING ansiVolumeName; UNICODE_STRING unicodeVolumeName; BOOL b; NTSTATUS status; unicodeVolumeMountPoint = Basep8BitStringToStaticUnicodeString(lpszVolumeMountPoint); if (!unicodeVolumeMountPoint) { return FALSE; } ansiVolumeName.Buffer = lpszVolumeName; ansiVolumeName.MaximumLength = (USHORT) (cchBufferLength - 1); unicodeVolumeName.Buffer = NULL; unicodeVolumeName.MaximumLength = 0; try { unicodeVolumeName.MaximumLength = (ansiVolumeName.MaximumLength + 1)*sizeof(WCHAR); unicodeVolumeName.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), unicodeVolumeName.MaximumLength); if (!unicodeVolumeName.Buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } b = GetVolumeNameForVolumeMountPointW(unicodeVolumeMountPoint->Buffer, unicodeVolumeName.Buffer, cchBufferLength); if (b) { RtlInitUnicodeString(&unicodeVolumeName, unicodeVolumeName.Buffer); status = BasepUnicodeStringTo8BitString(&ansiVolumeName, &unicodeVolumeName, FALSE); if (!NT_SUCCESS(status)) { BaseSetLastNTError(status); return FALSE; } ansiVolumeName.Buffer[ansiVolumeName.Length] = 0; } } finally { if (unicodeVolumeName.Buffer) { RtlFreeHeap(RtlProcessHeap(), 0, unicodeVolumeName.Buffer); } } return b; } BOOL GetVolumeNameForRoot( LPCWSTR DeviceName, LPWSTR lpszVolumeName, DWORD cchBufferLength ) /*++ Routine Description: This routine queries the volume name for the given NT device name. Arguments: DeviceName - Supplies a DOS device name terminated by a '\'. lpszVolumeName - Returns the volume name pointed to by the DOS device name. cchBufferLength - Supplies the size of the preceeding buffer. Return Value: FALSE - Failure. The error code is returned in GetLastError(). TRUE - Success. --*/ { NTSTATUS status; UNICODE_STRING devicePath, symName; OBJECT_ATTRIBUTES oa; HANDLE h; IO_STATUS_BLOCK ioStatus; WCHAR buffer[MAX_PATH]; PMOUNTDEV_NAME name; BOOL b; DWORD bytes, i; PMOUNTMGR_MOUNT_POINT point; PMOUNTMGR_MOUNT_POINTS points; if (GetDriveTypeW(DeviceName) == DRIVE_REMOTE) { SetLastError(ERROR_PATH_NOT_FOUND); return FALSE; } if (!RtlDosPathNameToNtPathName_U(DeviceName, &devicePath, NULL, NULL)) { SetLastError(ERROR_PATH_NOT_FOUND); return FALSE; } if (devicePath.Buffer[devicePath.Length/sizeof(WCHAR) - 1] == '\\') { devicePath.Buffer[devicePath.Length/sizeof(WCHAR) - 1] = 0; devicePath.Length -= sizeof(WCHAR); } if (devicePath.Length >= 2*sizeof(WCHAR) && devicePath.Buffer[devicePath.Length/sizeof(WCHAR) - 1] == ':') { devicePath.Buffer[devicePath.Length/sizeof(WCHAR) - 2] = (WCHAR) toupper(devicePath.Buffer[devicePath.Length/sizeof(WCHAR) - 2]); } InitializeObjectAttributes(&oa, &devicePath, OBJ_CASE_INSENSITIVE, NULL, NULL); status = NtOpenFile(&h, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT); if (!NT_SUCCESS(status)) { RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer); SetLastError(RtlNtStatusToDosError(status)); return FALSE; } name = (PMOUNTDEV_NAME) buffer; b = DeviceIoControl(h, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, name, MAX_PATH*sizeof(WCHAR), &bytes, NULL); NtClose(h); if (!b) { RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer); return FALSE; } RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer); devicePath.Length = name->NameLength; devicePath.MaximumLength = devicePath.Length + sizeof(WCHAR); devicePath.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), devicePath.MaximumLength); if (!devicePath.Buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } RtlCopyMemory(devicePath.Buffer, name->Name, name->NameLength); devicePath.Buffer[devicePath.Length/sizeof(WCHAR)] = 0; point = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), devicePath.Length + sizeof(MOUNTMGR_MOUNT_POINT)); if (!point) { RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } RtlZeroMemory(point, sizeof(MOUNTMGR_MOUNT_POINT)); point->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT); point->DeviceNameLength = devicePath.Length; RtlCopyMemory((PCHAR) point + point->DeviceNameOffset, devicePath.Buffer, point->DeviceNameLength); RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer); points = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), sizeof(MOUNTMGR_MOUNT_POINTS)); if (!points) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); RtlFreeHeap(RtlProcessHeap(), 0, point); return FALSE; } h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { RtlFreeHeap(RtlProcessHeap(), 0, points); RtlFreeHeap(RtlProcessHeap(), 0, point); return FALSE; } b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, point, devicePath.Length + sizeof(MOUNTMGR_MOUNT_POINT), points, sizeof(MOUNTMGR_MOUNT_POINTS), &bytes, NULL); while (!b && GetLastError() == ERROR_MORE_DATA) { bytes = points->Size; RtlFreeHeap(RtlProcessHeap(), 0, points); points = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), bytes); if (!points) { CloseHandle(h); SetLastError(ERROR_NOT_ENOUGH_MEMORY); RtlFreeHeap(RtlProcessHeap(), 0, point); return FALSE; } b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, point, devicePath.Length + sizeof(MOUNTMGR_MOUNT_POINT), points, bytes, &bytes, NULL); } CloseHandle(h); RtlFreeHeap(RtlProcessHeap(), 0, point); if (!b) { RtlFreeHeap(RtlProcessHeap(), 0, points); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } for (i = 0; i < points->NumberOfMountPoints; i++) { symName.Length = symName.MaximumLength = points->MountPoints[i].SymbolicLinkNameLength; symName.Buffer = (PWSTR) ((PCHAR) points + points->MountPoints[i].SymbolicLinkNameOffset); if (MOUNTMGR_IS_NT_VOLUME_NAME(&symName)) { break; } } if (i == points->NumberOfMountPoints) { RtlFreeHeap(RtlProcessHeap(), 0, points); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (cchBufferLength*sizeof(WCHAR) < symName.Length + 2*sizeof(WCHAR)) { RtlFreeHeap(RtlProcessHeap(), 0, points); SetLastError(ERROR_FILENAME_EXCED_RANGE); return FALSE; } RtlCopyMemory(lpszVolumeName, symName.Buffer, symName.Length); lpszVolumeName[1] = '\\'; lpszVolumeName[symName.Length/sizeof(WCHAR)] = '\\'; lpszVolumeName[symName.Length/sizeof(WCHAR) + 1] = 0; RtlFreeHeap(RtlProcessHeap(), 0, points); return TRUE; } BOOL BasepGetVolumeNameFromReparsePoint( LPCWSTR lpszVolumeMountPoint, LPWSTR lpszVolumeName, DWORD cchBufferLength, PBOOL ResultOfOpen ) { HANDLE h; PREPARSE_DATA_BUFFER reparse; BOOL b; DWORD bytes; UNICODE_STRING mountName; WCHAR c; h = CreateFileW(lpszVolumeMountPoint, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { if (ResultOfOpen) { *ResultOfOpen = FALSE; } return FALSE; } if (ResultOfOpen) { *ResultOfOpen = TRUE; } reparse = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), MAXIMUM_REPARSE_DATA_BUFFER_SIZE); if (!reparse) { CloseHandle(h); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } b = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, reparse, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytes, NULL); CloseHandle(h); if (!b || reparse->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) { RtlFreeHeap(RtlProcessHeap(), 0, reparse); return FALSE; } if (cchBufferLength*sizeof(WCHAR) < reparse->MountPointReparseBuffer.SubstituteNameLength + sizeof(WCHAR)) { RtlFreeHeap(RtlProcessHeap(), 0, reparse); SetLastError(ERROR_FILENAME_EXCED_RANGE); return FALSE; } RtlCopyMemory(lpszVolumeName, (PCHAR) reparse->MountPointReparseBuffer.PathBuffer + reparse->MountPointReparseBuffer.SubstituteNameOffset, reparse->MountPointReparseBuffer.SubstituteNameLength); c = lpszVolumeName[1]; lpszVolumeName[1] = '\\'; lpszVolumeName[reparse->MountPointReparseBuffer.SubstituteNameLength/ sizeof(WCHAR)] = 0; mountName.Length = mountName.MaximumLength = reparse->MountPointReparseBuffer.SubstituteNameLength; mountName.Buffer = lpszVolumeName; RtlFreeHeap(RtlProcessHeap(), 0, reparse); if (!MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&mountName)) { lpszVolumeName[1] = c; SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } return TRUE; } BOOL BasepGetVolumeNameForVolumeMountPoint( LPCWSTR lpszVolumeMountPoint, LPWSTR lpszVolumeName, DWORD cchBufferLength, PBOOL ResultOfOpen ) /*++ Routine Description: This routine returns the volume name for a given volume mount point. Arguments: lpszVolumeMountPoint - Supplies the volume mount point. lpszVolumeName - Returns the volume name. cchBufferLength - Supplies the size of the volume name buffer. Return Value: FALSE - Failure. TRUE - Success. --*/ { UNICODE_STRING unicodeVolumeMountPoint; BOOL b; if (ResultOfOpen) { *ResultOfOpen = TRUE; } RtlInitUnicodeString(&unicodeVolumeMountPoint, lpszVolumeMountPoint); if (unicodeVolumeMountPoint.Buffer[ unicodeVolumeMountPoint.Length/sizeof(WCHAR) - 1] != '\\') { BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID); if (lpszVolumeName && cchBufferLength >= 1) { *lpszVolumeName = 0; } return FALSE; } if (unicodeVolumeMountPoint.Length == 6 && unicodeVolumeMountPoint.Buffer[1] == ':') { b = GetVolumeNameForRoot(lpszVolumeMountPoint, lpszVolumeName, cchBufferLength); if (!b && lpszVolumeName && cchBufferLength >= 1) { *lpszVolumeName = 0; } return b; } if (unicodeVolumeMountPoint.Length == 14 && unicodeVolumeMountPoint.Buffer[0] == '\\' && unicodeVolumeMountPoint.Buffer[1] == '\\' && (unicodeVolumeMountPoint.Buffer[2] == '.' || unicodeVolumeMountPoint.Buffer[2] == '?') && unicodeVolumeMountPoint.Buffer[3] == '\\' && unicodeVolumeMountPoint.Buffer[5] == ':') { b = GetVolumeNameForRoot(lpszVolumeMountPoint + 4, lpszVolumeName, cchBufferLength); if (!b && lpszVolumeName && cchBufferLength >= 1) { *lpszVolumeName = 0; } return b; } if (GetVolumeNameForRoot(lpszVolumeMountPoint, lpszVolumeName, cchBufferLength)) { return TRUE; } b = BasepGetVolumeNameFromReparsePoint(lpszVolumeMountPoint, lpszVolumeName, cchBufferLength, ResultOfOpen); if (!b && lpszVolumeName && cchBufferLength >= 1) { *lpszVolumeName = 0; } return b; } BOOL WINAPI GetVolumeNameForVolumeMountPointW( LPCWSTR lpszVolumeMountPoint, LPWSTR lpszVolumeName, DWORD cchBufferLength ) /*++ Routine Description: This routine returns the volume name for a given volume mount point. Arguments: lpszVolumeMountPoint - Supplies the volume mount point. lpszVolumeName - Returns the volume name. cchBufferLength - Supplies the size of the volume name buffer. Return Value: FALSE - Failure. TRUE - Success. --*/ { return BasepGetVolumeNameForVolumeMountPoint(lpszVolumeMountPoint, lpszVolumeName, cchBufferLength, NULL); } BOOL WINAPI SetVolumeMountPointA( LPCSTR lpszVolumeMountPoint, LPCSTR lpszVolumeName ) { PUNICODE_STRING unicodeVolumeMountPoint; UNICODE_STRING unicodeVolumeName; BOOL b; unicodeVolumeMountPoint = Basep8BitStringToStaticUnicodeString( lpszVolumeMountPoint); if (!unicodeVolumeMountPoint) { return FALSE; } if (!Basep8BitStringToDynamicUnicodeString(&unicodeVolumeName, lpszVolumeName)) { return FALSE; } b = SetVolumeMountPointW(unicodeVolumeMountPoint->Buffer, unicodeVolumeName.Buffer); RtlFreeUnicodeString(&unicodeVolumeName); return b; } BOOL SetVolumeNameForRoot( LPCWSTR DeviceName, LPCWSTR lpszVolumeName ) /*++ Routine Description: This routine sets the volume name for the given DOS device name. Arguments: DeviceName - Supplies a DOS device name terminated by a '\'. lpszVolumeName - Supplies the volume name that the DOS device name will point to. Return Value: FALSE - Failure. The error code is returned in GetLastError(). TRUE - Success. --*/ { UNICODE_STRING devicePath, volumeName; DWORD inputLength; PMOUNTMGR_CREATE_POINT_INPUT input; HANDLE h; BOOL b; DWORD bytes; devicePath.Length = 28; devicePath.MaximumLength = devicePath.Length + sizeof(WCHAR); devicePath.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), devicePath.MaximumLength); if (!devicePath.Buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } RtlCopyMemory(devicePath.Buffer, L"\\DosDevices\\", 24); devicePath.Buffer[12] = (WCHAR)toupper(DeviceName[0]); devicePath.Buffer[13] = ':'; devicePath.Buffer[14] = 0; RtlInitUnicodeString(&volumeName, lpszVolumeName); volumeName.Length -= sizeof(WCHAR); inputLength = sizeof(MOUNTMGR_CREATE_POINT_INPUT) + devicePath.Length + volumeName.Length; input = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), inputLength); if (!input) { RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } input->SymbolicLinkNameOffset = sizeof(MOUNTMGR_CREATE_POINT_INPUT); input->SymbolicLinkNameLength = devicePath.Length; input->DeviceNameOffset = input->SymbolicLinkNameOffset + input->SymbolicLinkNameLength; input->DeviceNameLength = volumeName.Length; RtlCopyMemory((PCHAR) input + input->SymbolicLinkNameOffset, devicePath.Buffer, input->SymbolicLinkNameLength); RtlCopyMemory((PCHAR) input + input->DeviceNameOffset, volumeName.Buffer, input->DeviceNameLength); ((PWSTR) ((PCHAR) input + input->DeviceNameOffset))[1] = '?'; RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer); h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { RtlFreeHeap(RtlProcessHeap(), 0, input); return FALSE; } b = DeviceIoControl(h, IOCTL_MOUNTMGR_CREATE_POINT, input, inputLength, NULL, 0, &bytes, NULL); CloseHandle(h); RtlFreeHeap(RtlProcessHeap(), 0, input); return b; } VOID NotifyMountMgr( LPCWSTR lpszVolumeMountPoint, LPCWSTR lpszVolumeName, BOOL IsPointCreated ) /*++ Routine Description: This routine notifies the mount mgr that a volume mount point was created or deleted so that the mount mgr can update the remote database on the volume where the mount point was created or deleted. Arguments: lpszVolumeMountPoint - Supplies the directory where the volume mount point resides. lpszVolumeName - Supplies the volume name. IsPointCreated - Supplies wheter or not the point was created or deleted. Return Value: None. --*/ { UNICODE_STRING unicodeSourceVolumeName; UNICODE_STRING unicodeTargetVolumeName; DWORD inputSize; PMOUNTMGR_VOLUME_MOUNT_POINT input; HANDLE h; DWORD ioControl, bytes; if (!RtlDosPathNameToNtPathName_U(lpszVolumeMountPoint, &unicodeSourceVolumeName, NULL, NULL)) { return; } RtlInitUnicodeString(&unicodeTargetVolumeName, lpszVolumeName); unicodeSourceVolumeName.Length -= sizeof(WCHAR); unicodeTargetVolumeName.Length -= sizeof(WCHAR); inputSize = sizeof(MOUNTMGR_VOLUME_MOUNT_POINT) + unicodeSourceVolumeName.Length + unicodeTargetVolumeName.Length; input = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), inputSize); if (!input) { RtlFreeHeap(RtlProcessHeap(), 0, unicodeSourceVolumeName.Buffer); return; } input->SourceVolumeNameOffset = sizeof(MOUNTMGR_VOLUME_MOUNT_POINT); input->SourceVolumeNameLength = unicodeSourceVolumeName.Length; input->TargetVolumeNameOffset = input->SourceVolumeNameOffset + input->SourceVolumeNameLength; input->TargetVolumeNameLength = unicodeTargetVolumeName.Length; RtlCopyMemory((PCHAR) input + input->SourceVolumeNameOffset, unicodeSourceVolumeName.Buffer, input->SourceVolumeNameLength); RtlCopyMemory((PCHAR) input + input->TargetVolumeNameOffset, unicodeTargetVolumeName.Buffer, input->TargetVolumeNameLength); ((PWSTR) ((PCHAR) input + input->TargetVolumeNameOffset))[1] = '?'; h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { RtlFreeHeap(RtlProcessHeap(), 0, input); RtlFreeHeap(RtlProcessHeap(), 0, unicodeSourceVolumeName.Buffer); return; } if (IsPointCreated) { ioControl = IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED; } else { ioControl = IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED; } DeviceIoControl(h, ioControl, input, inputSize, NULL, 0, &bytes, NULL); CloseHandle(h); RtlFreeHeap(RtlProcessHeap(), 0, input); RtlFreeHeap(RtlProcessHeap(), 0, unicodeSourceVolumeName.Buffer); } BOOL WINAPI SetVolumeMountPointW( LPCWSTR lpszVolumeMountPoint, LPCWSTR lpszVolumeName ) /*++ Routine Description: This routine sets a mount point on the given directory pointed to by 'VolumeMountPoint' which points to the volume given by 'VolumeName'. In the case when 'VolumeMountPoint' is of the form "D:\", the drive letter for the given volume is set to 'D:'. Arguments: lpszVolumeMountPoint - Supplies the directory where the volume mount point will reside. lpszVolumeName - Supplies the volume name. Return Value: FALSE - Failure. TRUE - Success. --*/ { UNICODE_STRING unicodeVolumeMountPoint; UNICODE_STRING unicodeVolumeName; BOOLEAN isVolume; BOOL b; WCHAR volumeMountPointVolumePrefix[MAX_PATH]; HANDLE h; PREPARSE_DATA_BUFFER reparse; DWORD bytes; if (GetVolumeNameForVolumeMountPointW(lpszVolumeMountPoint, volumeMountPointVolumePrefix, MAX_PATH) || GetLastError() == ERROR_FILENAME_EXCED_RANGE) { SetLastError(ERROR_DIR_NOT_EMPTY); return FALSE; } RtlInitUnicodeString(&unicodeVolumeMountPoint, lpszVolumeMountPoint); RtlInitUnicodeString(&unicodeVolumeName, lpszVolumeName); if (unicodeVolumeMountPoint.Length == 0 || unicodeVolumeName.Length == 0) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (unicodeVolumeMountPoint.Buffer[ unicodeVolumeMountPoint.Length/sizeof(WCHAR) - 1] != '\\' || unicodeVolumeName.Buffer[ unicodeVolumeName.Length/sizeof(WCHAR) - 1] != '\\') { BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID); return FALSE; } if (!MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&unicodeVolumeName)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (!IsThisAVolumeName(lpszVolumeName, &isVolume)) { return FALSE; } if (!isVolume) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (unicodeVolumeMountPoint.Length == 6 && unicodeVolumeMountPoint.Buffer[1] == ':') { return SetVolumeNameForRoot(lpszVolumeMountPoint, lpszVolumeName); } if (unicodeVolumeMountPoint.Length == 14 && unicodeVolumeMountPoint.Buffer[0] == '\\' && unicodeVolumeMountPoint.Buffer[1] == '\\' && (unicodeVolumeMountPoint.Buffer[2] == '.' || unicodeVolumeMountPoint.Buffer[2] == '?') && unicodeVolumeMountPoint.Buffer[3] == '\\' && unicodeVolumeMountPoint.Buffer[5] == ':') { return SetVolumeNameForRoot(lpszVolumeMountPoint + 4, lpszVolumeName); } if (GetDriveTypeW(lpszVolumeMountPoint) != DRIVE_FIXED) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { return FALSE; } CloseHandle(h); h = CreateFileW(lpszVolumeMountPoint, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { return FALSE; } reparse = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), MAXIMUM_REPARSE_DATA_BUFFER_SIZE); if (!reparse) { CloseHandle(h); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } reparse->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; reparse->ReparseDataLength = (USHORT) (FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - REPARSE_DATA_BUFFER_HEADER_SIZE + unicodeVolumeName.Length + 2*sizeof(WCHAR)); reparse->Reserved = 0; reparse->MountPointReparseBuffer.SubstituteNameOffset = 0; reparse->MountPointReparseBuffer.SubstituteNameLength = unicodeVolumeName.Length; reparse->MountPointReparseBuffer.PrintNameOffset = reparse->MountPointReparseBuffer.SubstituteNameLength + sizeof(WCHAR); reparse->MountPointReparseBuffer.PrintNameLength = 0; CopyMemory(reparse->MountPointReparseBuffer.PathBuffer, unicodeVolumeName.Buffer, reparse->MountPointReparseBuffer.SubstituteNameLength); reparse->MountPointReparseBuffer.PathBuffer[1] = '?'; reparse->MountPointReparseBuffer.PathBuffer[ unicodeVolumeName.Length/sizeof(WCHAR)] = 0; reparse->MountPointReparseBuffer.PathBuffer[ unicodeVolumeName.Length/sizeof(WCHAR) + 1] = 0; b = DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, reparse, REPARSE_DATA_BUFFER_HEADER_SIZE + reparse->ReparseDataLength, NULL, 0, &bytes, NULL); RtlFreeHeap(RtlProcessHeap(), 0, reparse); CloseHandle(h); if (b) { NotifyMountMgr(lpszVolumeMountPoint, lpszVolumeName, TRUE); } return b; } BOOL WINAPI DeleteVolumeMountPointA( LPCSTR lpszVolumeMountPoint ) { PUNICODE_STRING unicodeVolumeMountPoint; BOOL b; unicodeVolumeMountPoint = Basep8BitStringToStaticUnicodeString( lpszVolumeMountPoint); if (!unicodeVolumeMountPoint) { return FALSE; } b = DeleteVolumeMountPointW(unicodeVolumeMountPoint->Buffer); return b; } BOOL DeleteVolumeNameForRoot( LPCWSTR DeviceName ) /*++ Routine Description: This routine deletes the given DOS device name. Arguments: DeviceName - Supplies a DOS device name terminated by a '\'. Return Value: FALSE - Failure. The error code is returned in GetLastError(). TRUE - Success. --*/ { UNICODE_STRING devicePath; DWORD inputLength; PMOUNTMGR_MOUNT_POINT input; DWORD outputLength; PMOUNTMGR_MOUNT_POINTS output; HANDLE h; BOOL b; DWORD bytes; devicePath.Length = 28; devicePath.MaximumLength = devicePath.Length + sizeof(WCHAR); devicePath.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), devicePath.MaximumLength); if (!devicePath.Buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } RtlCopyMemory(devicePath.Buffer, L"\\DosDevices\\", 24); devicePath.Buffer[12] = (WCHAR)toupper(DeviceName[0]); devicePath.Buffer[13] = ':'; devicePath.Buffer[14] = 0; inputLength = sizeof(MOUNTMGR_MOUNT_POINT) + devicePath.Length; input = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), inputLength); if (!input) { RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } RtlZeroMemory(input, sizeof(MOUNTMGR_MOUNT_POINT)); input->SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINT); input->SymbolicLinkNameLength = devicePath.Length; RtlCopyMemory((PCHAR) input + input->SymbolicLinkNameOffset, devicePath.Buffer, input->SymbolicLinkNameLength); RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer); outputLength = sizeof(MOUNTMGR_MOUNT_POINTS) + 3*MAX_PATH*sizeof(WCHAR); output = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), outputLength); if (!output) { RtlFreeHeap(RtlProcessHeap(), 0, input); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { RtlFreeHeap(RtlProcessHeap(), 0, output); RtlFreeHeap(RtlProcessHeap(), 0, input); return FALSE; } b = DeviceIoControl(h, IOCTL_MOUNTMGR_DELETE_POINTS, input, inputLength, output, outputLength, &bytes, NULL); CloseHandle(h); RtlFreeHeap(RtlProcessHeap(), 0, output); RtlFreeHeap(RtlProcessHeap(), 0, input); return b; } BOOL WINAPI DeleteVolumeMountPointW( LPCWSTR lpszVolumeMountPoint ) /*++ Routine Description: This routine removes the NTFS junction point from the given directory or remove the drive letter symbolic link pointing to the given volume. Arguments: lpszVolumeMountPoint - Supplies the volume mount piont. Return Value: FALSE - Failure. TRUE - Success. --*/ { UNICODE_STRING unicodeVolumeMountPoint; HANDLE h; PREPARSE_DATA_BUFFER reparse; BOOL b; DWORD bytes; UNICODE_STRING substituteName; RtlInitUnicodeString(&unicodeVolumeMountPoint, lpszVolumeMountPoint); if (unicodeVolumeMountPoint.Buffer[ unicodeVolumeMountPoint.Length/sizeof(WCHAR) - 1] != '\\') { BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID); return FALSE; } if (unicodeVolumeMountPoint.Length == 6 && unicodeVolumeMountPoint.Buffer[1] == ':') { return DeleteVolumeNameForRoot(lpszVolumeMountPoint); } if (unicodeVolumeMountPoint.Length == 14 && unicodeVolumeMountPoint.Buffer[0] == '\\' && unicodeVolumeMountPoint.Buffer[1] == '\\' && (unicodeVolumeMountPoint.Buffer[2] == '.' || unicodeVolumeMountPoint.Buffer[2] == '?') && unicodeVolumeMountPoint.Buffer[3] == '\\' && unicodeVolumeMountPoint.Buffer[5] == ':') { return DeleteVolumeNameForRoot(lpszVolumeMountPoint + 4); } h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { return FALSE; } CloseHandle(h); h = CreateFileW(lpszVolumeMountPoint, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { return FALSE; } reparse = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), MAXIMUM_REPARSE_DATA_BUFFER_SIZE); if (!reparse) { CloseHandle(h); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } b = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, reparse, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytes, NULL); if (!b || reparse->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) { RtlFreeHeap(RtlProcessHeap(), 0, reparse); CloseHandle(h); return FALSE; } substituteName.MaximumLength = substituteName.Length = reparse->MountPointReparseBuffer.SubstituteNameLength; substituteName.Buffer = (PWSTR) ((PCHAR) reparse->MountPointReparseBuffer.PathBuffer + reparse->MountPointReparseBuffer.SubstituteNameOffset); if (!MOUNTMGR_IS_NT_VOLUME_NAME_WB(&substituteName)) { RtlFreeHeap(RtlProcessHeap(), 0, reparse); CloseHandle(h); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } reparse->ReparseDataLength = 0; b = DeviceIoControl(h, FSCTL_DELETE_REPARSE_POINT, reparse, REPARSE_DATA_BUFFER_HEADER_SIZE, NULL, 0, &bytes, NULL); CloseHandle(h); if (b) { substituteName.Buffer[1] = '\\'; substituteName.Buffer[substituteName.Length/sizeof(WCHAR)] = 0; NotifyMountMgr(lpszVolumeMountPoint, substituteName.Buffer, FALSE); } RtlFreeHeap(RtlProcessHeap(), 0, reparse); return b; } BOOL WINAPI GetVolumePathNameA( LPCSTR lpszFileName, LPSTR lpszVolumePathName, DWORD cchBufferLength ) { PUNICODE_STRING unicodeFileName; ANSI_STRING ansiVolumePathName; UNICODE_STRING unicodeVolumePathName; BOOL b; NTSTATUS status; unicodeFileName = Basep8BitStringToStaticUnicodeString(lpszFileName); if (!unicodeFileName) { return FALSE; } ansiVolumePathName.Buffer = lpszVolumePathName; ansiVolumePathName.MaximumLength = (USHORT) (cchBufferLength - 1); unicodeVolumePathName.Buffer = NULL; unicodeVolumePathName.MaximumLength = 0; try { unicodeVolumePathName.MaximumLength = (ansiVolumePathName.MaximumLength + 1)*sizeof(WCHAR); unicodeVolumePathName.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), unicodeVolumePathName.MaximumLength); if (!unicodeVolumePathName.Buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } b = GetVolumePathNameW(unicodeFileName->Buffer, unicodeVolumePathName.Buffer, cchBufferLength); if (b) { RtlInitUnicodeString(&unicodeVolumePathName, unicodeVolumePathName.Buffer); status = BasepUnicodeStringTo8BitString(&ansiVolumePathName, &unicodeVolumePathName, FALSE); if (!NT_SUCCESS(status)) { BaseSetLastNTError(status); return FALSE; } ansiVolumePathName.Buffer[ansiVolumePathName.Length] = 0; } } finally { if (unicodeVolumePathName.Buffer) { RtlFreeHeap(RtlProcessHeap(), 0, unicodeVolumePathName.Buffer); } } return b; } BOOL WINAPI GetVolumePathNameW( LPCWSTR lpszFileName, LPWSTR lpszVolumePathName, DWORD cchBufferLength ) /*++ Routine Description: This routine will return a full path whose prefix is the longest prefix that represents a volume. Arguments: lpszFileName - Supplies the file name. lpszVolumePathName - Returns the volume path name. cchBufferLength - Supplies the volume path name buffer length. Return Value: FALSE - Failure. TRUE - Success. --*/ { DWORD fullPathLength; PWSTR fullPath, p; WCHAR c; UNICODE_STRING name, dosName, prefix; BOOL b, resultOfOpen; PWSTR volumeName; DWORD i; fullPathLength = GetFullPathNameW(lpszFileName, 0, NULL, NULL); if (!fullPathLength) { return FALSE; } fullPathLength += 10; fullPath = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), fullPathLength*sizeof(WCHAR)); if (!fullPath) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } if (!GetFullPathNameW(lpszFileName, fullPathLength, fullPath, &p)) { RtlFreeHeap(RtlProcessHeap(), 0, fullPath); return FALSE; } RtlInitUnicodeString(&name, fullPath); // // Append a trailing backslash to start the search. // if (name.Buffer[(name.Length/sizeof(WCHAR)) - 1] != '\\') { name.Length += sizeof(WCHAR); name.Buffer[(name.Length/sizeof(WCHAR)) - 1] = '\\'; name.Buffer[name.Length/sizeof(WCHAR)] = 0; } volumeName = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), MAXIMUM_REPARSE_DATA_BUFFER_SIZE); if (!volumeName) { RtlFreeHeap(RtlProcessHeap(), 0, fullPath); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } p = NULL; c = 0; for (;;) { b = BasepGetVolumeNameForVolumeMountPoint( name.Buffer, volumeName, MAXIMUM_REPARSE_DATA_BUFFER_SIZE/ sizeof(WCHAR), &resultOfOpen); if (b) { break; } if (!resultOfOpen && GetLastError() == ERROR_ACCESS_DENIED) { resultOfOpen = TRUE; } if (*volumeName) { RtlFreeHeap(RtlProcessHeap(), 0, fullPath); if (volumeName[0] == '\\' && volumeName[1] == '?' && volumeName[2] == '?' && volumeName[3] == '\\') { if (volumeName[4] && volumeName[5] == ':') { RtlInitUnicodeString(&name, volumeName); MoveMemory(volumeName, volumeName + 4, name.Length - 3*sizeof(WCHAR)); } else { volumeName[1] = '\\'; } b = GetVolumePathNameW(volumeName, lpszVolumePathName, cchBufferLength); } else { RtlInitUnicodeString(&name, volumeName); RtlInitUnicodeString(&prefix, L"\\\\?\\GLOBALROOT"); dosName.Length = name.Length + prefix.Length; dosName.MaximumLength = dosName.Length + sizeof(WCHAR); dosName.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), dosName.MaximumLength); if (!dosName.Buffer) { RtlFreeHeap(RtlProcessHeap(), 0, volumeName); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } CopyMemory(dosName.Buffer, prefix.Buffer, prefix.Length); CopyMemory((PCHAR) dosName.Buffer + prefix.Length, name.Buffer, name.Length); dosName.Buffer[dosName.Length/sizeof(WCHAR)] = 0; b = GetVolumePathNameW(dosName.Buffer, lpszVolumePathName, cchBufferLength); RtlFreeHeap(RtlProcessHeap(), 0, dosName.Buffer); } RtlFreeHeap(RtlProcessHeap(), 0, volumeName); return b; } if (!resultOfOpen && p) { *p = c; RtlInitUnicodeString(&name, fullPath); break; } if (name.Length <= sizeof(WCHAR)) { break; } for (i = name.Length/sizeof(WCHAR) - 2; i > 0; i--) { if (name.Buffer[i] == '\\') { break; } } if (!i) { break; } if (resultOfOpen) { p = &name.Buffer[i + 1]; c = *p; *p = 0; } else { name.Buffer[i + 1] = 0; } RtlInitUnicodeString(&name, fullPath); } RtlFreeHeap(RtlProcessHeap(), 0, volumeName); if (!resultOfOpen && !p) { RtlFreeHeap(RtlProcessHeap(), 0, fullPath); return FALSE; } if (cchBufferLength*sizeof(WCHAR) < name.Length + sizeof(WCHAR)) { RtlFreeHeap(RtlProcessHeap(), 0, fullPath); SetLastError(ERROR_FILENAME_EXCED_RANGE); return FALSE; } RtlCopyMemory(lpszVolumePathName, name.Buffer, name.Length); lpszVolumePathName[name.Length/sizeof(WCHAR)] = 0; RtlFreeHeap(RtlProcessHeap(), 0, fullPath); return TRUE; } BOOL GetVolumePathNamesForVolumeNameA( LPCSTR lpszVolumeName, LPSTR lpszVolumePathNames, DWORD cchBufferLength, PDWORD lpcchReturnLength ) { PUNICODE_STRING unicodeVolumeName; ANSI_STRING ansiVolumePathNames; UNICODE_STRING unicodeVolumePathNames; BOOL b; NTSTATUS status; DWORD len; unicodeVolumeName = Basep8BitStringToStaticUnicodeString(lpszVolumeName); if (!unicodeVolumeName) { return FALSE; } ansiVolumePathNames.Buffer = lpszVolumePathNames; ansiVolumePathNames.MaximumLength = (USHORT) cchBufferLength; unicodeVolumePathNames.Buffer = NULL; unicodeVolumePathNames.MaximumLength = 0; try { unicodeVolumePathNames.MaximumLength = ansiVolumePathNames.MaximumLength*sizeof(WCHAR); if (unicodeVolumePathNames.MaximumLength) { unicodeVolumePathNames.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), unicodeVolumePathNames.MaximumLength); if (!unicodeVolumePathNames.Buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } } else { unicodeVolumePathNames.Buffer = NULL; } b = GetVolumePathNamesForVolumeNameW(unicodeVolumeName->Buffer, unicodeVolumePathNames.Buffer, cchBufferLength, &len); if (b || GetLastError() == ERROR_MORE_DATA) { if (b) { unicodeVolumePathNames.Length = (USHORT) len*sizeof(WCHAR); } else { unicodeVolumePathNames.Length = (USHORT) cchBufferLength*sizeof(WCHAR); } status = BasepUnicodeStringTo8BitString(&ansiVolumePathNames, &unicodeVolumePathNames, FALSE); if (!NT_SUCCESS(status)) { BaseSetLastNTError(status); return FALSE; } if (lpcchReturnLength) { if (b) { *lpcchReturnLength = ansiVolumePathNames.Length/sizeof(WCHAR); } else { // Give an upper bound for the ANSI length since we // don't actually know it. *lpcchReturnLength = 2*len; } } } } finally { if (unicodeVolumePathNames.Buffer) { RtlFreeHeap(RtlProcessHeap(), 0, unicodeVolumePathNames.Buffer); } } return b; } BOOL GetVolumePathNamesForVolumeNameW( LPCWSTR lpszVolumeName, LPWSTR lpszVolumePathNames, DWORD cchBufferLength, PDWORD lpcchReturnLength ) /*++ Routine Description: This routine returns a Multi-Sz list of volume path names for the given volume name. The returned 'lpcchReturnLength' will include the extra tailing null characteristic of a Multi-Sz unless ERROR_MORE_DATA is returned in which case the list returned is as long as possible and may contain a part of a volume path. Arguments: lpszVolumeName - Supplies the volume name. lpszVolumePathNames - Returns the volume path names. cchBufferLength - Supplies the size of the return buffer. lpcchReturnLength - Returns the number of characters copied back to the return buffer on success or the total number of characters necessary for the buffer on ERROR_MORE_DATA. Return Value: FALSE - Failure. TRUE - Success. --*/ { UNICODE_STRING unicodeVolumeName; PMOUNTMGR_TARGET_NAME targetName; HANDLE h; BOOL b; DWORD bytes, len, i, j, n; PMOUNTMGR_VOLUME_PATHS volumePaths; RtlInitUnicodeString(&unicodeVolumeName, lpszVolumeName); if (unicodeVolumeName.Buffer[unicodeVolumeName.Length/sizeof(WCHAR) - 1] != '\\') { BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID); return FALSE; } if (!MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&unicodeVolumeName)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } targetName = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), MAX_PATH*sizeof(WCHAR)); if (!targetName) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } ZeroMemory(targetName, MAX_PATH*sizeof(WCHAR)); targetName->DeviceNameLength = unicodeVolumeName.Length - sizeof(WCHAR); RtlCopyMemory(targetName->DeviceName, lpszVolumeName, targetName->DeviceNameLength); targetName->DeviceName[1] = '?'; h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { RtlFreeHeap(RtlProcessHeap(), 0, targetName); return FALSE; } len = sizeof(MOUNTMGR_VOLUME_PATHS); volumePaths = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), len); if (!volumePaths) { CloseHandle(h); RtlFreeHeap(RtlProcessHeap(), 0, targetName); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } for (;;) { b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS, targetName, MAX_PATH*sizeof(WCHAR), volumePaths, len, &bytes, NULL); if (b) { break; } if (GetLastError() != ERROR_MORE_DATA) { RtlFreeHeap(RtlProcessHeap(), 0, volumePaths); RtlFreeHeap(RtlProcessHeap(), 0, targetName); return FALSE; } len = sizeof(MOUNTMGR_VOLUME_PATHS) + volumePaths->MultiSzLength; RtlFreeHeap(RtlProcessHeap(), 0, volumePaths); volumePaths = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), len); if (!volumePaths) { CloseHandle(h); RtlFreeHeap(RtlProcessHeap(), 0, targetName); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } } CloseHandle(h); RtlFreeHeap(RtlProcessHeap(), 0, targetName); n = 0; for (i = 0, j = 0; i < cchBufferLength && j < volumePaths->MultiSzLength/sizeof(WCHAR) - 1; i++, j++) { if (!volumePaths->MultiSz[j]) { n++; lpszVolumePathNames[i++] = '\\'; if (i == cchBufferLength) { break; } } lpszVolumePathNames[i] = volumePaths->MultiSz[j]; } for (; j < volumePaths->MultiSzLength/sizeof(WCHAR) - 1; j++) { if (!volumePaths->MultiSz[j]) { n++; } } if (i < cchBufferLength) { b = TRUE; lpszVolumePathNames[i++] = 0; if (lpcchReturnLength) { *lpcchReturnLength = i; } } else { b = FALSE; SetLastError(ERROR_MORE_DATA); if (lpcchReturnLength) { *lpcchReturnLength = volumePaths->MultiSzLength/sizeof(WCHAR) + n; } } RtlFreeHeap(RtlProcessHeap(), 0, volumePaths); return b; }