Implement GetVolumePathName.

This commit is contained in:
Erich E. Hoover 2014-01-25 10:08:49 -07:00
parent 34a6c25071
commit dd5b1514b2
5 changed files with 425 additions and 1 deletions

View File

@ -0,0 +1,199 @@
From bcf395630fde511be0606ad8292385f37d127969 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Sat, 25 Jan 2014 09:47:12 -0700
Subject: kernel32: Implement GetVolumePathName.
---
dlls/kernel32/tests/volume.c | 3 -
dlls/kernel32/volume.c | 132 +++++++++++++++++++++++++++++++++++++-----
2 files changed, 116 insertions(+), 19 deletions(-)
diff --git a/dlls/kernel32/tests/volume.c b/dlls/kernel32/tests/volume.c
index 61da509..4b3bdf5 100644
--- a/dlls/kernel32/tests/volume.c
+++ b/dlls/kernel32/tests/volume.c
@@ -649,15 +649,12 @@ static void test_GetVolumePathNameA(void)
volume[0] = '\0';
ret = pGetVolumePathNameA(pathC1, volume, sizeof(volume));
ok(ret, "expected success\n");
-todo_wine
ok(!strcmp(expected, volume) || broken(!strcasecmp(expected, volume)) /* <=XP */,
"expected name '%s', returned '%s'\n", expected, volume);
volume[0] = '\0';
ret = pGetVolumePathNameA(pathC2, volume, sizeof(volume));
-todo_wine
ok(ret, "expected success\n");
-todo_wine
ok(!strcmp(expected, volume), "expected name '%s', returned '%s'\n", expected, volume);
/* test an invalid path */
diff --git a/dlls/kernel32/volume.c b/dlls/kernel32/volume.c
index 1509d73..a4b158a 100644
--- a/dlls/kernel32/volume.c
+++ b/dlls/kernel32/volume.c
@@ -1784,7 +1784,7 @@ BOOL WINAPI GetVolumePathNameA(LPCSTR filename, LPSTR volumepathname, DWORD bufl
BOOL ret;
WCHAR *filenameW = NULL, *volumeW = NULL;
- FIXME("(%s, %p, %d), stub!\n", debugstr_a(filename), volumepathname, buflen);
+ TRACE("(%s, %p, %d)\n", debugstr_a(filename), volumepathname, buflen);
if (filename && !(filenameW = FILE_name_AtoW( filename, FALSE )))
return FALSE;
@@ -1800,37 +1800,137 @@ BOOL WINAPI GetVolumePathNameA(LPCSTR filename, LPSTR volumepathname, DWORD bufl
/***********************************************************************
* GetVolumePathNameW (KERNEL32.@)
+ *
+ * This routine is intended to find the most basic path on the same filesystem
+ * for any particular path name. Since we can have very complicated drive/path
+ * relationships on Unix systems, due to symbolic links, the safest way to
+ * handle this is to start with the full path and work our way back folder by
+ * folder unil we find a folder on a different drive (or run out of folders).
*/
BOOL WINAPI GetVolumePathNameW(LPCWSTR filename, LPWSTR volumepathname, DWORD buflen)
{
- const WCHAR *p = filename;
+ const WCHAR ntprefixW[] = { '\\','\\','?','\\',0 };
+ const WCHAR fallbackpathW[] = { 'C',':','\\',0 };
+ NTSTATUS status = STATUS_SUCCESS;
+ WCHAR *volumenameW = NULL, *c;
+ int pos, last_pos, stop_pos;
+ UNICODE_STRING nt_name;
+ ANSI_STRING unix_name;
+ BOOL first_run = TRUE;
+ dev_t search_dev = 0;
+ struct stat st;
- FIXME("(%s, %p, %d), stub!\n", debugstr_w(filename), volumepathname, buflen);
+ TRACE("(%s, %p, %d)\n", debugstr_w(filename), volumepathname, buflen);
- if (!filename || !volumepathname || !buflen)
+ if (!filename || !volumepathname || buflen == 0)
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return FALSE;
+ }
+ last_pos = pos = strlenW( filename );
+ /* allocate enough memory for searching the path (need room for a slash and a NULL terminator) */
+ if (!(volumenameW = HeapAlloc( GetProcessHeap(), 0, (pos + 2) * sizeof(WCHAR) )))
{
- SetLastError(ERROR_INVALID_PARAMETER);
+ SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return FALSE;
}
+ strcpyW( volumenameW, filename );
+ stop_pos = 0;
+ /* stop searching slashes early for NT-type and nearly NT-type paths */
+ if (strncmpW(ntprefixW, filename, strlenW(ntprefixW)) == 0)
+ stop_pos = strlenW(ntprefixW)-1;
+ else if (strncmpW(ntprefixW, filename, 2) == 0)
+ stop_pos = 2;
+
+ do
+ {
+ volumenameW[pos+0] = '\\';
+ volumenameW[pos+1] = '\0';
+ if (!RtlDosPathNameToNtPathName_U( volumenameW, &nt_name, NULL, NULL ))
+ goto cleanup;
+ volumenameW[pos] = '\0';
+ status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, FALSE );
+ RtlFreeUnicodeString( &nt_name );
+ if (status == STATUS_SUCCESS)
+ {
+ if (stat( unix_name.Buffer, &st ) != 0)
+ {
+ RtlFreeAnsiString( &unix_name );
+ status = STATUS_OBJECT_NAME_INVALID;
+ goto cleanup;
+ }
+ RtlFreeAnsiString( &unix_name );
+ if (first_run)
+ {
+ first_run = FALSE;
+ search_dev = st.st_dev;
+ }
+ else if (st.st_dev != search_dev)
+ {
+ /* folder is on a new filesystem, return the last folder */
+ break;
+ }
+ }
+ last_pos = pos;
+ c = strrchrW( volumenameW, '\\' );
+ if (c != NULL)
+ pos = c-volumenameW;
+ } while (c != NULL && pos > stop_pos);
- if (p && tolowerW(p[0]) >= 'a' && tolowerW(p[0]) <= 'z' && p[1] ==':' && p[2] == '\\')
+ if (status != STATUS_SUCCESS)
{
- if (buflen < 4)
+ /* the path was completely invalid */
+ if (filename[0] != '\\')
{
- SetLastError(ERROR_FILENAME_EXCED_RANGE);
- return FALSE;
+ /* DOS-style paths revert to C:\ (anything not beginning with a slash) */
+ last_pos = strlenW(fallbackpathW);
+ filename = fallbackpathW;
+ status = STATUS_SUCCESS;
+ }
+ else
+ {
+ /* NT-style paths fail */
+ status = STATUS_OBJECT_NAME_INVALID;
+ goto cleanup;
}
- volumepathname[0] = p[0];
- volumepathname[1] = ':';
- volumepathname[2] = '\\';
- volumepathname[3] = 0;
- return TRUE;
}
- SetLastError(ERROR_INVALID_NAME);
- return FALSE;
+ /* include the terminating backslash unless returning the full path */
+ if (filename[last_pos] != '\0')
+ last_pos++;
+ /* require room to NULL terminate the string */
+ if ((filename[last_pos] == '\\' && last_pos * sizeof(WCHAR) <= buflen)
+ || (last_pos+1) * sizeof(WCHAR) <= buflen)
+ {
+ memcpy(volumepathname, filename, last_pos*sizeof(WCHAR));
+ /* remove the terminating backslash if the buffer is one byte short */
+ if (filename[last_pos] == '\\' && (last_pos+1) * sizeof(WCHAR) > buflen)
+ last_pos--;
+ volumepathname[last_pos] = '\0';
+ }
+ else
+ status = STATUS_NAME_TOO_LONG;
+
+ if (status == STATUS_SUCCESS)
+ {
+ if (volumepathname[1] == ':')
+ {
+ /* DOS-style paths always return upper-case drive letters */
+ volumepathname[0] = toupper(volumepathname[0]);
+ }
+ TRACE("Successfully translated path %s to mount-point %s\n",
+ debugstr_w(filename), debugstr_w(volumepathname));
+ }
+
+cleanup:
+ HeapFree( GetProcessHeap(), 0, volumenameW );
+
+ if (status != STATUS_SUCCESS)
+ SetLastError( RtlNtStatusToDosError(status) );
+ return (status == STATUS_SUCCESS);
}
+
/***********************************************************************
* GetVolumePathNamesForVolumeNameA (KERNEL32.@)
*/
--
1.7.9.5

