mirror of
https://github.com/reactos/reactos.git
synced 2024-08-23 19:58:25 +00:00
688 lines
21 KiB
C
688 lines
21 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS system libraries
|
|
* FILE: dll/win32/kernel32/client/file/mntpoint.c
|
|
* PURPOSE: File volume mount point functions
|
|
* PROGRAMMER: Pierre Schweitzer (pierre@reactos.org)
|
|
*/
|
|
|
|
#include <k32.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
static BOOL
|
|
GetVolumeNameForRoot(IN LPCWSTR lpszRootPath,
|
|
OUT LPWSTR lpszVolumeName,
|
|
IN DWORD cchBufferLength)
|
|
{
|
|
BOOL Ret;
|
|
NTSTATUS Status;
|
|
PWSTR FoundVolume;
|
|
DWORD BytesReturned;
|
|
UNICODE_STRING NtPathName;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PMOUNTMGR_MOUNT_POINT MountPoint;
|
|
ULONG CurrentMntPt, FoundVolumeLen;
|
|
PMOUNTMGR_MOUNT_POINTS MountPoints;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE VolumeHandle, MountMgrHandle;
|
|
struct
|
|
{
|
|
MOUNTDEV_NAME;
|
|
WCHAR Buffer[MAX_PATH];
|
|
} MountDevName;
|
|
|
|
/* It makes no sense on a non-local drive */
|
|
if (GetDriveTypeW(lpszRootPath) == DRIVE_REMOTE)
|
|
{
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get the NT path */
|
|
if (!RtlDosPathNameToNtPathName_U(lpszRootPath, &NtPathName, NULL, NULL))
|
|
{
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
|
|
/* If it's a root path - likely - drop backslash to open volume */
|
|
if (NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] == L'\\')
|
|
{
|
|
NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] = UNICODE_NULL;
|
|
NtPathName.Length -= sizeof(WCHAR);
|
|
}
|
|
|
|
/* If that's a DOS volume, upper case the letter */
|
|
if (NtPathName.Length >= 2 * sizeof(WCHAR))
|
|
{
|
|
if (NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] == L':')
|
|
{
|
|
NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 2] = _toupper(NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 2]);
|
|
}
|
|
}
|
|
|
|
/* Attempt to open the volume */
|
|
InitializeObjectAttributes(&ObjectAttributes, &NtPathName,
|
|
OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE | FILE_READ_ATTRIBUTES,
|
|
&ObjectAttributes, &IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_ALERT);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Query the device name - that's what we'll translate */
|
|
if (!DeviceIoControl(VolumeHandle, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL,
|
|
0, &MountDevName, sizeof(MountDevName), &BytesReturned,
|
|
NULL))
|
|
{
|
|
NtClose(VolumeHandle);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
return FALSE;
|
|
}
|
|
|
|
/* No longer need the volume */
|
|
NtClose(VolumeHandle);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
|
|
/* We'll keep the device name for later usage */
|
|
NtPathName.Length = MountDevName.NameLength;
|
|
NtPathName.MaximumLength = MountDevName.NameLength + sizeof(UNICODE_NULL);
|
|
NtPathName.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NtPathName.MaximumLength);
|
|
if (NtPathName.Buffer == NULL)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlCopyMemory(NtPathName.Buffer, MountDevName.Name, NtPathName.Length);
|
|
NtPathName.Buffer[NtPathName.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
/* Allocate the structure for querying the mount mgr */
|
|
MountPoint = RtlAllocateHeap(RtlGetProcessHeap(), 0,
|
|
NtPathName.Length + sizeof(MOUNTMGR_MOUNT_POINT));
|
|
if (MountPoint == NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* 0 everything, we provide a device name */
|
|
RtlZeroMemory(MountPoint, sizeof(MOUNTMGR_MOUNT_POINT));
|
|
MountPoint->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
|
|
MountPoint->DeviceNameLength = NtPathName.Length;
|
|
RtlCopyMemory((PVOID)((ULONG_PTR)MountPoint + sizeof(MOUNTMGR_MOUNT_POINT)), NtPathName.Buffer, NtPathName.Length);
|
|
|
|
/* Allocate a dummy output buffer to probe for size */
|
|
MountPoints = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(MOUNTMGR_MOUNT_POINTS));
|
|
if (MountPoints == NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Open a handle to the mount manager */
|
|
MountMgrHandle = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
|
|
INVALID_HANDLE_VALUE);
|
|
if (MountMgrHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Query the names associated to our device name */
|
|
Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_POINTS,
|
|
MountPoint, NtPathName.Length + sizeof(MOUNTMGR_MOUNT_POINT),
|
|
MountPoints, sizeof(MOUNTMGR_MOUNT_POINTS), &BytesReturned,
|
|
NULL);
|
|
/* As long as the buffer is too small, keep looping */
|
|
while (!Ret && GetLastError() == ERROR_MORE_DATA)
|
|
{
|
|
ULONG BufferSize;
|
|
|
|
/* Get the size we've to allocate */
|
|
BufferSize = MountPoints->Size;
|
|
/* Reallocate the buffer with enough room */
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
|
|
MountPoints = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferSize);
|
|
if (MountPoints == NULL)
|
|
{
|
|
CloseHandle(MountMgrHandle);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Reissue the request, it should work now! */
|
|
Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_POINTS,
|
|
MountPoint, NtPathName.Length + sizeof(MOUNTMGR_MOUNT_POINT),
|
|
MountPoints, BufferSize, &BytesReturned, NULL);
|
|
}
|
|
|
|
/* We're done, no longer need the mount manager */
|
|
CloseHandle(MountMgrHandle);
|
|
/* Nor our input buffer */
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
|
|
|
|
/* If the mount manager failed, just quit */
|
|
if (!Ret)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
CurrentMntPt = 0;
|
|
/* If there were no associated mount points, we'll return the device name */
|
|
if (MountPoints->NumberOfMountPoints == 0)
|
|
{
|
|
FoundVolume = NtPathName.Buffer;
|
|
FoundVolumeLen = NtPathName.Length;
|
|
}
|
|
/* Otherwise, find one which is matching */
|
|
else
|
|
{
|
|
for (; CurrentMntPt < MountPoints->NumberOfMountPoints; ++CurrentMntPt)
|
|
{
|
|
UNICODE_STRING SymbolicLink;
|
|
|
|
/* Make a string of it, to easy the checks */
|
|
SymbolicLink.Length = MountPoints->MountPoints[CurrentMntPt].SymbolicLinkNameLength;
|
|
SymbolicLink.MaximumLength = SymbolicLink.Length;
|
|
SymbolicLink.Buffer = (PVOID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[CurrentMntPt].SymbolicLinkNameOffset);
|
|
/* If that's a NT volume name (GUID form), keep it! */
|
|
if (MOUNTMGR_IS_NT_VOLUME_NAME(&SymbolicLink))
|
|
{
|
|
FoundVolume = SymbolicLink.Buffer;
|
|
FoundVolumeLen = SymbolicLink.Length;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We couldn't find anything matching, return an error */
|
|
if (CurrentMntPt == MountPoints->NumberOfMountPoints)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
/* We found a matching volume, have we enough memory to return it? */
|
|
if (cchBufferLength * sizeof(WCHAR) < FoundVolumeLen + 2 * sizeof(WCHAR))
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
|
|
SetLastError(ERROR_FILENAME_EXCED_RANGE);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Copy it back! */
|
|
RtlCopyMemory(lpszVolumeName, FoundVolume, FoundVolumeLen);
|
|
/* Make it compliant */
|
|
lpszVolumeName[1] = L'\\';
|
|
/* And transform it as root path */
|
|
lpszVolumeName[FoundVolumeLen / sizeof(WCHAR)] = L'\\';
|
|
lpszVolumeName[FoundVolumeLen / sizeof(WCHAR) + 1] = UNICODE_NULL;
|
|
|
|
/* We're done! */
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
BasepGetVolumeNameFromReparsePoint(IN LPCWSTR lpszMountPoint,
|
|
OUT LPWSTR lpszVolumeName,
|
|
IN DWORD cchBufferLength,
|
|
OUT LPBOOL IsAMountPoint)
|
|
{
|
|
WCHAR Old;
|
|
DWORD BytesReturned;
|
|
HANDLE ReparseHandle;
|
|
UNICODE_STRING SubstituteName;
|
|
PREPARSE_DATA_BUFFER ReparseBuffer;
|
|
|
|
/* Try to open the reparse point */
|
|
ReparseHandle = CreateFileW(lpszMountPoint, 0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
|
|
INVALID_HANDLE_VALUE);
|
|
/* It failed! */
|
|
if (ReparseHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
/* Report it's not a mount point (it's not a reparse point) */
|
|
if (IsAMountPoint != NULL)
|
|
{
|
|
*IsAMountPoint = FALSE;
|
|
}
|
|
|
|
/* And zero output */
|
|
if (lpszVolumeName != NULL && cchBufferLength >= 1)
|
|
{
|
|
lpszVolumeName[0] = UNICODE_NULL;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* This is a mount point! */
|
|
if (IsAMountPoint != NULL)
|
|
{
|
|
*IsAMountPoint = TRUE;
|
|
}
|
|
|
|
/* Prepare a buffer big enough to read its data */
|
|
ReparseBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
|
if (ReparseBuffer == NULL)
|
|
{
|
|
CloseHandle(ReparseHandle);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
/* Zero output */
|
|
if (lpszVolumeName != NULL && cchBufferLength >= 1)
|
|
{
|
|
lpszVolumeName[0] = UNICODE_NULL;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Dump the reparse point data */
|
|
if (!DeviceIoControl(ReparseHandle, FSCTL_GET_REPARSE_POINT, NULL, 0,
|
|
ReparseBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &BytesReturned,
|
|
NULL))
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer);
|
|
CloseHandle(ReparseHandle);
|
|
|
|
/* Zero output */
|
|
if (lpszVolumeName != NULL && cchBufferLength >= 1)
|
|
{
|
|
lpszVolumeName[0] = UNICODE_NULL;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* We no longer need the reparse point */
|
|
CloseHandle(ReparseHandle);
|
|
|
|
/* We only handle mount points */
|
|
if (ReparseBuffer->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer);
|
|
|
|
/* Zero output */
|
|
if (lpszVolumeName != NULL && cchBufferLength >= 1)
|
|
{
|
|
lpszVolumeName[0] = UNICODE_NULL;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Do we have enough room for copying substitue name? */
|
|
if ((ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL)) > cchBufferLength * sizeof(WCHAR))
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer);
|
|
SetLastError(ERROR_FILENAME_EXCED_RANGE);
|
|
|
|
/* Zero output */
|
|
if (lpszVolumeName != NULL && cchBufferLength >= 1)
|
|
{
|
|
lpszVolumeName[0] = UNICODE_NULL;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Copy the link target */
|
|
RtlCopyMemory(lpszVolumeName,
|
|
&ReparseBuffer->MountPointReparseBuffer.PathBuffer[ReparseBuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
|
|
ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength);
|
|
/* Make it DOS valid */
|
|
Old = lpszVolumeName[1];
|
|
/* We want a root path */
|
|
lpszVolumeName[1] = L'\\';
|
|
/* And null terminate obviously */
|
|
lpszVolumeName[ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
/* Make it a string to easily check it */
|
|
SubstituteName.Length = ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength;
|
|
SubstituteName.MaximumLength = SubstituteName.Length;
|
|
SubstituteName.Buffer = lpszVolumeName;
|
|
|
|
/* No longer need the data? */
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer);
|
|
|
|
/* Is that a dos volume name with backslash? */
|
|
if (MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&SubstituteName))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/* No, so restore previous name and return to the caller */
|
|
lpszVolumeName[1] = Old;
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
BasepGetVolumeNameForVolumeMountPoint(IN LPCWSTR lpszMountPoint,
|
|
OUT LPWSTR lpszVolumeName,
|
|
IN DWORD cchBufferLength,
|
|
OUT LPBOOL IsAMountPoint)
|
|
{
|
|
BOOL Ret;
|
|
UNICODE_STRING MountPoint;
|
|
|
|
/* Assume it's a mount point (likely for non reparse points) */
|
|
if (IsAMountPoint != NULL)
|
|
{
|
|
*IsAMountPoint = 1;
|
|
}
|
|
|
|
/* Make a string with the mount point name */
|
|
RtlInitUnicodeString(&MountPoint, lpszMountPoint);
|
|
/* Not a root path? */
|
|
if (MountPoint.Buffer[(MountPoint.Length / sizeof(WCHAR)) - 1] != L'\\')
|
|
{
|
|
BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
|
|
/* Zero output */
|
|
if (lpszVolumeName != NULL && cchBufferLength >= 1)
|
|
{
|
|
lpszVolumeName[0] = UNICODE_NULL;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Does it look like <letter>:\? */
|
|
if (MountPoint.Length == 3 * sizeof(WCHAR))
|
|
{
|
|
/* Try to get volume name for root path */
|
|
Ret = GetVolumeNameForRoot(lpszMountPoint, lpszVolumeName, cchBufferLength);
|
|
/* It failed? */
|
|
if (!Ret)
|
|
{
|
|
/* If wasn't a drive letter, so maybe a reparse point? */
|
|
if (MountPoint.Buffer[1] != ':')
|
|
{
|
|
Ret = BasepGetVolumeNameFromReparsePoint(lpszMountPoint, lpszVolumeName, cchBufferLength, IsAMountPoint);
|
|
}
|
|
/* It was, so zero output */
|
|
else if (lpszVolumeName != NULL && cchBufferLength >= 1)
|
|
{
|
|
lpszVolumeName[0] = UNICODE_NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Try to get volume name for root path */
|
|
Ret = GetVolumeNameForRoot(lpszMountPoint, lpszVolumeName, cchBufferLength);
|
|
/* It failed? */
|
|
if (!Ret)
|
|
{
|
|
/* It was a DOS volume as UNC name, so fail and zero output */
|
|
if (MountPoint.Length == 14 && MountPoint.Buffer[0] == '\\' && MountPoint.Buffer[1] == '\\' &&
|
|
(MountPoint.Buffer[2] == '.' || MountPoint.Buffer[2] == '?') && MountPoint.Buffer[3] == L'\\' &&
|
|
MountPoint.Buffer[5] == ':')
|
|
{
|
|
if (lpszVolumeName != NULL && cchBufferLength >= 1)
|
|
{
|
|
lpszVolumeName[0] = UNICODE_NULL;
|
|
}
|
|
}
|
|
/* Maybe it's a reparse point? */
|
|
else
|
|
{
|
|
Ret = BasepGetVolumeNameFromReparsePoint(lpszMountPoint, lpszVolumeName, cchBufferLength, IsAMountPoint);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
/**
|
|
* @name GetVolumeNameForVolumeMountPointW
|
|
* @implemented
|
|
*
|
|
* Return an unique volume name for a drive root or mount point.
|
|
*
|
|
* @param VolumeMountPoint
|
|
* Pointer to string that contains either root drive name or
|
|
* mount point name.
|
|
* @param VolumeName
|
|
* Pointer to buffer that is filled with resulting unique
|
|
* volume name on success.
|
|
* @param VolumeNameLength
|
|
* Size of VolumeName buffer in TCHARs.
|
|
*
|
|
* @return
|
|
* TRUE when the function succeeds and the VolumeName buffer is filled,
|
|
* FALSE otherwise.
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetVolumeNameForVolumeMountPointW(IN LPCWSTR VolumeMountPoint,
|
|
OUT LPWSTR VolumeName,
|
|
IN DWORD VolumeNameLength)
|
|
{
|
|
BOOL Ret;
|
|
|
|
/* Just query our internal function */
|
|
Ret = BasepGetVolumeNameForVolumeMountPoint(VolumeMountPoint, VolumeName,
|
|
VolumeNameLength, NULL);
|
|
if (!Ret && VolumeName != NULL && VolumeNameLength >= 1)
|
|
{
|
|
VolumeName[0] = UNICODE_NULL;
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetVolumeNameForVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint,
|
|
IN LPSTR lpszVolumeName,
|
|
IN DWORD cchBufferLength)
|
|
{
|
|
BOOL Ret;
|
|
ANSI_STRING VolumeName;
|
|
UNICODE_STRING VolumeNameU;
|
|
PUNICODE_STRING VolumeMountPointU;
|
|
|
|
/* Convert mount point to unicode */
|
|
VolumeMountPointU = Basep8BitStringToStaticUnicodeString(lpszVolumeMountPoint);
|
|
if (VolumeMountPointU == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Initialize the strings we'll use for convention */
|
|
VolumeName.Buffer = lpszVolumeName;
|
|
VolumeName.Length = 0;
|
|
VolumeName.MaximumLength = cchBufferLength - 1;
|
|
|
|
VolumeNameU.Length = 0;
|
|
VolumeNameU.MaximumLength = (cchBufferLength - 1) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
|
|
/* Allocate a buffer big enough to contain the returned name */
|
|
VolumeNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeNameU.MaximumLength);
|
|
if (VolumeNameU.Buffer == NULL)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Query -W */
|
|
Ret = GetVolumeNameForVolumeMountPointW(VolumeMountPointU->Buffer, VolumeNameU.Buffer, cchBufferLength);
|
|
/* If it succeed, perform -A conversion */
|
|
if (Ret)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
/* Reinit our string for length */
|
|
RtlInitUnicodeString(&VolumeNameU, VolumeNameU.Buffer);
|
|
/* Convert to ANSI */
|
|
Status = RtlUnicodeStringToAnsiString(&VolumeName, &VolumeNameU, FALSE);
|
|
/* If conversion failed, force failure, otherwise, just null terminate */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
Ret = FALSE;
|
|
BaseSetLastNTError(Status);
|
|
}
|
|
else
|
|
{
|
|
VolumeName.Buffer[VolumeName.Length] = ANSI_NULL;
|
|
}
|
|
}
|
|
|
|
/* Internal buffer no longer needed */
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameU.Buffer);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
SetVolumeMountPointW(IN LPCWSTR lpszVolumeMountPoint,
|
|
IN LPCWSTR lpszVolumeName)
|
|
{
|
|
STUB;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
SetVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint,
|
|
IN LPCSTR lpszVolumeName)
|
|
{
|
|
STUB;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
DeleteVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint)
|
|
{
|
|
STUB;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
DeleteVolumeMountPointW(IN LPCWSTR lpszVolumeMountPoint)
|
|
{
|
|
STUB;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
HANDLE
|
|
WINAPI
|
|
FindFirstVolumeMountPointW(IN LPCWSTR lpszRootPathName,
|
|
IN LPWSTR lpszVolumeMountPoint,
|
|
IN DWORD cchBufferLength)
|
|
{
|
|
STUB;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
HANDLE
|
|
WINAPI
|
|
FindFirstVolumeMountPointA(IN LPCSTR lpszRootPathName,
|
|
IN LPSTR lpszVolumeMountPoint,
|
|
IN DWORD cchBufferLength)
|
|
{
|
|
STUB;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
FindNextVolumeMountPointA(IN HANDLE hFindVolumeMountPoint,
|
|
IN LPSTR lpszVolumeMountPoint,
|
|
DWORD cchBufferLength)
|
|
{
|
|
STUB;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
FindNextVolumeMountPointW(IN HANDLE hFindVolumeMountPoint,
|
|
IN LPWSTR lpszVolumeMountPoint,
|
|
DWORD cchBufferLength)
|
|
{
|
|
STUB;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
FindVolumeMountPointClose(IN HANDLE hFindVolumeMountPoint)
|
|
{
|
|
STUB;
|
|
return 0;
|
|
}
|
|
|
|
/* EOF */
|