mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-08-27 03:43:36 +00:00
Note: The wine implementation is broken (it relies on broken behavior of NtQueryObject) and cannot be used.
307 lines
10 KiB
C
307 lines
10 KiB
C
/*
|
|
* PROJECT: ReactOS Win32 Base API
|
|
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
|
* PURPOSE: Implementation of GetFinalPathNameByHandleW
|
|
* COPYRIGHT: Copyright 2025 Timo Kreuzer <[email protected]>
|
|
*/
|
|
|
|
#include "k32_vista.h"
|
|
#include <winnls.h>
|
|
#include <mountmgr.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
// Note: The wine implementation is broken and cannot be used.
|
|
|
|
static
|
|
NTSTATUS
|
|
QueryDosVolumeNameForNtDeviceName(
|
|
_In_ PCUNICODE_STRING DeviceName,
|
|
_Inout_ PUNICODE_STRING DosVolumeName)
|
|
{
|
|
static const UNICODE_STRING MupDevice = RTL_CONSTANT_STRING(L"\\Device\\Mup");
|
|
static const UNICODE_STRING LanManDevice = RTL_CONSTANT_STRING(L"\\Device\\LanmanRedirector");
|
|
static const UNICODE_STRING MountMgrDevice = RTL_CONSTANT_STRING(L"\\Device\\MountPointManager");
|
|
static OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&MountMgrDevice, OBJ_CASE_INSENSITIVE);
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE MountMgrHandle;
|
|
WCHAR Buffer[MAX_PATH];
|
|
PMOUNTMGR_TARGET_NAME TargetName = (PMOUNTMGR_TARGET_NAME)Buffer;
|
|
PMOUNTMGR_VOLUME_PATHS VolumePaths = (PMOUNTMGR_VOLUME_PATHS)Buffer;
|
|
NTSTATUS Status;
|
|
|
|
/* Validate parameters */
|
|
if ((DeviceName == NULL) || (DeviceName->Buffer == NULL) || (DeviceName->Length == 0) ||
|
|
(DosVolumeName == NULL) || (DosVolumeName->Buffer == NULL) || (DosVolumeName->MaximumLength == 0))
|
|
{
|
|
ASSERT(FALSE);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Reset the output name */
|
|
DosVolumeName->Length = 0;
|
|
|
|
/* Check for network shares */
|
|
if (RtlEqualUnicodeString(DeviceName, &MupDevice, TRUE) ||
|
|
RtlEqualUnicodeString(DeviceName, &LanManDevice, TRUE))
|
|
{
|
|
/* For Mup or LanmanRedirector we use the UNC path format */
|
|
return RtlAppendUnicodeToString(DosVolumeName, L"\\\\?\\UNC\\");
|
|
}
|
|
|
|
/* Open a handle to the mount manager */
|
|
Status = NtCreateFile(&MountMgrHandle,
|
|
STANDARD_RIGHTS_READ,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN_IF,
|
|
0,
|
|
NULL,
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtCreateFile failed: %lx\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Use the MountMgr to query the volume name */
|
|
TargetName->DeviceNameLength = DeviceName->Length;
|
|
RtlCopyMemory(TargetName->DeviceName, DeviceName->Buffer, DeviceName->Length);
|
|
Status = NtDeviceIoControlFile(MountMgrHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
|
|
Buffer,
|
|
sizeof(Buffer),
|
|
Buffer,
|
|
sizeof(Buffer));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtDeviceIoControlFile failed: %lx\n", Status);
|
|
goto Exit;
|
|
}
|
|
|
|
if ((IoStatusBlock.Information < sizeof(*VolumePaths)) ||
|
|
(IoStatusBlock.Information < (VolumePaths->MultiSzLength + sizeof(ULONG))))
|
|
{
|
|
DPRINT1("Invalid information returned: %lu\n", IoStatusBlock.Information);
|
|
ASSERT(FALSE);
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Exit;
|
|
}
|
|
|
|
/* Construct the full volume path name from the first returned DOS volume */
|
|
if (!NT_SUCCESS(RtlAppendUnicodeToString(DosVolumeName, L"\\\\?\\")) ||
|
|
!NT_SUCCESS(RtlAppendUnicodeToString(DosVolumeName, VolumePaths->MultiSz) ||
|
|
!NT_SUCCESS(RtlAppendUnicodeToString(DosVolumeName, L"\\"))))
|
|
{
|
|
DPRINT1("RtlAppendUnicodeToString failed: %lx\n", Status);
|
|
goto Exit;
|
|
}
|
|
|
|
/* This is based on Windows behavior */
|
|
SetLastError(ERROR_SUCCESS);
|
|
|
|
Exit:
|
|
NtClose(MountMgrHandle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
GetFinalPathNameByHandleW(
|
|
_In_ HANDLE hFile,
|
|
_Out_writes_(cchFilePath) LPWSTR lpszFilePath,
|
|
_In_ DWORD cchFilePath,
|
|
_In_ DWORD dwFlags)
|
|
{
|
|
struct
|
|
{
|
|
union
|
|
{
|
|
OBJECT_NAME_INFORMATION Object;
|
|
FILE_NAME_INFORMATION File;
|
|
};
|
|
WCHAR String[MAX_PATH];
|
|
} NameInfoBuffer;
|
|
PFILE_NAME_INFORMATION FileNameInfo = &NameInfoBuffer.File;
|
|
PUNICODE_STRING ObjectName = &NameInfoBuffer.Object.Name;
|
|
WCHAR VolumeBuffer[sizeof("\\Device\\Volume{01234567-89ab-cdef-0123-456789abcdef}")];
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PWSTR PathSplit;
|
|
UNICODE_STRING DeviceName, RelativePath, VolumeName;
|
|
SIZE_T VolumeLength;
|
|
ULONG VolumeType;
|
|
NTSTATUS Status;
|
|
ULONG FinalLength;
|
|
BOOL Success;
|
|
|
|
/* Extract the requested volume type */
|
|
VolumeType = dwFlags & (VOLUME_NAME_GUID | VOLUME_NAME_NONE | VOLUME_NAME_NT);
|
|
|
|
/* Check if more than one volume type flag is set */
|
|
if (VolumeType & (VolumeType- 1))
|
|
{
|
|
DPRINT1("Invalid flags passed: %lx\n", dwFlags);
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
/* Query the file name. This returns the relative path without volume.
|
|
This also validates that the handle refers to a file. */
|
|
Status = NtQueryInformationFile(hFile,
|
|
&IoStatusBlock,
|
|
&NameInfoBuffer,
|
|
sizeof(NameInfoBuffer),
|
|
FileNameInformation);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtQueryInformationFile failed: %lx\n", Status);
|
|
BaseSetLastNTError(Status);
|
|
return 0;
|
|
}
|
|
|
|
/* If no volume was requested, return the relative path */
|
|
if (VolumeType == VOLUME_NAME_NONE)
|
|
{
|
|
FinalLength = FileNameInfo->FileNameLength / sizeof(WCHAR);
|
|
if (cchFilePath < FinalLength + 1)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FinalLength + 1;
|
|
}
|
|
|
|
/* Copy the name to the caller's buffer */
|
|
RtlCopyMemory(lpszFilePath, FileNameInfo->FileName, FileNameInfo->FileNameLength);
|
|
lpszFilePath[FinalLength] = UNICODE_NULL;
|
|
|
|
/* Return the length of the name */
|
|
return FinalLength;
|
|
}
|
|
|
|
/* Query the NT object name */
|
|
Status = NtQueryObject(hFile,
|
|
ObjectNameInformation,
|
|
&NameInfoBuffer,
|
|
sizeof(NameInfoBuffer) - sizeof(WCHAR),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtQueryObject failed: %lx\n", Status);
|
|
BaseSetLastNTError(Status);
|
|
return 0;
|
|
}
|
|
|
|
/* If the NT path was requested, we return the name as it is */
|
|
if (VolumeType == VOLUME_NAME_NT)
|
|
{
|
|
/* Calculate the final length and validate the buffer size */
|
|
FinalLength = ObjectName->Length / sizeof(WCHAR);
|
|
if (cchFilePath < FinalLength + 1)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FinalLength + 1;
|
|
}
|
|
|
|
/* Copy the name to the caller's buffer */
|
|
RtlCopyMemory(lpszFilePath, ObjectName->Buffer, ObjectName->Length);
|
|
lpszFilePath[ObjectName->Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
/* Return the length of the name */
|
|
return FinalLength;
|
|
}
|
|
|
|
/* For everything else we need to split the path */
|
|
PathSplit = wcschr(&ObjectName->Buffer[sizeof("\\Device\\") - 1], L'\\');
|
|
if (PathSplit == NULL)
|
|
{
|
|
DPRINT1("Invalid object name: %wZ\n", ObjectName);
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return 0;
|
|
}
|
|
|
|
DeviceName.Buffer = ObjectName->Buffer;
|
|
DeviceName.Length = (USHORT)(PathSplit - ObjectName->Buffer) * sizeof(WCHAR);
|
|
DeviceName.MaximumLength = DeviceName.Length;
|
|
|
|
RelativePath.Buffer = PathSplit + 1;
|
|
RelativePath.Length = ObjectName->Length - DeviceName.Length - sizeof(WCHAR);
|
|
RelativePath.MaximumLength = RelativePath.Length;
|
|
|
|
/* Query the DOS volume name */
|
|
RtlInitEmptyUnicodeString(&VolumeName, VolumeBuffer, sizeof(VolumeBuffer));
|
|
Status = QueryDosVolumeNameForNtDeviceName(&DeviceName, &VolumeName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("QueryDosVolumeNameForNtDeviceName failed: %lx\n", Status);
|
|
BaseSetLastNTError(Status);
|
|
return 0;
|
|
}
|
|
|
|
if (VolumeType == VOLUME_NAME_DOS)
|
|
{
|
|
/* Calculate the final length and validate the buffer size */
|
|
FinalLength = ((VolumeName.Length + RelativePath.Length) / sizeof(WCHAR));
|
|
if (cchFilePath < FinalLength + 1)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FinalLength + 1;
|
|
}
|
|
|
|
/* Construct the final name */
|
|
RtlCopyMemory(lpszFilePath, VolumeName.Buffer, VolumeName.Length);
|
|
RtlCopyMemory(lpszFilePath + (VolumeName.Length / sizeof(WCHAR)),
|
|
RelativePath.Buffer,
|
|
RelativePath.Length);
|
|
lpszFilePath[FinalLength] = UNICODE_NULL;
|
|
|
|
/* Return the length of the name */
|
|
return FinalLength;
|
|
}
|
|
else if (VolumeType == VOLUME_NAME_GUID)
|
|
{
|
|
/* Query the GUID volume name */
|
|
Success = GetVolumeNameForVolumeMountPointW(VolumeBuffer,
|
|
VolumeBuffer,
|
|
ARRAYSIZE(VolumeBuffer));
|
|
if (!Success)
|
|
{
|
|
DPRINT1("GetVolumeNameForVolumeMountPointW failed: %d\n", GetLastError());
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
return 0;
|
|
}
|
|
|
|
/* Calculate the final length and validate the buffer size */
|
|
VolumeLength = wcslen(VolumeBuffer);
|
|
FinalLength = VolumeLength + (RelativePath.Length / sizeof(WCHAR));
|
|
if (cchFilePath < FinalLength + 1)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FinalLength + 1;
|
|
}
|
|
|
|
/* Construct the final name */
|
|
RtlCopyMemory(lpszFilePath, VolumeBuffer, VolumeLength * sizeof(WCHAR));
|
|
RtlCopyMemory(lpszFilePath + VolumeLength,
|
|
RelativePath.Buffer,
|
|
RelativePath.Length);
|
|
lpszFilePath[FinalLength] = UNICODE_NULL;
|
|
|
|
/* This is based on Windows behavior */
|
|
SetLastError(ERROR_SUCCESS);
|
|
|
|
return FinalLength;
|
|
}
|
|
|
|
DPRINT1("Invalid flags passed: %lx\n", dwFlags);
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|