View File

@ -0,0 +1,170 @@
From 9d3403bbcab0fbfb465a224fae93ebbc9e442e35 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Sat, 25 Jan 2014 09:53:39 -0700
Subject: kernel32: Convert GetVolumePathName tests into a list.
---
dlls/kernel32/tests/volume.c | 137 +++++++++++++++++++++++-------------------
1 file changed, 74 insertions(+), 63 deletions(-)
diff --git a/dlls/kernel32/tests/volume.c b/dlls/kernel32/tests/volume.c
index 4b3bdf5..7f68dd0 100644
--- a/dlls/kernel32/tests/volume.c
+++ b/dlls/kernel32/tests/volume.c
@@ -591,79 +591,90 @@ static void test_disk_extents(void)
static void test_GetVolumePathNameA(void)
{
- BOOL ret;
- char volume[MAX_PATH];
- char expected[] = "C:\\", pathC1[] = "C:\\", pathC2[] = "C::";
+ char volume_path[MAX_PATH];
+ struct {
+ const char *file_name;
+ const char *path_name;
+ DWORD path_len;
+ DWORD error;
+ DWORD broken_error;
+ } test_paths[] = {
+ { /* test 0: NULL parameters, 0 output length */
+ NULL, NULL, 0,
+ ERROR_INVALID_PARAMETER, 0xdeadbeef /* XP */
+ },
+ { /* test 1: empty input, NULL output, 0 output length */
+ "", NULL, 0,
+ ERROR_INVALID_PARAMETER, 0xdeadbeef /* XP */
+ },
+ { /* test 2: valid input, NULL output, 0 output length */
+ "C:\\", NULL, 0,
+ ERROR_INVALID_PARAMETER, ERROR_FILENAME_EXCED_RANGE /* XP */
+ },
+ { /* test 3: valid input, valid output, 0 output length */
+ "C:\\", "C:\\", 0,
+ ERROR_INVALID_PARAMETER, ERROR_FILENAME_EXCED_RANGE /* XP */
+ },
+ { /* test 4: valid input, valid output, 1 output length */
+ "C:\\", "C:\\", 1,
+ ERROR_FILENAME_EXCED_RANGE, NO_ERROR
+ },
+ { /* test 5: valid input, valid output, valid output length */
+ "C:\\", "C:\\", sizeof(volume_path),
+ NO_ERROR, NO_ERROR
+ },
+ { /* test 6: lowercase input, uppercase output, valid output length */
+ "c:\\", "C:\\", sizeof(volume_path),
+ NO_ERROR, NO_ERROR
+ },
+ { /* test 7: poor quality input, valid output, valid output length */
+ "C::", "C:\\", sizeof(volume_path),
+ NO_ERROR, NO_ERROR
+ },
+ { /* test 8: really bogus input, valid output, 1 output length */
+ "\\\\$$$", "C:\\", 1,
+ ERROR_INVALID_NAME, ERROR_FILENAME_EXCED_RANGE
+ },
+ };
+ BOOL ret, success;
DWORD error;
+ UINT i;
+ /* GetVolumePathNameA is not present before w2k */
if (!pGetVolumePathNameA)
{
win_skip("required functions not found\n");
return;
}
- SetLastError( 0xdeadbeef );
- ret = pGetVolumePathNameA(NULL, NULL, 0);
- error = GetLastError();
- ok(!ret, "expected failure\n");
- ok(error == ERROR_INVALID_PARAMETER
- || broken( error == 0xdeadbeef) /* <=XP */,
- "expected ERROR_INVALID_PARAMETER got %u\n", error);
-
- SetLastError( 0xdeadbeef );
- ret = pGetVolumePathNameA("", NULL, 0);
- error = GetLastError();
- ok(!ret, "expected failure\n");
- ok(error == ERROR_INVALID_PARAMETER
- || broken( error == 0xdeadbeef) /* <=XP */,
- "expected ERROR_INVALID_PARAMETER got %u\n", error);
+ for(i=0; i<sizeof(test_paths)/sizeof(test_paths[0]); i++)
+ {
+ char *output = (test_paths[i].path_name != NULL ? volume_path : NULL);
+ BOOL expected_ret = test_paths[i].error == NO_ERROR ? TRUE : FALSE;
- SetLastError( 0xdeadbeef );
- ret = pGetVolumePathNameA(pathC1, NULL, 0);
- error = GetLastError();
- ok(!ret, "expected failure\n");
- ok(error == ERROR_INVALID_PARAMETER
- || broken(error == ERROR_FILENAME_EXCED_RANGE) /* <=XP */,
- "expected ERROR_INVALID_PARAMETER got %u\n", error);
+ volume_path[0] = 0;
+ SetLastError( 0xdeadbeef );
+ ret = pGetVolumePathNameA( test_paths[i].file_name, output, test_paths[i].path_len );
+ error = GetLastError();
+ ok(ret == expected_ret, "GetVolumePathName test %d %s unexpectedly.\n",
+ i, test_paths[i].error == NO_ERROR ? "failed" : "succeeded");
- SetLastError( 0xdeadbeef );
- ret = pGetVolumePathNameA(pathC1, volume, 0);
- error = GetLastError();
- ok(!ret, "expected failure\n");
- ok(error == ERROR_INVALID_PARAMETER
- || broken(error == ERROR_FILENAME_EXCED_RANGE ) /* <=XP */,
- "expected ERROR_INVALID_PARAMETER got %u\n", error);
-
- SetLastError( 0xdeadbeef );
- ret = pGetVolumePathNameA(pathC1, volume, 1);
- error = GetLastError();
- ok(!ret, "expected failure\n");
- ok(error == ERROR_FILENAME_EXCED_RANGE, "expected ERROR_FILENAME_EXCED_RANGE got %u\n", error);
-
- volume[0] = '\0';
- ret = pGetVolumePathNameA(pathC1, volume, sizeof(volume));
- ok(ret, "expected success\n");
- ok(!strcmp(expected, volume), "expected name '%s', returned '%s'\n", pathC1, volume);
-
- pathC1[0] = tolower(pathC1[0]);
- volume[0] = '\0';
- ret = pGetVolumePathNameA(pathC1, volume, sizeof(volume));
- ok(ret, "expected success\n");
- ok(!strcmp(expected, volume) || broken(!strcasecmp(expected, volume)) /* <=XP */,
- "expected name '%s', returned '%s'\n", expected, volume);
-
- volume[0] = '\0';
- ret = pGetVolumePathNameA(pathC2, volume, sizeof(volume));
- ok(ret, "expected success\n");
- ok(!strcmp(expected, volume), "expected name '%s', returned '%s'\n", expected, volume);
-
- /* test an invalid path */
- SetLastError( 0xdeadbeef );
- ret = pGetVolumePathNameA("\\\\$$$", volume, 1);
- error = GetLastError();
- ok(!ret, "expected failure\n");
- ok(error == ERROR_INVALID_NAME || broken(ERROR_FILENAME_EXCED_RANGE) /* <=2000 */,
- "expected ERROR_INVALID_NAME got %u\n", error);
+ if(ret)
+ {
+ /* If we succeeded then make sure the path is correct */
+ success = (strcmp( volume_path, test_paths[i].path_name ) == 0)
+ || broken(strcasecmp( volume_path, test_paths[i].path_name ) == 0) /* XP */;
+ ok(success, "GetVolumePathName test %d unexpectedly returned path %s (expected %s).\n",
+ i, volume_path, test_paths[i].path_name);
+ }
+ else
+ {
+ /* On success Windows always returns ERROR_MORE_DATA, so only worry about failure */
+ success = (error == test_paths[i].error);
+ ok(success, "GetVolumePathName test %d unexpectedly returned error 0x%x (expected 0x%x).\n",
+ i, error, test_paths[i].error);
+ }
+ }
}
static void test_GetVolumePathNamesForVolumeNameA(void)
--
1.7.9.5

