mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2024-11-21 16:46:54 -08:00
200 lines
7.3 KiB
Diff
200 lines
7.3 KiB
Diff
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
|
|
|