From 552715f0c15d9a75ca13baf0ca84ebc939a3c745 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Tue, 29 Mar 2011 13:03:42 +0200 Subject: [PATCH] kernel32: Implement and test GetVolumePathNamesForVolumeName. --- dlls/kernel32/tests/volume.c | 169 +++++++++++++++++++++++++++++++++++ dlls/kernel32/volume.c | 159 ++++++++++++++++++++++++++++++-- 2 files changed, 322 insertions(+), 6 deletions(-) diff --git a/dlls/kernel32/tests/volume.c b/dlls/kernel32/tests/volume.c index 15f7e3cc867..fb920f3bc57 100644 --- a/dlls/kernel32/tests/volume.c +++ b/dlls/kernel32/tests/volume.c @@ -32,6 +32,8 @@ static BOOL (WINAPI *pFindVolumeClose)(HANDLE); static UINT (WINAPI *pGetLogicalDriveStringsA)(UINT,LPSTR); static UINT (WINAPI *pGetLogicalDriveStringsW)(UINT,LPWSTR); static BOOL (WINAPI *pGetVolumeInformationA)(LPCSTR, LPSTR, DWORD, LPDWORD, LPDWORD, LPDWORD, LPSTR, DWORD); +static BOOL (WINAPI *pGetVolumePathNamesForVolumeNameA)(LPCSTR, LPSTR, DWORD, LPDWORD); +static BOOL (WINAPI *pGetVolumePathNamesForVolumeNameW)(LPCWSTR, LPWSTR, DWORD, LPDWORD); /* ############################### */ @@ -525,6 +527,169 @@ static void test_disk_extents(void) CloseHandle( handle ); } +static void test_GetVolumePathNamesForVolumeNameA(void) +{ + BOOL ret; + char volume[MAX_PATH], buffer[MAX_PATH]; + DWORD len, error; + + if (!pGetVolumePathNamesForVolumeNameA || !pGetVolumeNameForVolumeMountPointA) + { + win_skip("required functions not found\n"); + return; + } + + ret = pGetVolumeNameForVolumeMountPointA( "c:\\", volume, sizeof(volume) ); + ok(ret, "failed to get volume name %u\n", GetLastError()); + trace("c:\\ -> %s\n", volume); + + SetLastError( 0xdeadbeef ); + ret = pGetVolumePathNamesForVolumeNameA( NULL, NULL, 0, NULL ); + error = GetLastError(); + ok(!ret, "expected failure\n"); + ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error); + + SetLastError( 0xdeadbeef ); + ret = pGetVolumePathNamesForVolumeNameA( "", NULL, 0, NULL ); + error = GetLastError(); + ok(!ret, "expected failure\n"); + ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error); + + SetLastError( 0xdeadbeef ); + ret = pGetVolumePathNamesForVolumeNameA( volume, NULL, 0, NULL ); + error = GetLastError(); + ok(!ret, "expected failure\n"); + ok(error == ERROR_MORE_DATA, "expected ERROR_MORE_DATA got %u\n", error); + + SetLastError( 0xdeadbeef ); + ret = pGetVolumePathNamesForVolumeNameA( volume, buffer, 0, NULL ); + error = GetLastError(); + ok(!ret, "expected failure\n"); + ok(error == ERROR_MORE_DATA, "expected ERROR_MORE_DATA got %u\n", error); + + memset( buffer, 0xff, sizeof(buffer) ); + ret = pGetVolumePathNamesForVolumeNameA( volume, buffer, sizeof(buffer), NULL ); + ok(ret, "failed to get path names %u\n", GetLastError()); + ok(!strcmp( "C:\\", buffer ), "expected \"\\C:\" got \"%s\"\n", buffer); + ok(!buffer[4], "expected double null-terminated buffer\n"); + + len = 0; + SetLastError( 0xdeadbeef ); + ret = pGetVolumePathNamesForVolumeNameA( NULL, NULL, 0, &len ); + error = GetLastError(); + ok(!ret, "expected failure\n"); + ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error); + + len = 0; + SetLastError( 0xdeadbeef ); + ret = pGetVolumePathNamesForVolumeNameA( NULL, NULL, sizeof(buffer), &len ); + error = GetLastError(); + ok(!ret, "expected failure\n"); + ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error); + + len = 0; + SetLastError( 0xdeadbeef ); + ret = pGetVolumePathNamesForVolumeNameA( NULL, buffer, sizeof(buffer), &len ); + error = GetLastError(); + ok(!ret, "expected failure\n"); + ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error); + + len = 0; + SetLastError( 0xdeadbeef ); + ret = pGetVolumePathNamesForVolumeNameA( NULL, buffer, sizeof(buffer), &len ); + error = GetLastError(); + ok(!ret, "expected failure\n"); + ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error); + + len = 0; + memset( buffer, 0xff, sizeof(buffer) ); + ret = pGetVolumePathNamesForVolumeNameA( volume, buffer, sizeof(buffer), &len ); + ok(ret, "failed to get path names %u\n", GetLastError()); + ok(len == 5 || broken(len == 2), "expected 5 got %u\n", len); + ok(!strcmp( "C:\\", buffer ), "expected \"\\C:\" got \"%s\"\n", buffer); + ok(!buffer[4], "expected double null-terminated buffer\n"); +} + +static void test_GetVolumePathNamesForVolumeNameW(void) +{ + static const WCHAR empty[] = {0}; + static const WCHAR drive_c[] = {'c',':','\\',0}; + static const WCHAR volume_null[] = {'\\','\\','?','\\','V','o','l','u','m','e', + '{','0','0','0','0','0','0','0','0','-','0','0','0','0','-','0','0','0','0', + '-','0','0','0','0','-','0','0','0','0','0','0','0','0','0','0','0','0','}','\\',0}; + BOOL ret; + WCHAR volume[MAX_PATH], buffer[MAX_PATH]; + DWORD len, error; + + if (!pGetVolumePathNamesForVolumeNameW || !pGetVolumeNameForVolumeMountPointW) + { + win_skip("required functions not found\n"); + return; + } + + ret = pGetVolumeNameForVolumeMountPointW( drive_c, volume, sizeof(volume)/sizeof(volume[0]) ); + ok(ret, "failed to get volume name %u\n", GetLastError()); + + SetLastError( 0xdeadbeef ); + ret = pGetVolumePathNamesForVolumeNameW( empty, NULL, 0, NULL ); + error = GetLastError(); + ok(!ret, "expected failure\n"); + ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error); + + SetLastError( 0xdeadbeef ); + ret = pGetVolumePathNamesForVolumeNameW( volume, NULL, 0, NULL ); + error = GetLastError(); + ok(!ret, "expected failure\n"); + ok(error == ERROR_MORE_DATA, "expected ERROR_MORE_DATA got %u\n", error); + + SetLastError( 0xdeadbeef ); + ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, 0, NULL ); + error = GetLastError(); + ok(!ret, "expected failure\n"); + ok(error == ERROR_MORE_DATA, "expected ERROR_MORE_DATA got %u\n", error); + + if (0) { /* crash */ + ret = pGetVolumePathNamesForVolumeNameW( volume, NULL, sizeof(buffer), NULL ); + ok(ret, "failed to get path names %u\n", GetLastError()); + } + + ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), NULL ); + ok(ret, "failed to get path names %u\n", GetLastError()); + + len = 0; + memset( buffer, 0xff, sizeof(buffer) ); + ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), &len ); + ok(ret, "failed to get path names %u\n", GetLastError()); + ok(len == 5, "expected 5 got %u\n", len); + ok(!buffer[4], "expected double null-terminated buffer\n"); + + len = 0; + volume[1] = '?'; + volume[lstrlenW( volume ) - 1] = 0; + SetLastError( 0xdeadbeef ); + ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), &len ); + error = GetLastError(); + ok(!ret, "expected failure\n"); + ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error); + + len = 0; + volume[0] = '\\'; + volume[1] = 0; + SetLastError( 0xdeadbeef ); + ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), &len ); + error = GetLastError(); + ok(!ret, "expected failure\n"); + todo_wine ok(error == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER got %u\n", error); + + len = 0; + lstrcpyW( volume, volume_null ); + SetLastError( 0xdeadbeef ); + ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), &len ); + error = GetLastError(); + ok(!ret, "expected failure\n"); + ok(error == ERROR_FILE_NOT_FOUND, "expected ERROR_FILE_NOT_FOUND got %u\n", error); +} + START_TEST(volume) { hdll = GetModuleHandleA("kernel32.dll"); @@ -536,6 +701,8 @@ START_TEST(volume) pGetLogicalDriveStringsA = (void *) GetProcAddress(hdll, "GetLogicalDriveStringsA"); pGetLogicalDriveStringsW = (void *) GetProcAddress(hdll, "GetLogicalDriveStringsW"); pGetVolumeInformationA = (void *) GetProcAddress(hdll, "GetVolumeInformationA"); + pGetVolumePathNamesForVolumeNameA = (void *) GetProcAddress(hdll, "GetVolumePathNamesForVolumeNameA"); + pGetVolumePathNamesForVolumeNameW = (void *) GetProcAddress(hdll, "GetVolumePathNamesForVolumeNameW"); test_query_dos_deviceA(); test_FindFirstVolume(); @@ -546,4 +713,6 @@ START_TEST(volume) test_GetVolumeInformationA(); test_enum_vols(); test_disk_extents(); + test_GetVolumePathNamesForVolumeNameA(); + test_GetVolumePathNamesForVolumeNameW(); } diff --git a/dlls/kernel32/volume.c b/dlls/kernel32/volume.c index 16849670ec6..25affc3f46f 100644 --- a/dlls/kernel32/volume.c +++ b/dlls/kernel32/volume.c @@ -1625,19 +1625,166 @@ BOOL WINAPI GetVolumePathNameW(LPCWSTR filename, LPWSTR volumepathname, DWORD bu */ BOOL WINAPI GetVolumePathNamesForVolumeNameA(LPCSTR volumename, LPSTR volumepathname, DWORD buflen, PDWORD returnlen) { - FIXME("(%s, %p, %d, %p), stub!\n", debugstr_a(volumename), volumepathname, buflen, returnlen); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + BOOL ret; + WCHAR *volumenameW = NULL, *volumepathnameW; + + if (volumename && !(volumenameW = FILE_name_AtoW( volumename, TRUE ))) return FALSE; + if (!(volumepathnameW = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) ))) + { + HeapFree( GetProcessHeap(), 0, volumenameW ); + return FALSE; + } + if ((ret = GetVolumePathNamesForVolumeNameW( volumenameW, volumepathnameW, buflen, returnlen ))) + { + char *path = volumepathname; + const WCHAR *pathW = volumepathnameW; + + while (*pathW) + { + int len = strlenW( pathW ) + 1; + FILE_name_WtoA( pathW, len, path, buflen ); + buflen -= len; + pathW += len; + path += len; + } + path[0] = 0; + } + HeapFree( GetProcessHeap(), 0, volumenameW ); + HeapFree( GetProcessHeap(), 0, volumepathnameW ); + return ret; } +static MOUNTMGR_MOUNT_POINTS *query_mount_points( HANDLE mgr, MOUNTMGR_MOUNT_POINT *input, DWORD insize ) +{ + MOUNTMGR_MOUNT_POINTS *output; + DWORD outsize = 1024; + + for (;;) + { + if (!(output = HeapAlloc( GetProcessHeap(), 0, outsize ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return NULL; + } + if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, input, insize, output, outsize, NULL, NULL )) break; + outsize = output->Size; + HeapFree( GetProcessHeap(), 0, output ); + if (GetLastError() != ERROR_MORE_DATA) return NULL; + } + return output; +} /*********************************************************************** * GetVolumePathNamesForVolumeNameW (KERNEL32.@) */ BOOL WINAPI GetVolumePathNamesForVolumeNameW(LPCWSTR volumename, LPWSTR volumepathname, DWORD buflen, PDWORD returnlen) { - FIXME("(%s, %p, %d, %p), stub!\n", debugstr_w(volumename), volumepathname, buflen, returnlen); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + static const WCHAR dosdevicesW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'}; + HANDLE mgr; + DWORD len, size; + MOUNTMGR_MOUNT_POINT *spec; + MOUNTMGR_MOUNT_POINTS *link, *target = NULL; + WCHAR *name, *path; + BOOL ret = FALSE; + UINT i, j; + + TRACE("%s, %p, %u, %p\n", debugstr_w(volumename), volumepathname, buflen, returnlen); + + if (!volumename || (len = strlenW( volumename )) != 49) + { + SetLastError( ERROR_INVALID_NAME ); + return FALSE; + } + mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 ); + if (mgr == INVALID_HANDLE_VALUE) return FALSE; + + size = sizeof(*spec) + sizeof(WCHAR) * (len - 1); /* remove trailing backslash */ + if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done; + spec->SymbolicLinkNameOffset = sizeof(*spec); + spec->SymbolicLinkNameLength = size - sizeof(*spec); + name = (WCHAR *)((char *)spec + spec->SymbolicLinkNameOffset); + memcpy( name, volumename, size - sizeof(*spec) ); + name[1] = '?'; /* map \\?\ to \??\ */ + + target = query_mount_points( mgr, spec, size ); + HeapFree( GetProcessHeap(), 0, spec ); + if (!target) + { + goto done; + } + if (!target->NumberOfMountPoints) + { + SetLastError( ERROR_FILE_NOT_FOUND ); + goto done; + } + len = 0; + path = volumepathname; + for (i = 0; i < target->NumberOfMountPoints; i++) + { + link = NULL; + if (target->MountPoints[i].DeviceNameOffset) + { + const WCHAR *device = (const WCHAR *)((const char *)target + target->MountPoints[i].DeviceNameOffset); + USHORT device_len = target->MountPoints[i].DeviceNameLength; + + size = sizeof(*spec) + device_len; + if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done; + spec->DeviceNameOffset = sizeof(*spec); + spec->DeviceNameLength = device_len; + memcpy( (char *)spec + spec->DeviceNameOffset, device, device_len ); + + link = query_mount_points( mgr, spec, size ); + HeapFree( GetProcessHeap(), 0, spec ); + } + else if (target->MountPoints[i].UniqueIdOffset) + { + const WCHAR *id = (const WCHAR *)((const char *)target + target->MountPoints[i].UniqueIdOffset); + USHORT id_len = target->MountPoints[i].UniqueIdLength; + + size = sizeof(*spec) + id_len; + if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done; + spec->UniqueIdOffset = sizeof(*spec); + spec->UniqueIdLength = id_len; + memcpy( (char *)spec + spec->UniqueIdOffset, id, id_len ); + + link = query_mount_points( mgr, spec, size ); + HeapFree( GetProcessHeap(), 0, spec ); + } + if (!link) continue; + for (j = 0; j < link->NumberOfMountPoints; j++) + { + const WCHAR *linkname; + + if (!link->MountPoints[j].SymbolicLinkNameOffset) continue; + linkname = (const WCHAR *)((const char *)link + link->MountPoints[j].SymbolicLinkNameOffset); + + if (link->MountPoints[j].SymbolicLinkNameLength == sizeof(dosdevicesW) + 2 * sizeof(WCHAR) && + !memicmpW( linkname, dosdevicesW, sizeof(dosdevicesW) / sizeof(WCHAR) )) + { + len += 4; + if (volumepathname && len < buflen) + { + path[0] = linkname[sizeof(dosdevicesW) / sizeof(WCHAR)]; + path[1] = ':'; + path[2] = '\\'; + path[3] = 0; + path += 4; + } + } + } + HeapFree( GetProcessHeap(), 0, link ); + } + if (buflen <= len) SetLastError( ERROR_MORE_DATA ); + else if (volumepathname) + { + volumepathname[len] = 0; + ret = TRUE; + } + if (returnlen) *returnlen = len + 1; + +done: + HeapFree( GetProcessHeap(), 0, target ); + CloseHandle( mgr ); + return ret; } /***********************************************************************