View File

@ -0,0 +1,51 @@
From 0ca1995e881b0951348ddfb4ab702f7cc3746549 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Sat, 25 Jan 2014 09:54:39 -0700
Subject: kernel32: Add a bunch more GetVolumePathName tests.
---
dlls/kernel32/tests/volume.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/dlls/kernel32/tests/volume.c b/dlls/kernel32/tests/volume.c
index 7f68dd0..9159cad 100644
--- a/dlls/kernel32/tests/volume.c
+++ b/dlls/kernel32/tests/volume.c
@@ -635,6 +635,34 @@ static void test_GetVolumePathNameA(void)
"\\\\$$$", "C:\\", 1,
ERROR_INVALID_NAME, ERROR_FILENAME_EXCED_RANGE
},
+ { /* test 9: a reasonable DOS path that is guaranteed to exist */
+ "C:\\windows\\system32", "C:\\", sizeof(volume_path),
+ NO_ERROR, NO_ERROR
+ },
+ { /* test 10: a reasonable DOS path that shouldn't exist */
+ "C:\\windows\\system32\\AnInvalidFolder", "C:\\", sizeof(volume_path),
+ NO_ERROR, NO_ERROR
+ },
+ { /* test 11: an unreasonable DOS path */
+ "InvalidDrive:\\AnInvalidFolder", "C:\\", sizeof(volume_path),
+ NO_ERROR, NO_ERROR
+ },
+ { /* test 12: a reasonable NT-converted DOS path that shouldn't exist */
+ "\\\\?\\C:\\AnInvalidFolder", "\\\\?\\C:\\", sizeof(volume_path),
+ NO_ERROR, NO_ERROR
+ },
+ { /* test 13: an unreasonable NT-converted DOS path */
+ "\\\\?\\InvalidDrive:\\AnInvalidFolder", "", sizeof(volume_path),
+ ERROR_INVALID_NAME, NO_ERROR
+ },
+ { /* test 14: an unreasonable NT volume path */
+ "\\\\?\\Volume{00000000-00-0000-0000-000000000000}\\AnInvalidFolder", "", sizeof(volume_path),
+ ERROR_INVALID_NAME, NO_ERROR
+ },
+ { /* test 15: an unreasonable NT-ish path */
+ "\\\\ReallyBogus\\InvalidDrive:\\AnInvalidFolder", "", sizeof(volume_path),
+ ERROR_INVALID_NAME, NO_ERROR
+ },
};
BOOL ret, success;
DWORD error;
--
1.7.9.5

