mirror of
https://github.com/transmission/transmission
synced 2025-02-22 14:10:34 +00:00
Fix UNC paths resolution on Windows
While resolved paths always contain the `\\?\` prefix, it's not always correct to strip only those 4 chars. In case of UNC paths, the prefix is actually a bit longer (`\\?\UNC\`) and needs to be replaced with `\\` instead. Failing to do so results in invalid paths, e.g. `\\Host\Share\File` becomes `UNC\Host\Share\File` which totally wrong.
This commit is contained in:
parent
6da6629887
commit
3106675261
3 changed files with 55 additions and 11 deletions
|
@ -34,6 +34,9 @@ struct tr_sys_dir_win32
|
|||
char* utf8_name;
|
||||
};
|
||||
|
||||
static wchar_t const native_local_path_prefix[] = { '\\', '\\', '?', '\\' };
|
||||
static wchar_t const native_unc_path_prefix[] = { '\\', '\\', '?', '\\', 'U', 'N', 'C', '\\' };
|
||||
|
||||
static void set_system_error(tr_error** error, DWORD code)
|
||||
{
|
||||
char* message;
|
||||
|
@ -146,14 +149,12 @@ static wchar_t* path_to_native_path_ex(char const* path, int extra_chars_after,
|
|||
/* Extending maximum path length limit up to ~32K. See "Naming Files, Paths, and Namespaces"
|
||||
(https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx) for more info */
|
||||
|
||||
wchar_t const local_prefix[] = { '\\', '\\', '?', '\\' };
|
||||
wchar_t const unc_prefix[] = { '\\', '\\', '?', '\\', 'U', 'N', 'C', '\\' };
|
||||
|
||||
bool const is_relative = tr_sys_path_is_relative(path);
|
||||
bool const is_unc = is_unc_path(path);
|
||||
|
||||
/* `-2` for UNC since we overwrite existing prefix slashes */
|
||||
int const extra_chars_before = is_relative ? 0 : (is_unc ? TR_N_ELEMENTS(unc_prefix) - 2 : TR_N_ELEMENTS(local_prefix));
|
||||
int const extra_chars_before = is_relative ? 0 : (is_unc ? TR_N_ELEMENTS(native_unc_path_prefix) - 2 :
|
||||
TR_N_ELEMENTS(native_local_path_prefix));
|
||||
|
||||
/* TODO (?): TR_ASSERT(!is_relative); */
|
||||
|
||||
|
@ -171,12 +172,12 @@ static wchar_t* path_to_native_path_ex(char const* path, int extra_chars_after,
|
|||
if (is_unc)
|
||||
{
|
||||
/* UNC path: "\\server\share" -> "\\?\UNC\server\share" */
|
||||
memcpy(wide_path, unc_prefix, sizeof(unc_prefix));
|
||||
memcpy(wide_path, native_unc_path_prefix, sizeof(native_unc_path_prefix));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Local path: "C:" -> "\\?\C:" */
|
||||
memcpy(wide_path, local_prefix, sizeof(local_prefix));
|
||||
memcpy(wide_path, native_local_path_prefix, sizeof(native_local_path_prefix));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,6 +202,30 @@ static wchar_t* path_to_native_path(char const* path)
|
|||
return path_to_native_path_ex(path, 0, NULL);
|
||||
}
|
||||
|
||||
static char* native_path_to_path(wchar_t const* wide_path)
|
||||
{
|
||||
if (wide_path == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool const is_unc = wcsncmp(wide_path, native_unc_path_prefix, TR_N_ELEMENTS(native_unc_path_prefix)) == 0;
|
||||
bool const is_local = !is_unc && wcsncmp(wide_path, native_local_path_prefix, TR_N_ELEMENTS(native_local_path_prefix)) == 0;
|
||||
|
||||
size_t const skip_chars = is_unc ? TR_N_ELEMENTS(native_unc_path_prefix) :
|
||||
(is_local ? TR_N_ELEMENTS(native_local_path_prefix) : 0);
|
||||
|
||||
char* const path = tr_win32_native_to_utf8_ex(wide_path + skip_chars, -1, is_unc ? 2 : 0, 0, NULL);
|
||||
|
||||
if (is_unc && path != NULL)
|
||||
{
|
||||
path[0] = '\\';
|
||||
path[1] = '\\';
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static tr_sys_file_t open_file(char const* path, DWORD access, DWORD disposition, DWORD flags, tr_error** error)
|
||||
{
|
||||
TR_ASSERT(path != NULL);
|
||||
|
@ -541,8 +566,9 @@ char* tr_sys_path_resolve(char const* path, tr_error** error)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
/* Resolved path always begins with "\\?\", so skip those first four chars. */
|
||||
ret = tr_win32_native_to_utf8(wide_ret + 4, -1);
|
||||
TR_ASSERT(wcsncmp(wide_ret, L"\\\\?\\", 4) == 0);
|
||||
|
||||
ret = native_path_to_path(wide_ret);
|
||||
|
||||
if (ret != NULL)
|
||||
{
|
||||
|
|
|
@ -1236,10 +1236,21 @@ char* tr_utf8clean(char const* str, size_t max_len)
|
|||
#ifdef _WIN32
|
||||
|
||||
char* tr_win32_native_to_utf8(wchar_t const* text, int text_size)
|
||||
{
|
||||
return tr_win32_native_to_utf8_ex(text, text_size, 0, 0, NULL);
|
||||
}
|
||||
|
||||
char* tr_win32_native_to_utf8_ex(wchar_t const* text, int text_size, int extra_chars_before, int extra_chars_after,
|
||||
int* real_result_size)
|
||||
{
|
||||
char* ret = NULL;
|
||||
int size;
|
||||
|
||||
if (text_size == -1)
|
||||
{
|
||||
text_size = wcslen(text);
|
||||
}
|
||||
|
||||
size = WideCharToMultiByte(CP_UTF8, 0, text, text_size, NULL, 0, NULL, NULL);
|
||||
|
||||
if (size == 0)
|
||||
|
@ -1247,15 +1258,20 @@ char* tr_win32_native_to_utf8(wchar_t const* text, int text_size)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
ret = tr_new(char, size + 1);
|
||||
size = WideCharToMultiByte(CP_UTF8, 0, text, text_size, ret, size, NULL, NULL);
|
||||
ret = tr_new(char, size + extra_chars_before + extra_chars_after + 1);
|
||||
size = WideCharToMultiByte(CP_UTF8, 0, text, text_size, ret + extra_chars_before, size, NULL, NULL);
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret[size] = '\0';
|
||||
ret[size + extra_chars_before + extra_chars_after] = '\0';
|
||||
|
||||
if (real_result_size != NULL)
|
||||
{
|
||||
*real_result_size = size;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -115,6 +115,8 @@ char* tr_utf8clean(char const* str, size_t len) TR_GNUC_MALLOC;
|
|||
#ifdef _WIN32
|
||||
|
||||
char* tr_win32_native_to_utf8(wchar_t const* text, int text_size);
|
||||
char* tr_win32_native_to_utf8_ex(wchar_t const* text, int text_size, int extra_chars_before, int extra_chars_after,
|
||||
int* real_result_size);
|
||||
wchar_t* tr_win32_utf8_to_native(char const* text, int text_size);
|
||||
wchar_t* tr_win32_utf8_to_native_ex(char const* text, int text_size, int extra_chars_before, int extra_chars_after,
|
||||
int* real_result_size);
|
||||
|
|
Loading…
Reference in a new issue