From dd5b1514b2d1a5fe1c3443eab216bd25471556f2 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Sat, 25 Jan 2014 10:08:49 -0700 Subject: [PATCH] Implement GetVolumePathName. --- ...kernel32-Implement-GetVolumePathName.patch | 199 ++++++++++++++++++ ...-GetVolumePathName-tests-into-a-list.patch | 170 +++++++++++++++ ...a-bunch-more-GetVolumePathName-tests.patch | 51 +++++ .../fbea4ef6-85ac-4524-b32d-fc9882b73e5a.def | 3 + patches/patch-list.patch | 3 +- 5 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 patches/10-GetVolumePathName/0001-kernel32-Implement-GetVolumePathName.patch create mode 100644 patches/10-GetVolumePathName/0002-kernel32-Convert-GetVolumePathName-tests-into-a-list.patch create mode 100644 patches/10-GetVolumePathName/0003-kernel32-Add-a-bunch-more-GetVolumePathName-tests.patch create mode 100644 patches/10-GetVolumePathName/fbea4ef6-85ac-4524-b32d-fc9882b73e5a.def diff --git a/patches/10-GetVolumePathName/0001-kernel32-Implement-GetVolumePathName.patch b/patches/10-GetVolumePathName/0001-kernel32-Implement-GetVolumePathName.patch new file mode 100644 index 00000000..b7a2a2f2 --- /dev/null +++ b/patches/10-GetVolumePathName/0001-kernel32-Implement-GetVolumePathName.patch @@ -0,0 +1,199 @@ +From bcf395630fde511be0606ad8292385f37d127969 Mon Sep 17 00:00:00 2001 +From: "Erich E. Hoover" +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 + diff --git a/patches/10-GetVolumePathName/0002-kernel32-Convert-GetVolumePathName-tests-into-a-list.patch b/patches/10-GetVolumePathName/0002-kernel32-Convert-GetVolumePathName-tests-into-a-list.patch new file mode 100644 index 00000000..17f9ee44 --- /dev/null +++ b/patches/10-GetVolumePathName/0002-kernel32-Convert-GetVolumePathName-tests-into-a-list.patch @@ -0,0 +1,170 @@ +From 9d3403bbcab0fbfb465a224fae93ebbc9e442e35 Mon Sep 17 00:00:00 2001 +From: "Erich E. Hoover" +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 +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 + diff --git a/patches/10-GetVolumePathName/fbea4ef6-85ac-4524-b32d-fc9882b73e5a.def b/patches/10-GetVolumePathName/fbea4ef6-85ac-4524-b32d-fc9882b73e5a.def new file mode 100644 index 00000000..d627a25a --- /dev/null +++ b/patches/10-GetVolumePathName/fbea4ef6-85ac-4524-b32d-fc9882b73e5a.def @@ -0,0 +1,3 @@ +Revision: 1 +Author: Erich E. Hoover +Title: Implement GetVolumePathName. diff --git a/patches/patch-list.patch b/patches/patch-list.patch index 677d5185..7293989f 100644 --- a/patches/patch-list.patch +++ b/patches/patch-list.patch @@ -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." },