View File

@ -0,0 +1,3 @@
Revision: 1
Author: Erich E. Hoover
Title: Implement GetVolumePathName.

View File

@ -33,7 +33,7 @@ diff --git a/libs/wine/config.c b/libs/wine/config.c
index a273502..5fa0cd5 100644
--- a/libs/wine/config.c
+++ b/libs/wine/config.c
@@ -478,6 +478,35 @@ const char *wine_get_version(void)
@@ -478,6 +478,36 @@ const char *wine_get_version(void)
return PACKAGE_VERSION;
}
@ -52,6 +52,7 @@ index a273502..5fa0cd5 100644
+ { "29b2af38-7edd-11e3-a08d-0090f5c75ad5:1", "Erich E. Hoover", "Add support for security access parameters for named pipes." },
+ { "4cd13e94-7f2d-11e3-b5eb-0090f5c75ad5:1", "Erich E. Hoover", "Support for junction points/reparse points." },
+ { "5fb1f5c8-7f17-11e3-9b62-0090f5c75ad5:1", "Erich E. Hoover", "Implement TransmitFile." },
+ { "fbea4ef6-85ac-4524-b32d-fc9882b73e5a:1", "Erich E. Hoover", "Implement GetVolumePathName." },
+ { "0b21d7ac-0387-4493-aa38-fbafe3e749f5:1", "Michael Müller", "Decrease minimum SetTimer interval from 15 to 5 ms." },
+ { "19835498-8d90-4673-867e-2376af4d7c76:1", "Sebastian Lackner", "Allow to set wined3d strictDrawOrdering via environment variable." },
+ { "59bd38b7-bbdc-4cfd-9ccd-1c72c4ed84c0:1", "Sebastian Lackner", "Implement X11DRV_FLUSH_GDI_DISPLAY ExtEscape command." },