Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307/* * PROJECT: ReactOS Win32 Base API * LICENSE: MIT (https://spdx.org/licenses/MIT) * PURPOSE: Implementation of GetFinalPathNameByHandleW * COPYRIGHT: Copyright 2025 Timo Kreuzer <timo.kreuzer@reactos.org> */
#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.
staticNTSTATUSQueryDosVolumeNameForNtDeviceName( _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;}
DWORDWINAPIGetFinalPathNameByHandleW( _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;}