mirror of
https://github.com/reactos/reactos.git
synced 2024-10-30 11:35:58 +00:00
1423 lines
43 KiB
C
1423 lines
43 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS system libraries
|
|
* FILE: dll/win32/kernel32/client/file/volume.c
|
|
* PURPOSE: File volume functions
|
|
* PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
|
|
* Erik Bos, Alexandre Julliard :
|
|
* GetLogicalDriveStringsA,
|
|
* GetLogicalDriveStringsW, GetLogicalDrives
|
|
* Pierre Schweitzer (pierre@reactos.org)
|
|
* UPDATE HISTORY:
|
|
* Created 01/11/98
|
|
*/
|
|
//WINE copyright notice:
|
|
/*
|
|
* DOS drives handling functions
|
|
*
|
|
* Copyright 1993 Erik Bos
|
|
* Copyright 1996 Alexandre Julliard
|
|
*/
|
|
|
|
#include <k32.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetVolumeInformationA(IN LPCSTR lpRootPathName,
|
|
IN LPSTR lpVolumeNameBuffer,
|
|
IN DWORD nVolumeNameSize,
|
|
OUT LPDWORD lpVolumeSerialNumber OPTIONAL,
|
|
OUT LPDWORD lpMaximumComponentLength OPTIONAL,
|
|
OUT LPDWORD lpFileSystemFlags OPTIONAL,
|
|
OUT LPSTR lpFileSystemNameBuffer OPTIONAL,
|
|
IN DWORD nFileSystemNameSize)
|
|
{
|
|
BOOL Ret;
|
|
NTSTATUS Status;
|
|
PUNICODE_STRING RootPathNameU;
|
|
ANSI_STRING VolumeName, FileSystemName;
|
|
UNICODE_STRING VolumeNameU, FileSystemNameU;
|
|
|
|
/* If no root path provided, default to \ */
|
|
if (lpRootPathName == NULL)
|
|
{
|
|
lpRootPathName = "\\";
|
|
}
|
|
|
|
/* Convert root path to unicode */
|
|
RootPathNameU = Basep8BitStringToStaticUnicodeString(lpRootPathName);
|
|
if (RootPathNameU == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Init all our STRINGS (U/A) */
|
|
VolumeNameU.Buffer = NULL;
|
|
VolumeNameU.MaximumLength = 0;
|
|
FileSystemNameU.Buffer = NULL;
|
|
FileSystemNameU.MaximumLength = 0;
|
|
|
|
VolumeName.Buffer = lpVolumeNameBuffer;
|
|
VolumeName.MaximumLength = nVolumeNameSize + 1;
|
|
FileSystemName.Buffer = lpFileSystemNameBuffer;
|
|
FileSystemName.MaximumLength = nFileSystemNameSize + 1;
|
|
|
|
/* Assume failure for now */
|
|
Ret = FALSE;
|
|
|
|
/* If caller wants volume name, allocate a buffer to receive it */
|
|
if (lpVolumeNameBuffer != NULL)
|
|
{
|
|
VolumeNameU.MaximumLength = sizeof(WCHAR) * (nVolumeNameSize + 1);
|
|
VolumeNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0,
|
|
VolumeNameU.MaximumLength);
|
|
if (VolumeNameU.Buffer == NULL)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto CleanAndQuit;
|
|
}
|
|
}
|
|
|
|
/* If caller wants file system name, allocate a buffer to receive it */
|
|
if (lpFileSystemNameBuffer != NULL)
|
|
{
|
|
FileSystemNameU.MaximumLength = sizeof(WCHAR) * (nVolumeNameSize + 1);
|
|
FileSystemNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0,
|
|
FileSystemNameU.MaximumLength);
|
|
if (FileSystemNameU.Buffer == NULL)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto CleanAndQuit;
|
|
}
|
|
}
|
|
|
|
/* Call W */
|
|
Ret = GetVolumeInformationW(RootPathNameU->Buffer, VolumeNameU.Buffer,
|
|
nVolumeNameSize, lpVolumeSerialNumber,
|
|
lpMaximumComponentLength, lpFileSystemFlags,
|
|
FileSystemNameU.Buffer, nFileSystemNameSize);
|
|
/* If it succeed, convert back to ANSI */
|
|
if (Ret)
|
|
{
|
|
if (lpVolumeNameBuffer != NULL)
|
|
{
|
|
RtlInitUnicodeString(&VolumeNameU, VolumeNameU.Buffer);
|
|
Status = RtlUnicodeStringToAnsiString(&VolumeName, &VolumeNameU, FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
BaseSetLastNTError(Status);
|
|
Ret = FALSE;
|
|
|
|
goto CleanAndQuit;
|
|
}
|
|
}
|
|
|
|
if (lpFileSystemNameBuffer != NULL)
|
|
{
|
|
RtlInitUnicodeString(&FileSystemNameU, FileSystemNameU.Buffer);
|
|
Status = RtlUnicodeStringToAnsiString(&FileSystemName, &FileSystemNameU, FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
BaseSetLastNTError(Status);
|
|
Ret = FALSE;
|
|
|
|
goto CleanAndQuit;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Clean and quit */
|
|
CleanAndQuit:
|
|
if (VolumeNameU.Buffer != NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameU.Buffer);
|
|
}
|
|
|
|
if (FileSystemNameU.Buffer != NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, FileSystemNameU.Buffer);
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
static BOOL
|
|
IsThisARootDirectory(IN HANDLE VolumeHandle,
|
|
IN PUNICODE_STRING NtPathName)
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
struct
|
|
{
|
|
FILE_NAME_INFORMATION;
|
|
WCHAR Buffer[MAX_PATH];
|
|
} FileNameInfo;
|
|
|
|
/* If we have a handle, query the name */
|
|
if (VolumeHandle)
|
|
{
|
|
Status = NtQueryInformationFile(VolumeHandle, &IoStatusBlock, &FileNameInfo, sizeof(FileNameInfo), FileNameInformation);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check we properly end with a \ */
|
|
if (FileNameInfo.FileName[FileNameInfo.FileNameLength / sizeof(WCHAR) - 1] != L'\\')
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* If we have a path */
|
|
if (NtPathName != NULL)
|
|
{
|
|
HANDLE LinkHandle;
|
|
WCHAR Buffer[512];
|
|
ULONG ReturnedLength;
|
|
UNICODE_STRING LinkTarget;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
|
|
NtPathName->Length -= sizeof(WCHAR);
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes, NtPathName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL, NULL);
|
|
|
|
/* Try to see whether that's a symbolic name */
|
|
Status = NtOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes);
|
|
NtPathName->Length += sizeof(WCHAR);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* If so, query the target */
|
|
LinkTarget.Buffer = Buffer;
|
|
LinkTarget.Length = 0;
|
|
LinkTarget.MaximumLength = sizeof(Buffer);
|
|
|
|
Status = NtQuerySymbolicLinkObject(LinkHandle, &LinkTarget, &ReturnedLength);
|
|
NtClose(LinkHandle);
|
|
/* A root directory (NtName) is a symbolic link */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetVolumeInformationW(IN LPCWSTR lpRootPathName,
|
|
IN LPWSTR lpVolumeNameBuffer,
|
|
IN DWORD nVolumeNameSize,
|
|
OUT LPDWORD lpVolumeSerialNumber OPTIONAL,
|
|
OUT LPDWORD lpMaximumComponentLength OPTIONAL,
|
|
OUT LPDWORD lpFileSystemFlags OPTIONAL,
|
|
OUT LPWSTR lpFileSystemNameBuffer OPTIONAL,
|
|
IN DWORD nFileSystemNameSize)
|
|
{
|
|
BOOL Ret;
|
|
NTSTATUS Status;
|
|
HANDLE VolumeHandle;
|
|
LPCWSTR RootPathName;
|
|
UNICODE_STRING NtPathName;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PFILE_FS_VOLUME_INFORMATION VolumeInfo;
|
|
PFILE_FS_ATTRIBUTE_INFORMATION VolumeAttr;
|
|
ULONG OldMode, VolumeInfoSize, VolumeAttrSize;
|
|
|
|
/* If no root path provided, default to \ */
|
|
if (lpRootPathName == NULL)
|
|
{
|
|
RootPathName = L"\\";
|
|
}
|
|
else
|
|
{
|
|
RootPathName = lpRootPathName;
|
|
}
|
|
|
|
/* Convert to NT name */
|
|
if (!RtlDosPathNameToNtPathName_U(RootPathName, &NtPathName, NULL, NULL))
|
|
{
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check we really end with a backslash */
|
|
if (NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] != L'\\')
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Try to open the received path */
|
|
InitializeObjectAttributes(&ObjectAttributes, &NtPathName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL, NULL);
|
|
|
|
/* No errors to the user */
|
|
RtlSetThreadErrorMode(RTL_SEM_FAILCRITICALERRORS, &OldMode);
|
|
Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, 0, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT);
|
|
RtlSetThreadErrorMode(OldMode, NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check whether that's a root directory */
|
|
if (!IsThisARootDirectory(VolumeHandle, &NtPathName))
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
NtClose(VolumeHandle);
|
|
SetLastError(ERROR_DIR_NOT_ROOT);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
|
|
|
|
/* Assume we don't need to query FileFsVolumeInformation */
|
|
VolumeInfo = NULL;
|
|
/* If user wants volume name, allocate a buffer to query it */
|
|
if (lpVolumeNameBuffer != NULL)
|
|
{
|
|
VolumeInfoSize = nVolumeNameSize + sizeof(FILE_FS_VOLUME_INFORMATION);
|
|
}
|
|
/* If user just wants the serial number, allocate a dummy buffer */
|
|
else if (lpVolumeSerialNumber != NULL)
|
|
{
|
|
VolumeInfoSize = MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_VOLUME_INFORMATION);
|
|
}
|
|
/* Otherwise, nothing to query */
|
|
else
|
|
{
|
|
VolumeInfoSize = 0;
|
|
}
|
|
|
|
/* If we're to query, allocate a big enough buffer */
|
|
if (VolumeInfoSize != 0)
|
|
{
|
|
VolumeInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeInfoSize);
|
|
if (VolumeInfo == NULL)
|
|
{
|
|
NtClose(VolumeHandle);
|
|
BaseSetLastNTError(STATUS_NO_MEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Assume we don't need to query FileFsAttributeInformation */
|
|
VolumeAttr = NULL;
|
|
/* If user wants filesystem name, allocate a buffer to query it */
|
|
if (lpFileSystemNameBuffer != NULL)
|
|
{
|
|
VolumeAttrSize = nFileSystemNameSize + sizeof(FILE_FS_ATTRIBUTE_INFORMATION);
|
|
}
|
|
/* If user just wants max compo len or flags, allocate a dummy buffer */
|
|
else if (lpMaximumComponentLength != NULL || lpFileSystemFlags != NULL)
|
|
{
|
|
VolumeAttrSize = MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION);
|
|
}
|
|
else
|
|
{
|
|
VolumeAttrSize = 0;
|
|
}
|
|
|
|
/* If we're to query, allocate a big enough buffer */
|
|
if (VolumeAttrSize != 0)
|
|
{
|
|
VolumeAttr = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeAttrSize);
|
|
if (VolumeAttr == NULL)
|
|
{
|
|
if (VolumeInfo != NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeInfo);
|
|
}
|
|
|
|
NtClose(VolumeHandle);
|
|
BaseSetLastNTError(STATUS_NO_MEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Assume we'll fail */
|
|
Ret = FALSE;
|
|
|
|
/* If we're to query FileFsVolumeInformation, do it now! */
|
|
if (VolumeInfo != NULL)
|
|
{
|
|
Status = NtQueryVolumeInformationFile(VolumeHandle, &IoStatusBlock, VolumeInfo, VolumeInfoSize, FileFsVolumeInformation);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
BaseSetLastNTError(Status);
|
|
goto CleanAndQuit;
|
|
}
|
|
}
|
|
|
|
/* If we're to query FileFsAttributeInformation, do it now! */
|
|
if (VolumeAttr != NULL)
|
|
{
|
|
Status = NtQueryVolumeInformationFile(VolumeHandle, &IoStatusBlock, VolumeAttr, VolumeAttrSize, FileFsAttributeInformation);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
BaseSetLastNTError(Status);
|
|
goto CleanAndQuit;
|
|
}
|
|
}
|
|
|
|
/* If user wants volume name */
|
|
if (lpVolumeNameBuffer != NULL)
|
|
{
|
|
/* Check its buffer can hold it (+ 0) */
|
|
if (VolumeInfo->VolumeLabelLength >= nVolumeNameSize)
|
|
{
|
|
SetLastError(ERROR_BAD_LENGTH);
|
|
goto CleanAndQuit;
|
|
}
|
|
|
|
/* Copy and zero */
|
|
RtlCopyMemory(lpVolumeNameBuffer, VolumeInfo->VolumeLabel, VolumeInfo->VolumeLabelLength);
|
|
lpVolumeNameBuffer[VolumeInfo->VolumeLabelLength / sizeof(WCHAR)] = UNICODE_NULL;
|
|
}
|
|
|
|
/* If user wants wants serial number, return it */
|
|
if (lpVolumeSerialNumber != NULL)
|
|
{
|
|
*lpVolumeSerialNumber = VolumeInfo->VolumeSerialNumber;
|
|
}
|
|
|
|
/* If user wants filesystem name */
|
|
if (lpFileSystemNameBuffer != NULL)
|
|
{
|
|
/* Check its buffer can hold it (+ 0) */
|
|
if (VolumeAttr->FileSystemNameLength >= nFileSystemNameSize)
|
|
{
|
|
SetLastError(ERROR_BAD_LENGTH);
|
|
goto CleanAndQuit;
|
|
}
|
|
|
|
/* Copy and zero */
|
|
RtlCopyMemory(lpFileSystemNameBuffer, VolumeAttr->FileSystemName, VolumeAttr->FileSystemNameLength);
|
|
lpFileSystemNameBuffer[VolumeAttr->FileSystemNameLength / sizeof(WCHAR)] = UNICODE_NULL;
|
|
}
|
|
|
|
/* If user wants wants max compo len, return it */
|
|
if (lpMaximumComponentLength != NULL)
|
|
{
|
|
*lpMaximumComponentLength = VolumeAttr->MaximumComponentNameLength;
|
|
}
|
|
|
|
/* If user wants wants FS flags, return them */
|
|
if (lpFileSystemFlags != NULL)
|
|
{
|
|
*lpFileSystemFlags = VolumeAttr->FileSystemAttributes;
|
|
}
|
|
|
|
/* We did it! */
|
|
Ret = TRUE;
|
|
|
|
CleanAndQuit:
|
|
NtClose(VolumeHandle);
|
|
|
|
if (VolumeInfo != NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeInfo);
|
|
}
|
|
|
|
if (VolumeAttr != NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeAttr);
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
SetVolumeLabelA(IN LPCSTR lpRootPathName,
|
|
IN LPCSTR lpVolumeName OPTIONAL) /* NULL if deleting label */
|
|
{
|
|
BOOL Ret;
|
|
UNICODE_STRING VolumeNameU;
|
|
PUNICODE_STRING RootPathNameU;
|
|
|
|
if (lpRootPathName == NULL)
|
|
{
|
|
lpRootPathName = "\\";
|
|
}
|
|
|
|
RootPathNameU = Basep8BitStringToStaticUnicodeString(lpRootPathName);
|
|
if (RootPathNameU == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (lpVolumeName != NULL)
|
|
{
|
|
if (!Basep8BitStringToDynamicUnicodeString(&VolumeNameU, lpVolumeName))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VolumeNameU.Buffer = NULL;
|
|
}
|
|
|
|
Ret = SetVolumeLabelW(RootPathNameU->Buffer, VolumeNameU.Buffer);
|
|
RtlFreeUnicodeString(&VolumeNameU);
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
SetVolumeLabelW(IN LPCWSTR lpRootPathName,
|
|
IN LPCWSTR lpVolumeName OPTIONAL) /* NULL if deleting label */
|
|
{
|
|
BOOL Ret;
|
|
NTSTATUS Status;
|
|
PWSTR VolumeRoot;
|
|
HANDLE VolumeHandle;
|
|
WCHAR VolumeGuid[MAX_PATH];
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PFILE_FS_LABEL_INFORMATION FsLabelInfo;
|
|
UNICODE_STRING VolumeName, NtVolumeName;
|
|
|
|
/* If no root path provided, default to \ */
|
|
VolumeRoot = L"\\";
|
|
|
|
/* If user wants to set a label, make it a string */
|
|
if (lpVolumeName != NULL)
|
|
{
|
|
RtlInitUnicodeString(&VolumeName, lpVolumeName);
|
|
}
|
|
else
|
|
{
|
|
VolumeName.Length = 0;
|
|
VolumeName.MaximumLength = 0;
|
|
VolumeName.Buffer = NULL;
|
|
}
|
|
|
|
/* If we received a volume, try to get its GUID name */
|
|
if (lpRootPathName != NULL)
|
|
{
|
|
Ret = GetVolumeNameForVolumeMountPointW(lpRootPathName, VolumeGuid, MAX_PATH);
|
|
}
|
|
else
|
|
{
|
|
Ret = FALSE;
|
|
}
|
|
|
|
/* If we got the GUID name, use it */
|
|
if (Ret)
|
|
{
|
|
VolumeRoot = VolumeGuid;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, use the name provided by the caller */
|
|
if (lpRootPathName != NULL)
|
|
{
|
|
VolumeRoot = (PWSTR)lpRootPathName;
|
|
}
|
|
}
|
|
|
|
/* Convert to a NT path */
|
|
if (!RtlDosPathNameToNtPathName_U(VolumeRoot, &NtVolumeName, NULL, NULL))
|
|
{
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* Check we really end with a backslash */
|
|
if (NtVolumeName.Buffer[(NtVolumeName.Length / sizeof(WCHAR)) - 1] != L'\\')
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
|
|
BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Try to open the root directory */
|
|
InitializeObjectAttributes(&ObjectAttributes, &NtVolumeName,
|
|
OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE | FILE_WRITE_DATA,
|
|
&ObjectAttributes, &IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Validate it's really a root path */
|
|
if (!IsThisARootDirectory(VolumeHandle, NULL))
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
|
|
NtClose(VolumeHandle);
|
|
SetLastError(ERROR_DIR_NOT_ROOT);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Done */
|
|
NtClose(VolumeHandle);
|
|
|
|
/* Now, open the volume to perform the label change */
|
|
NtVolumeName.Length -= sizeof(WCHAR);
|
|
InitializeObjectAttributes(&ObjectAttributes, &NtVolumeName,
|
|
OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE | FILE_WRITE_DATA,
|
|
&ObjectAttributes, &IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Assume success */
|
|
Ret = TRUE;
|
|
|
|
/* Allocate a buffer that can hold new label and its size */
|
|
FsLabelInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(FILE_FS_LABEL_INFORMATION) + VolumeName.Length);
|
|
if (FsLabelInfo != NULL)
|
|
{
|
|
/* Copy name and set its size */
|
|
RtlCopyMemory(FsLabelInfo->VolumeLabel, VolumeName.Buffer, VolumeName.Length);
|
|
FsLabelInfo->VolumeLabelLength = VolumeName.Length;
|
|
|
|
/* And finally, set new label */
|
|
Status = NtSetVolumeInformationFile(VolumeHandle, &IoStatusBlock, FsLabelInfo, sizeof(FILE_FS_LABEL_INFORMATION) + VolumeName.Length, FileFsLabelInformation);
|
|
}
|
|
else
|
|
{
|
|
/* Allocation failed */
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* In case of failure, set status and mark failure */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
BaseSetLastNTError(Status);
|
|
Ret = FALSE;
|
|
}
|
|
|
|
/* We're done */
|
|
NtClose(VolumeHandle);
|
|
|
|
/* Free buffer if required */
|
|
if (FsLabelInfo != NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, FsLabelInfo);
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented (Wine 13 sep 2008)
|
|
*/
|
|
HANDLE
|
|
WINAPI
|
|
FindFirstVolumeW(IN LPWSTR volume,
|
|
IN DWORD len)
|
|
{
|
|
DWORD size = 1024;
|
|
DWORD br;
|
|
HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE );
|
|
if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
|
|
|
|
for (;;)
|
|
{
|
|
MOUNTMGR_MOUNT_POINT input;
|
|
MOUNTMGR_MOUNT_POINTS *output;
|
|
|
|
if (!(output = RtlAllocateHeap( RtlGetProcessHeap(), 0, size )))
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
break;
|
|
}
|
|
memset( &input, 0, sizeof(input) );
|
|
|
|
if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input),
|
|
output, size, &br, NULL ))
|
|
{
|
|
if (GetLastError() != ERROR_MORE_DATA) break;
|
|
size = output->Size;
|
|
RtlFreeHeap( RtlGetProcessHeap(), 0, output );
|
|
continue;
|
|
}
|
|
CloseHandle( mgr );
|
|
/* abuse the Size field to store the current index */
|
|
output->Size = 0;
|
|
if (!FindNextVolumeW( output, volume, len ))
|
|
{
|
|
RtlFreeHeap( RtlGetProcessHeap(), 0, output );
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
return (HANDLE)output;
|
|
}
|
|
CloseHandle( mgr );
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
/*
|
|
* @implemented (Wine 13 sep 2008)
|
|
*/
|
|
HANDLE
|
|
WINAPI
|
|
FindFirstVolumeA(IN LPSTR volume,
|
|
IN DWORD len)
|
|
{
|
|
WCHAR *buffer = NULL;
|
|
HANDLE handle;
|
|
|
|
buffer = RtlAllocateHeap( RtlGetProcessHeap(), 0, len * sizeof(WCHAR) );
|
|
|
|
if (!buffer)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
handle = FindFirstVolumeW( buffer, len );
|
|
|
|
if (handle != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
|
|
{
|
|
FindVolumeClose( handle );
|
|
handle = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
RtlFreeHeap( RtlGetProcessHeap(), 0, buffer );
|
|
return handle;
|
|
}
|
|
|
|
/*
|
|
* @implemented (Wine 13 sep 2008)
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
FindVolumeClose(IN HANDLE hFindVolume)
|
|
{
|
|
return RtlFreeHeap(RtlGetProcessHeap(), 0, hFindVolume);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetVolumePathNameA(IN LPCSTR lpszFileName,
|
|
IN LPSTR lpszVolumePathName,
|
|
IN DWORD cchBufferLength)
|
|
{
|
|
BOOL Ret;
|
|
PUNICODE_STRING FileNameU;
|
|
ANSI_STRING VolumePathName;
|
|
UNICODE_STRING VolumePathNameU;
|
|
|
|
/* Convert file name to unicode */
|
|
FileNameU = Basep8BitStringToStaticUnicodeString(lpszFileName);
|
|
if (FileNameU == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Initialize all the strings we'll need */
|
|
VolumePathName.Buffer = lpszVolumePathName;
|
|
VolumePathName.Length = 0;
|
|
VolumePathName.MaximumLength = cchBufferLength - 1;
|
|
|
|
VolumePathNameU.Length = 0;
|
|
VolumePathNameU.MaximumLength = (cchBufferLength - 1) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
|
|
/* Allocate a buffer for calling the -W */
|
|
VolumePathNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumePathNameU.MaximumLength);
|
|
if (VolumePathNameU.Buffer == NULL)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Call the -W implementation */
|
|
Ret = GetVolumePathNameW(FileNameU->Buffer, VolumePathNameU.Buffer, cchBufferLength);
|
|
/* If it succeed */
|
|
if (Ret)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
/* Convert back to ANSI */
|
|
RtlInitUnicodeString(&VolumePathNameU, VolumePathNameU.Buffer);
|
|
Status = RtlUnicodeStringToAnsiString(&VolumePathName, &VolumePathNameU, FALSE);
|
|
/* If conversion failed, just set error code and fail the rest */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
BaseSetLastNTError(Status);
|
|
Ret = FALSE;
|
|
}
|
|
/* Otherwise, null terminate the string (it's OK, we computed -1) */
|
|
else
|
|
{
|
|
VolumePathName.Buffer[VolumePathName.Length] = ANSI_NULL;
|
|
}
|
|
}
|
|
|
|
/* Free the buffer allocated for -W call */
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNameU.Buffer);
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetVolumePathNameW(IN LPCWSTR lpszFileName,
|
|
IN LPWSTR lpszVolumePathName,
|
|
IN DWORD cchBufferLength)
|
|
{
|
|
BOOL MountPoint;
|
|
DWORD FullPathLen;
|
|
WCHAR OldFilePart;
|
|
UNICODE_STRING FullPath;
|
|
PWSTR FullPathBuf, FilePart, VolumeNameBuf;
|
|
|
|
/* Probe for full path len */
|
|
FullPathLen = GetFullPathNameW(lpszFileName, 0, NULL, NULL);
|
|
if (FullPathLen == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Allocate a big enough buffer to receive it */
|
|
FullPathBuf = RtlAllocateHeap(RtlGetProcessHeap(), 0, (FullPathLen + 10) * sizeof(WCHAR));
|
|
if (FullPathBuf == NULL)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* And get full path name */
|
|
if (GetFullPathNameW(lpszFileName, FullPathLen + 10, FullPathBuf, &FilePart) == 0)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Make a string out of it */
|
|
RtlInitUnicodeString(&FullPath, FullPathBuf);
|
|
/* We will finish our string with '\', for ease of the parsing after */
|
|
if (FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\')
|
|
{
|
|
FullPath.Length += sizeof(WCHAR);
|
|
FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] = L'\\';
|
|
FullPath.Buffer[FullPath.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
}
|
|
|
|
/* Allocate a buffer big enough to receive our volume name */
|
|
VolumeNameBuf = RtlAllocateHeap(RtlGetProcessHeap(), 0, 0x2000 * sizeof(WCHAR));
|
|
if (VolumeNameBuf == NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* We don't care about file part: we added an extra backslash, so there's no
|
|
* file, we're back at the dir level.
|
|
* We'll recompute file part afterwards
|
|
*/
|
|
FilePart = NULL;
|
|
/* Keep track of the letter we could drop to shorten the string */
|
|
OldFilePart = UNICODE_NULL;
|
|
/* As long as querying volume name fails, keep looping */
|
|
while (!BasepGetVolumeNameForVolumeMountPoint(FullPath.Buffer, VolumeNameBuf, 0x2000u, &MountPoint))
|
|
{
|
|
USHORT LastSlash;
|
|
|
|
/* Not a mount point, but opening returning access denied? Assume it's one, just not
|
|
* a reparse backed one (classic mount point, a device)!
|
|
*/
|
|
if (!MountPoint && GetLastError() == ERROR_ACCESS_DENIED)
|
|
{
|
|
MountPoint = TRUE;
|
|
}
|
|
|
|
/* BasepGetVolumeNameForVolumeMountPoint failed, but returned a volume name.
|
|
* This can happen when we are given a reparse point where MountMgr could find associated
|
|
* volume name which is not a valid DOS volume
|
|
* A valid DOS name always starts with \\
|
|
*/
|
|
if (VolumeNameBuf[0] != UNICODE_NULL && (FullPath.Buffer[0] != L'\\' || FullPath.Buffer[1] != L'\\'))
|
|
{
|
|
CHAR RootPathName[4];
|
|
|
|
/* Construct a simple <letter>:\ string to get drive type */
|
|
RootPathName[0] = FullPath.Buffer[0];
|
|
RootPathName[1] = ':';
|
|
RootPathName[2] = '\\';
|
|
RootPathName[3] = ANSI_NULL;
|
|
|
|
/* If we weren't given a drive letter actually, or if that's not a remote drive
|
|
* Note: in this code path, we're recursive and stop fail loop
|
|
*/
|
|
if (FullPath.Buffer[1] != L':' || GetDriveTypeA(RootPathName) != DRIVE_REMOTE)
|
|
{
|
|
BOOL Ret;
|
|
|
|
/* We won't need the full path, we'll now work with the returned volume name */
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
|
|
/* If it wasn't an NT name which was returned */
|
|
if ((VolumeNameBuf[0] != L'\\') || (VolumeNameBuf[1] != L'?') ||
|
|
(VolumeNameBuf[2] != L'?') || (VolumeNameBuf[3] != L'\\'))
|
|
{
|
|
PWSTR GlobalPath;
|
|
UNICODE_STRING GlobalRoot;
|
|
|
|
/* Create a new name in the NT namespace (from Win32) */
|
|
RtlInitUnicodeString(&FullPath, VolumeNameBuf);
|
|
RtlInitUnicodeString(&GlobalRoot, L"\\\\?\\GLOBALROOT");
|
|
|
|
/* We allocate a buffer than can contain both the namespace and the volume name */
|
|
GlobalPath = RtlAllocateHeap(RtlGetProcessHeap(), 0, FullPath.Length + GlobalRoot.Length);
|
|
if (GlobalPath == NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameBuf);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Fill in the new query name */
|
|
RtlCopyMemory(GlobalPath, GlobalRoot.Buffer, GlobalRoot.Length);
|
|
RtlCopyMemory((PVOID)((ULONG_PTR)GlobalPath + GlobalRoot.Length), FullPath.Buffer, FullPath.Length);
|
|
GlobalPath[(FullPath.Length + GlobalRoot.Length) / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
/* Give it another try */
|
|
Ret = GetVolumePathNameW(GlobalPath, lpszVolumePathName, cchBufferLength);
|
|
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, GlobalPath);
|
|
}
|
|
else
|
|
{
|
|
/* If we don't have a drive letter in the Win32 name space \\.\<letter>: */
|
|
if ((VolumeNameBuf[4] != UNICODE_NULL) && (VolumeNameBuf[5] != L':'))
|
|
{
|
|
/* Shit our starting \\ */
|
|
RtlInitUnicodeString(&FullPath, VolumeNameBuf);
|
|
RtlMoveMemory(VolumeNameBuf, (PVOID)((ULONG_PTR)VolumeNameBuf + (2 * sizeof(WCHAR))), FullPath.Length - (3 * sizeof(WCHAR)));
|
|
}
|
|
/* Otherwise, just make sure we're double \ at the being to query again with the
|
|
* proper namespace
|
|
*/
|
|
else
|
|
{
|
|
VolumeNameBuf[1] = L'\\';
|
|
}
|
|
|
|
/* Give it another try */
|
|
Ret = GetVolumePathNameW(VolumeNameBuf, lpszVolumePathName, cchBufferLength);
|
|
}
|
|
|
|
/* And done! */
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameBuf);
|
|
return Ret;
|
|
}
|
|
}
|
|
|
|
/* No mount point but with a file part? Restore filepart and exit */
|
|
if (!MountPoint && FilePart != NULL)
|
|
{
|
|
FilePart[0] = OldFilePart;
|
|
RtlInitUnicodeString(&FullPath, FullPathBuf);
|
|
break;
|
|
}
|
|
|
|
/* We cannot go down the path any longer, too small */
|
|
if (FullPath.Length <= sizeof(WCHAR))
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* Prepare the next split */
|
|
LastSlash = (FullPath.Length / sizeof(WCHAR)) - 2;
|
|
if (FullPath.Length / sizeof(WCHAR) != 2)
|
|
{
|
|
do
|
|
{
|
|
if (FullPath.Buffer[LastSlash] == L'\\')
|
|
{
|
|
break;
|
|
}
|
|
|
|
--LastSlash;
|
|
} while (LastSlash != 0);
|
|
}
|
|
|
|
/* We couldn't split path, quit */
|
|
if (LastSlash == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* If that's a mount point, keep track of the directory name */
|
|
if (MountPoint)
|
|
{
|
|
FilePart = &FullPath.Buffer[LastSlash + 1];
|
|
OldFilePart = FilePart[0];
|
|
/* And null terminate the string */
|
|
FilePart[0] = UNICODE_NULL;
|
|
}
|
|
/* Otherwise, just null terminate the string */
|
|
else
|
|
{
|
|
FullPath.Buffer[LastSlash + 1] = UNICODE_NULL;
|
|
}
|
|
|
|
/* We went down a bit in the path, fix the string and retry */
|
|
RtlInitUnicodeString(&FullPath, FullPathBuf);
|
|
}
|
|
|
|
/* Once here, we'll return something from the full path buffer, so release
|
|
* output buffer
|
|
*/
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameBuf);
|
|
|
|
/* Not a mount point, bail out */
|
|
if (!MountPoint && FilePart == NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Make sure we have enough room to copy our volume */
|
|
if ((cchBufferLength * sizeof(WCHAR)) < FullPath.Length + sizeof(UNICODE_NULL))
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
|
|
SetLastError(ERROR_FILENAME_EXCED_RANGE);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Copy and null terminate */
|
|
RtlCopyMemory(lpszVolumePathName, FullPath.Buffer, FullPath.Length);
|
|
lpszVolumePathName[FullPath.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
|
|
|
|
/* Done! */
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
FindNextVolumeA(IN HANDLE handle,
|
|
IN LPSTR volume,
|
|
IN DWORD len)
|
|
{
|
|
WCHAR *buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
BOOL ret;
|
|
|
|
if (!buffer)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((ret = FindNextVolumeW( handle, buffer, len )))
|
|
{
|
|
if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE;
|
|
}
|
|
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, buffer);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
FindNextVolumeW(IN HANDLE handle,
|
|
IN LPWSTR volume,
|
|
IN DWORD len)
|
|
{
|
|
MOUNTMGR_MOUNT_POINTS *data = handle;
|
|
|
|
while (data->Size < data->NumberOfMountPoints)
|
|
{
|
|
static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',};
|
|
WCHAR *link = (WCHAR *)((char *)data + data->MountPoints[data->Size].SymbolicLinkNameOffset);
|
|
DWORD size = data->MountPoints[data->Size].SymbolicLinkNameLength;
|
|
data->Size++;
|
|
/* skip non-volumes */
|
|
if (size < sizeof(volumeW) || memcmp( link, volumeW, sizeof(volumeW) )) continue;
|
|
if (size + sizeof(WCHAR) >= len * sizeof(WCHAR))
|
|
{
|
|
SetLastError( ERROR_FILENAME_EXCED_RANGE );
|
|
return FALSE;
|
|
}
|
|
memcpy( volume, link, size );
|
|
volume[1] = '\\'; /* map \??\ to \\?\ */
|
|
volume[size / sizeof(WCHAR)] = '\\'; /* Windows appends a backslash */
|
|
volume[size / sizeof(WCHAR) + 1] = 0;
|
|
DPRINT( "returning entry %u %s\n", data->Size - 1, volume );
|
|
return TRUE;
|
|
}
|
|
SetLastError( ERROR_NO_MORE_FILES );
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetVolumePathNamesForVolumeNameA(IN LPCSTR lpszVolumeName,
|
|
IN LPSTR lpszVolumePathNames,
|
|
IN DWORD cchBufferLength,
|
|
OUT PDWORD lpcchReturnLength)
|
|
{
|
|
BOOL Ret;
|
|
NTSTATUS Status;
|
|
DWORD cchReturnLength;
|
|
ANSI_STRING VolumePathName;
|
|
PUNICODE_STRING VolumeNameU;
|
|
UNICODE_STRING VolumePathNamesU;
|
|
|
|
/* Convert volume name to unicode */
|
|
VolumeNameU = Basep8BitStringToStaticUnicodeString(lpszVolumeName);
|
|
if (VolumeNameU == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Initialize the strings we'll use later on */
|
|
VolumePathName.Length = 0;
|
|
VolumePathName.MaximumLength = cchBufferLength;
|
|
VolumePathName.Buffer = lpszVolumePathNames;
|
|
|
|
VolumePathNamesU.Length = 0;
|
|
VolumePathNamesU.MaximumLength = sizeof(WCHAR) * cchBufferLength;
|
|
/* If caller provided a non 0 sized string, allocate a buffer for our unicode string */
|
|
if (VolumePathNamesU.MaximumLength != 0)
|
|
{
|
|
VolumePathNamesU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.MaximumLength);
|
|
if (VolumePathNamesU.Buffer == NULL)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VolumePathNamesU.Buffer = NULL;
|
|
}
|
|
|
|
/* Call the -W implementation */
|
|
Ret = GetVolumePathNamesForVolumeNameW(VolumeNameU->Buffer, VolumePathNamesU.Buffer,
|
|
cchBufferLength, &cchReturnLength);
|
|
/* Call succeed, we'll return the total length */
|
|
if (Ret)
|
|
{
|
|
VolumePathNamesU.Length = sizeof(WCHAR) * cchReturnLength;
|
|
}
|
|
else
|
|
{
|
|
/* Else, if we fail for anything else than too small buffer, quit */
|
|
if (GetLastError() != ERROR_MORE_DATA)
|
|
{
|
|
if (VolumePathNamesU.Buffer != NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Otherwise, we'll just copy as much as we can */
|
|
VolumePathNamesU.Length = sizeof(WCHAR) * cchBufferLength;
|
|
}
|
|
|
|
/* Convert our output string back to ANSI */
|
|
Status = RtlUnicodeStringToAnsiString(&VolumePathName, &VolumePathNamesU, FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
BaseSetLastNTError(Status);
|
|
|
|
if (VolumePathNamesU.Buffer != NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* If caller wants return length, two cases... */
|
|
if (lpcchReturnLength != NULL)
|
|
{
|
|
/* We succeed: return the copied length */
|
|
if (Ret)
|
|
{
|
|
*lpcchReturnLength = VolumePathName.Length;
|
|
}
|
|
/* We failed, return the size we would have loved having! */
|
|
else
|
|
{
|
|
*lpcchReturnLength = sizeof(WCHAR) * cchReturnLength;
|
|
}
|
|
}
|
|
|
|
/* Release our buffer if allocated */
|
|
if (VolumePathNamesU.Buffer != NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer);
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetVolumePathNamesForVolumeNameW(IN LPCWSTR lpszVolumeName,
|
|
IN LPWSTR lpszVolumePathNames,
|
|
IN DWORD cchBufferLength,
|
|
OUT PDWORD lpcchReturnLength)
|
|
{
|
|
BOOL Ret;
|
|
PWSTR MultiSz;
|
|
DWORD BytesReturned;
|
|
HANDLE MountMgrHandle;
|
|
UNICODE_STRING VolumeName;
|
|
PMOUNTMGR_TARGET_NAME TargetName;
|
|
PMOUNTMGR_VOLUME_PATHS VolumePaths;
|
|
ULONG BufferSize, CharsInMgr, CharsInOutput, Paths;
|
|
|
|
/* First look that our volume name looks somehow correct */
|
|
RtlInitUnicodeString(&VolumeName, lpszVolumeName);
|
|
if (VolumeName.Buffer[(VolumeName.Length / sizeof(WCHAR)) - 1] != L'\\')
|
|
{
|
|
BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Validate it's a DOS volume name finishing with a backslash */
|
|
if (!MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&VolumeName))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Allocate an input MOUNTMGR_TARGET_NAME */
|
|
TargetName = RtlAllocateHeap(RtlGetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT));
|
|
if (TargetName == NULL)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* And fill it */
|
|
RtlZeroMemory(TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT));
|
|
TargetName->DeviceNameLength = VolumeName.Length - sizeof(WCHAR);
|
|
RtlCopyMemory(TargetName->DeviceName, VolumeName.Buffer, TargetName->DeviceNameLength);
|
|
TargetName->DeviceName[1] = L'?';
|
|
|
|
/* Open 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, TargetName);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Allocate an initial output buffer, just to get length */
|
|
VolumePaths = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(MOUNTMGR_VOLUME_PATHS));
|
|
if (VolumePaths == NULL)
|
|
{
|
|
CloseHandle(MountMgrHandle);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Query the paths */
|
|
Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS,
|
|
TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT),
|
|
VolumePaths, sizeof(MOUNTMGR_VOLUME_PATHS), &BytesReturned,
|
|
NULL);
|
|
/* Loop until we can query everything */
|
|
while (!Ret)
|
|
{
|
|
/* If failed for another reason than too small buffer, fail */
|
|
if (GetLastError() != ERROR_MORE_DATA)
|
|
{
|
|
CloseHandle(MountMgrHandle);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get the required length */
|
|
BufferSize = VolumePaths->MultiSzLength + sizeof(MOUNTMGR_VOLUME_PATHS);
|
|
|
|
/* And reallocate our output buffer (big enough this time) */
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths);
|
|
VolumePaths = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferSize);
|
|
if (VolumePaths == NULL)
|
|
{
|
|
CloseHandle(MountMgrHandle);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Query again the mount mgr */
|
|
Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS,
|
|
TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT),
|
|
VolumePaths, BufferSize, &BytesReturned, NULL);
|
|
}
|
|
|
|
/* We're done, no need for input nor mount mgr any longer */
|
|
CloseHandle(MountMgrHandle);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName);
|
|
|
|
/* Initialize:
|
|
- Number of paths we saw (useful to count extra \)
|
|
- Progress in mount mgr output
|
|
- Progress in output buffer
|
|
- Direct buffer to returned MultiSz
|
|
*/
|
|
Paths = 0;
|
|
CharsInMgr = 0;
|
|
CharsInOutput = 0;
|
|
MultiSz = VolumePaths->MultiSz;
|
|
|
|
/* If we have an output buffer */
|
|
if (cchBufferLength != 0)
|
|
{
|
|
/* Loop on the output to recopy it back to the caller
|
|
* Note that we loop until -1 not to handle last 0 (will be done later on)
|
|
*/
|
|
for (; (CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1) && (CharsInOutput < cchBufferLength);
|
|
++CharsInMgr, ++CharsInOutput)
|
|
{
|
|
/* When we reach the end of a path */
|
|
if (MultiSz[CharsInMgr] == UNICODE_NULL)
|
|
{
|
|
/* On path done (count), add an extra \ at the end */
|
|
++Paths;
|
|
lpszVolumePathNames[CharsInOutput] = L'\\';
|
|
++CharsInOutput;
|
|
/* Make sure we don't overflow */
|
|
if (CharsInOutput == cchBufferLength)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Copy the char to the caller
|
|
* So, in case we're in the end of a path, we wrote two chars to
|
|
* the output buffer: \\ and \0
|
|
*/
|
|
lpszVolumePathNames[CharsInOutput] = MultiSz[CharsInMgr];
|
|
}
|
|
}
|
|
|
|
/* If output buffer was too small (ie, we couldn't parse all the input buffer) */
|
|
if (CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1)
|
|
{
|
|
/* Keep looping on it, to count the number of extra \ that will be required
|
|
* So that on the next call, caller can allocate enough space
|
|
*/
|
|
for (; CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1; ++CharsInMgr)
|
|
{
|
|
if (MultiSz[CharsInMgr] == UNICODE_NULL)
|
|
{
|
|
++Paths;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If we couldn't write as much as we wanted to the output buffer
|
|
* This handles the case where we could write everything excepted the
|
|
* terminating \0 for multi SZ
|
|
*/
|
|
if (CharsInOutput >= cchBufferLength)
|
|
{
|
|
/* Fail and set appropriate error code */
|
|
Ret = FALSE;
|
|
SetLastError(ERROR_MORE_DATA);
|
|
/* If caller wants to know how many chars to allocate, return it */
|
|
if (lpcchReturnLength != NULL)
|
|
{
|
|
/* It's amount of extra \ + number of chars in MultiSz (including double \0) */
|
|
*lpcchReturnLength = Paths + (VolumePaths->MultiSzLength / sizeof(WCHAR));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* It succeed so terminate the multi SZ (second \0) */
|
|
lpszVolumePathNames[CharsInOutput] = UNICODE_NULL;
|
|
Ret = TRUE;
|
|
|
|
/* If caller wants the amount of chars written, return it */
|
|
if (lpcchReturnLength != NULL)
|
|
{
|
|
/* Including the terminating \0 we just added */
|
|
*lpcchReturnLength = CharsInOutput + 1;
|
|
}
|
|
}
|
|
|
|
/* Free last bits */
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths);
|
|
|
|
/* And return */
|
|
return Ret;
|
|
}
|
|
|
|
/* EOF */
|