Коммит 350ede0c создал по автору Pierre Schweitzer's avatar Pierre Schweitzer
Просмотр файлов

[KERNEL32]

Reimplement GetVolumePathNameW(). This fixes a lot of failing Wine tests.
Note that three of them are not fixed, which is a good thing as these also fail in the WHS bot!

svn path=/trunk/; revision=76031
владелец b896e9a9
...@@ -748,22 +748,59 @@ GetVolumePathNameA(IN LPCSTR lpszFileName, ...@@ -748,22 +748,59 @@ GetVolumePathNameA(IN LPCSTR lpszFileName,
IN LPSTR lpszVolumePathName, IN LPSTR lpszVolumePathName,
IN DWORD cchBufferLength) IN DWORD cchBufferLength)
{ {
PWCHAR FileNameW = NULL; BOOL Ret;
WCHAR VolumePathName[MAX_PATH]; PUNICODE_STRING FileNameU;
BOOL Result; ANSI_STRING VolumePathName;
UNICODE_STRING VolumePathNameU;
if (lpszFileName) /* Convert file name to unicode */
FileNameU = Basep8BitStringToStaticUnicodeString(lpszFileName);
if (FileNameU == NULL)
{ {
if (!(FileNameW = FilenameA2W(lpszFileName, FALSE))) return FALSE;
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;
} }
Result = GetVolumePathNameW(FileNameW, VolumePathName, cchBufferLength); /* Call the -W implementation */
Ret = GetVolumePathNameW(FileNameU->Buffer, VolumePathNameU.Buffer, cchBufferLength);
/* If it succeed */
if (Ret)
{
NTSTATUS Status;
if (Result) /* Convert back to ANSI */
FilenameW2A_N(lpszVolumePathName, MAX_PATH, VolumePathName, -1); 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;
}
}
return Result; /* Free the buffer allocated for -W call */
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNameU.Buffer);
return Ret;
} }
/* /*
...@@ -775,112 +812,235 @@ GetVolumePathNameW(IN LPCWSTR lpszFileName, ...@@ -775,112 +812,235 @@ GetVolumePathNameW(IN LPCWSTR lpszFileName,
IN LPWSTR lpszVolumePathName, IN LPWSTR lpszVolumePathName,
IN DWORD cchBufferLength) IN DWORD cchBufferLength)
{ {
DWORD PathLength; BOOL MountPoint;
UNICODE_STRING UnicodeFilePath; DWORD FullPathLen;
LPWSTR FilePart; WCHAR OldFilePart;
PWSTR FullFilePath, FilePathName; UNICODE_STRING FullPath;
ULONG PathSize; PWSTR FullPathBuf, FilePart, VolumeNameBuf;
WCHAR VolumeName[MAX_PATH];
DWORD ErrorCode;
BOOL Result = FALSE;
if (!lpszFileName || !lpszVolumePathName || !cchBufferLength) /* Probe for full path len */
FullPathLen = GetFullPathNameW(lpszFileName, 0, NULL, NULL);
if (FullPathLen == 0)
{ {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE; return FALSE;
} }
if (!(PathLength = GetFullPathNameW(lpszFileName, 0, NULL, NULL))) /* Allocate a big enough buffer to receive it */
FullPathBuf = RtlAllocateHeap(RtlGetProcessHeap(), 0, (FullPathLen + 10) * sizeof(WCHAR));
if (FullPathBuf == NULL)
{ {
return Result; SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
} }
else
/* 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))
{ {
PathLength = PathLength + 10; USHORT LastSlash;
PathSize = PathLength * sizeof(WCHAR);
if (!(FullFilePath = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize))) /* 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)
{ {
SetLastError(ERROR_NOT_ENOUGH_MEMORY); MountPoint = TRUE;
return Result;
} }
if (!GetFullPathNameW(lpszFileName, PathLength, FullFilePath, &FilePart)) /* 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'\\'))
{ {
RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath); CHAR RootPathName[4];
return Result;
} /* 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);
}
RtlInitUnicodeString(&UnicodeFilePath, FullFilePath); /* And done! */
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameBuf);
return Ret;
}
}
if (UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] != '\\') /* No mount point but with a file part? Restore filepart and exit */
if (!MountPoint && FilePart != NULL)
{ {
UnicodeFilePath.Length += sizeof(WCHAR); FilePart[0] = OldFilePart;
UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] = '\\'; RtlInitUnicodeString(&FullPath, FullPathBuf);
UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0'; break;
} }
if (!(FilePathName = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize))) /* We cannot go down the path any longer, too small */
if (FullPath.Length <= sizeof(WCHAR))
{ {
RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath); break;
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return Result;
} }
while (!GetVolumeNameForVolumeMountPointW(UnicodeFilePath.Buffer, /* Prepare the next split */
VolumeName, LastSlash = (FullPath.Length / sizeof(WCHAR)) - 2;
MAX_PATH)) if (FullPath.Length / sizeof(WCHAR) != 2)
{ {
if (((UnicodeFilePath.Length == 4) && (UnicodeFilePath.Buffer[0] == '\\') && do
(UnicodeFilePath.Buffer[1] == '\\')) || ((UnicodeFilePath.Length == 6) &&
(UnicodeFilePath.Buffer[1] == ':')))
{ {
break; if (FullPath.Buffer[LastSlash] == L'\\')
} {
break;
}
UnicodeFilePath.Length -= sizeof(WCHAR); --LastSlash;
UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0'; } while (LastSlash != 0);
}
memcpy(FilePathName, UnicodeFilePath.Buffer, UnicodeFilePath.Length); /* We couldn't split path, quit */
FilePathName[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0'; if (LastSlash == 0)
{
break;
}
if (!GetFullPathNameW(FilePathName, PathLength, FullFilePath, &FilePart)) /* If that's a mount point, keep track of the directory name */
{ if (MountPoint)
goto Cleanup2; {
} 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;
}
if (!FilePart) /* We went down a bit in the path, fix the string and retry */
{ RtlInitUnicodeString(&FullPath, FullPathBuf);
RtlInitUnicodeString(&UnicodeFilePath, FullFilePath); }
UnicodeFilePath.Length += sizeof(WCHAR);
UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] = '\\';
UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
break;
}
FilePart[0] = '\0'; /* Once here, we'll return something from the full path buffer, so release
RtlInitUnicodeString(&UnicodeFilePath, FullFilePath); * output buffer
} */
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameBuf);
/* Not a mount point, bail out */
if (!MountPoint && FilePart == NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
return FALSE;
} }
if (UnicodeFilePath.Length > (cchBufferLength * sizeof(WCHAR)) - sizeof(WCHAR)) /* Make sure we have enough room to copy our volume */
if ((cchBufferLength * sizeof(WCHAR)) < FullPath.Length + sizeof(UNICODE_NULL))
{ {
ErrorCode = ERROR_FILENAME_EXCED_RANGE; RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
goto Cleanup1; SetLastError(ERROR_FILENAME_EXCED_RANGE);
return FALSE;
} }
memcpy(lpszVolumePathName, UnicodeFilePath.Buffer, UnicodeFilePath.Length); /* Copy and null terminate */
lpszVolumePathName[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0'; RtlCopyMemory(lpszVolumePathName, FullPath.Buffer, FullPath.Length);
lpszVolumePathName[FullPath.Length / sizeof(WCHAR)] = UNICODE_NULL;
Result = TRUE; RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
goto Cleanup2;
Cleanup1: /* Done! */
SetLastError(ErrorCode); return TRUE;
Cleanup2:
RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
RtlFreeHeap(RtlGetProcessHeap(), 0, FilePathName);
return Result;
} }
/* /*
......
...@@ -440,6 +440,14 @@ BasepCopyFileExW( ...@@ -440,6 +440,14 @@ BasepCopyFileExW(
OUT LPHANDLE lpNewHandle OUT LPHANDLE lpNewHandle
); );
BOOL
BasepGetVolumeNameForVolumeMountPoint(
IN LPCWSTR lpszMountPoint,
OUT LPWSTR lpszVolumeName,
IN DWORD cchBufferLength,
OUT LPBOOL IsAMountPoint
);
/* FIXME: This is EXPORTED! It should go in an external kernel32.h header */ /* FIXME: This is EXPORTED! It should go in an external kernel32.h header */
VOID VOID
WINAPI WINAPI
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать