diff --git a/glib/gstdio-private.c b/glib/gstdio-private.c new file mode 100644 index 000000000..5eaaf09c5 --- /dev/null +++ b/glib/gstdio-private.c @@ -0,0 +1,77 @@ +/* gstdio-private.c - private glib functions for gstdio.c + * + * Copyright 2004 Tor Lillqvist + * Copyright 2018 Руслан Ижбулатов + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see . + */ + +/* Strips "\\\\?\\" extended prefix or + * "\\??\\" NT Object Manager prefix from + * @str in-place, using memmove. + * @str_size must point to the size of @str + * in gunichar2s, including NUL-terminator + * (if @str is NUL-terminated; it doesn't have to be). + * On return @str_size will correctly reflect changes + * in @str size (if any). + * Returns TRUE if @str was modified. + */ +static gboolean +_g_win32_strip_extended_ntobjm_prefix (gunichar2 *str, + gsize *str_size) +{ + const wchar_t *extended_prefix = L"\\\\?\\"; + const gsize extended_prefix_len = wcslen (extended_prefix); + const gsize extended_prefix_len_bytes = sizeof (gunichar2) * extended_prefix_len; + const gsize extended_prefix_with_drive_len_bytes = sizeof (gunichar2) * (extended_prefix_len + 2); + const wchar_t *ntobjm_prefix = L"\\??\\"; + const gsize ntobjm_prefix_len = wcslen (ntobjm_prefix); + const gsize ntobjm_prefix_len_bytes = sizeof (gunichar2) * ntobjm_prefix_len; + const gsize ntobjm_prefix_with_drive_len_bytes = sizeof (gunichar2) * (ntobjm_prefix_len + 2); + gboolean do_move = FALSE; + gsize move_shift = 0; + + if ((*str_size) * sizeof (gunichar2) > extended_prefix_with_drive_len_bytes && + memcmp (str, + extended_prefix, + extended_prefix_len_bytes) == 0 && + iswascii (str[extended_prefix_len]) && + iswalpha (str[extended_prefix_len]) && + str[extended_prefix_len + 1] == L':') + { + do_move = TRUE; + move_shift = extended_prefix_len; + } + else if ((*str_size) * sizeof (gunichar2) > ntobjm_prefix_with_drive_len_bytes && + memcmp (str, + ntobjm_prefix, + ntobjm_prefix_len_bytes) == 0 && + iswascii (str[ntobjm_prefix_len]) && + iswalpha (str[ntobjm_prefix_len]) && + str[ntobjm_prefix_len + 1] == L':') + { + do_move = TRUE; + move_shift = ntobjm_prefix_len; + } + + if (do_move) + { + *str_size -= move_shift; + memmove (str, + str + move_shift, + (*str_size) * sizeof (gunichar2)); + } + + return do_move; +} diff --git a/glib/gstdio.c b/glib/gstdio.c index ffbd37180..60266960a 100644 --- a/glib/gstdio.c +++ b/glib/gstdio.c @@ -121,6 +121,8 @@ w32_error_to_errno (DWORD error_code) } } +#include "gstdio-private.c" + static int _g_win32_stat_utf16_no_trailing_slashes (const gunichar2 *filename, int fd, @@ -259,15 +261,11 @@ _g_win32_stat_utf16_no_trailing_slashes (const gunichar2 *filename, if (new_len > 0) { - const wchar_t *extended_prefix = L"\\\\?\\"; - const gsize extended_prefix_len = wcslen (extended_prefix); - const gsize extended_prefix_len_bytes = sizeof (wchar_t) * extended_prefix_len; - /* Pretend that new_len doesn't count the terminating NUL char, - * and ask for a bit more space than is needed. + * and ask for a bit more space than is needed, and allocate even more. */ - filename_target_len = new_len + 5; - filename_target = g_malloc (filename_target_len * sizeof (wchar_t)); + filename_target_len = new_len + 3; + filename_target = g_malloc ((filename_target_len + 1) * sizeof (wchar_t)); new_len = GetFinalPathNameByHandleW (file_handle, filename_target, @@ -284,17 +282,32 @@ _g_win32_stat_utf16_no_trailing_slashes (const gunichar2 *filename, error_code = ERROR_BUFFER_OVERFLOW; g_clear_pointer (&filename_target, g_free); } - /* GetFinalPathNameByHandle() is documented to return extended paths, - * strip the extended prefix. - */ - else if (new_len > extended_prefix_len && - memcmp (filename_target, extended_prefix, extended_prefix_len_bytes) == 0) + else if (new_len == 0) { - new_len -= extended_prefix_len; - memmove (filename_target, - filename_target + extended_prefix_len, - (new_len + 1) * sizeof (wchar_t)); + g_clear_pointer (&filename_target, g_free); } + /* GetFinalPathNameByHandle() is documented to return extended paths, + * strip the extended prefix, if it is followed by a drive letter + * and a colon. Otherwise keep it (the path could be + * \\\\?\\Volume{GUID}\\ - it's only usable in extended form). + */ + else if (new_len > 0) + { + gsize len = new_len; + + /* Account for NUL-terminator maybe not being counted. + * This is why we overallocated earlier. + */ + if (filename_target[len] != L'\0') + { + len++; + filename_target[len] = L'\0'; + } + + _g_win32_strip_extended_ntobjm_prefix (filename_target, &len); + new_len = len; + } + } if (new_len == 0) @@ -513,10 +526,8 @@ _g_win32_readlink_utf16 (const gunichar2 *filename, gunichar2 *buf, gsize buf_size) { - const wchar_t *ntobjm_prefix = L"\\??\\"; - const gsize ntobjm_prefix_len_unichar2 = wcslen (ntobjm_prefix); - const gsize ntobjm_prefix_len_bytes = sizeof (gunichar2) * ntobjm_prefix_len_unichar2; - int result = _g_win32_readlink_utf16_raw (filename, buf, buf_size); + int result = _g_win32_readlink_utf16_raw (filename, buf, buf_size); + gsize string_size; if (result <= 0) return result; @@ -532,16 +543,16 @@ _g_win32_readlink_utf16 (const gunichar2 *filename, /* DeviceIoControl () tends to return filenames as NT Object Manager * names , i.e. "\\??\\C:\\foo\\bar". * Remove the leading 4-byte \??\ prefix, as glib (as well as many W32 API - * functions) is unprepared to deal with it. + * functions) is unprepared to deal with it. Unless it has no 'x:' drive + * letter part after the prefix, in which case we leave everything + * as-is, because the path could be "\??\Volume{GUID}" - stripping + * the prefix will allow it to be confused with relative links + * targeting "Volume{GUID}". */ - if (result > ntobjm_prefix_len_bytes && - memcmp (buf, ntobjm_prefix, ntobjm_prefix_len_bytes) == 0) - { - result -= ntobjm_prefix_len_bytes; - memmove (buf, buf + ntobjm_prefix_len_unichar2, result); - } + string_size = result / sizeof (gunichar2); + _g_win32_strip_extended_ntobjm_prefix (buf, &string_size); - return result; + return string_size * sizeof (gunichar2); } static gchar *