Merge branch 'w32-gfileinfo-improvements' into 'master'

W32 GFileInfo improvements

See merge request GNOME/glib!238
This commit is contained in:
LRN 2018-09-12 15:15:00 +00:00
commit cf4ea5ef75
8 changed files with 380 additions and 58 deletions

View File

@ -85,6 +85,8 @@
#define G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT (7340032 + 10)
#define G_FILE_ATTRIBUTE_ID_DOS_IS_ARCHIVE (8388608 + 1)
#define G_FILE_ATTRIBUTE_ID_DOS_IS_SYSTEM (8388608 + 2)
#define G_FILE_ATTRIBUTE_ID_DOS_IS_MOUNTPOINT (8388608 + 3)
#define G_FILE_ATTRIBUTE_ID_DOS_REPARSE_POINT_TAG (8388608 + 4)
#define G_FILE_ATTRIBUTE_ID_OWNER_USER (9437184 + 1)
#define G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL (9437184 + 2)
#define G_FILE_ATTRIBUTE_ID_OWNER_GROUP (9437184 + 3)

View File

@ -245,6 +245,8 @@ ensure_attribute_hash (void)
REGISTER_ATTRIBUTE (UNIX_IS_MOUNTPOINT);
REGISTER_ATTRIBUTE (DOS_IS_ARCHIVE);
REGISTER_ATTRIBUTE (DOS_IS_SYSTEM);
REGISTER_ATTRIBUTE (DOS_IS_MOUNTPOINT);
REGISTER_ATTRIBUTE (DOS_REPARSE_POINT_TAG);
REGISTER_ATTRIBUTE (OWNER_USER);
REGISTER_ATTRIBUTE (OWNER_USER_REAL);
REGISTER_ATTRIBUTE (OWNER_GROUP);

View File

@ -672,6 +672,33 @@ typedef struct _GFileInfoClass GFileInfoClass;
**/
#define G_FILE_ATTRIBUTE_DOS_IS_SYSTEM "dos::is-system" /* boolean */
/**
* G_FILE_ATTRIBUTE_DOS_IS_MOUNTPOINT:
*
* A key in the "dos" namespace for checking if the file is a NTFS mount point
* (a volume mount or a junction point).
* This attribute is %TRUE if file is a reparse point of type
* [IO_REPARSE_TAG_MOUNT_POINT](https://msdn.microsoft.com/en-us/library/dd541667.aspx).
* This attribute is only available for DOS file systems.
* Corresponding #GFileAttributeType is %G_FILE_ATTRIBUTE_TYPE_BOOLEAN.
*
* Since: 2.60
**/
#define G_FILE_ATTRIBUTE_DOS_IS_MOUNTPOINT "dos::is-mountpoint" /* boolean */
/**
* G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG:
*
* A key in the "dos" namespace for getting the file NTFS reparse tag.
* This value is 0 for files that are not reparse points.
* See the [Reparse Tags](https://msdn.microsoft.com/en-us/library/dd541667.aspx)
* page for possible reparse tag values. Corresponding #GFileAttributeType
* is %G_FILE_ATTRIBUTE_TYPE_UINT32.
*
* Since: 2.60
**/
#define G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG "dos::reparse-point-tag" /* uint32 */
/* Owner attributes */
/**

View File

@ -1867,6 +1867,12 @@ _g_local_file_info_get (const char *basename,
if (statbuf.attributes & FILE_ATTRIBUTE_SYSTEM)
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_SYSTEM, TRUE);
if (statbuf.reparse_tag == IO_REPARSE_TAG_MOUNT_POINT)
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_MOUNTPOINT, TRUE);
if (statbuf.reparse_tag != 0)
_g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_REPARSE_POINT_TAG, statbuf.reparse_tag);
#endif
symlink_target = NULL;

View File

@ -180,16 +180,24 @@ test_internal_enhanced_stdio (void)
gchar *programdata;
gchar *users_dir;
gchar *allusers;
GFile *gf_programdata, *gf_allusers;
GFileInfo *fi_programdata, *fi_allusers, *fi_allusers_target;
gchar *commondata;
GFile *gf_programdata, *gf_allusers, *gf_commondata;
GFileInfo *fi_programdata, *fi_allusers, *fi_allusers_target, *fi_commondata, *fi_commondata_target;
GFileType ft_allusers;
GFileType ft_allusers_target;
GFileType ft_programdata;
GFileType ft_commondata;
gboolean allusers_is_symlink;
gboolean commondata_is_symlink;
gboolean commondata_is_mount_point;
guint32 allusers_reparse_tag;
guint32 commondata_reparse_tag;
const gchar *id_allusers;
const gchar *id_allusers_target;
const gchar *id_commondata_target;
const gchar *id_programdata;
const gchar *allusers_target;
const gchar *commondata_target;
/* C:/ProgramData */
programdata = g_utf16_to_utf8 (programdata_dir_w, -1, NULL, NULL, NULL);
@ -201,7 +209,11 @@ test_internal_enhanced_stdio (void)
* for "C:/ProgramData".
*/
allusers = g_build_filename (users_dir, "All Users", NULL);
g_assert_nonnull (allusers);
/* "C:/Users/All Users/Application Data" is a known
* junction for "C:/ProgramData"
*/
commondata = g_build_filename (allusers, "Application Data", NULL);
/* We don't test g_stat() and g_lstat() on these directories,
* because it is pointless - there's no way to tell that these
@ -213,6 +225,7 @@ test_internal_enhanced_stdio (void)
*/
gf_programdata = g_file_new_for_path (programdata);
gf_allusers = g_file_new_for_path (allusers);
gf_commondata = g_file_new_for_path (commondata);
fi_programdata = g_file_query_info (gf_programdata,
G_FILE_ATTRIBUTE_ID_FILE ","
@ -229,52 +242,99 @@ test_internal_enhanced_stdio (void)
fi_allusers = g_file_query_info (gf_allusers,
G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG ","
G_FILE_ATTRIBUTE_ID_FILE ","
G_FILE_ATTRIBUTE_STANDARD_TYPE,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
NULL, NULL);
g_assert (g_file_info_has_attribute (fi_programdata, G_FILE_ATTRIBUTE_ID_FILE));
g_assert (g_file_info_has_attribute (fi_programdata, G_FILE_ATTRIBUTE_STANDARD_TYPE));
fi_commondata = g_file_query_info (gf_commondata,
G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
G_FILE_ATTRIBUTE_DOS_IS_MOUNTPOINT ","
G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG ","
G_FILE_ATTRIBUTE_ID_FILE ","
G_FILE_ATTRIBUTE_STANDARD_TYPE,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
NULL, NULL);
g_assert (g_file_info_has_attribute (fi_allusers_target, G_FILE_ATTRIBUTE_ID_FILE));
g_assert (g_file_info_has_attribute (fi_allusers_target, G_FILE_ATTRIBUTE_STANDARD_TYPE));
fi_commondata_target = g_file_query_info (gf_commondata,
G_FILE_ATTRIBUTE_ID_FILE ","
G_FILE_ATTRIBUTE_STANDARD_TYPE,
G_FILE_QUERY_INFO_NONE,
NULL, NULL);
g_assert (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_ID_FILE));
g_assert (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_TYPE));
g_assert (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK));
g_assert (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET));
g_assert_true (g_file_info_has_attribute (fi_programdata, G_FILE_ATTRIBUTE_ID_FILE));
g_assert_true (g_file_info_has_attribute (fi_programdata, G_FILE_ATTRIBUTE_STANDARD_TYPE));
g_assert_true (g_file_info_has_attribute (fi_allusers_target, G_FILE_ATTRIBUTE_ID_FILE));
g_assert_true (g_file_info_has_attribute (fi_allusers_target, G_FILE_ATTRIBUTE_STANDARD_TYPE));
g_assert_true (g_file_info_has_attribute (fi_commondata_target, G_FILE_ATTRIBUTE_ID_FILE));
g_assert_true (g_file_info_has_attribute (fi_commondata_target, G_FILE_ATTRIBUTE_STANDARD_TYPE));
g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_ID_FILE));
g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_TYPE));
g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK));
g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG));
g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET));
g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_ID_FILE));
g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_STANDARD_TYPE));
g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK));
g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_DOS_IS_MOUNTPOINT));
g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG));
g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET));
ft_allusers = g_file_info_get_file_type (fi_allusers);
ft_allusers_target = g_file_info_get_file_type (fi_allusers_target);
ft_programdata = g_file_info_get_file_type (fi_programdata);
ft_commondata = g_file_info_get_file_type (fi_commondata);
g_assert (ft_allusers == G_FILE_TYPE_SYMBOLIC_LINK);
g_assert (ft_allusers_target == G_FILE_TYPE_DIRECTORY);
g_assert (ft_programdata == G_FILE_TYPE_DIRECTORY);
g_assert_cmpint (ft_allusers, ==, G_FILE_TYPE_SYMBOLIC_LINK);
g_assert_cmpint (ft_allusers_target, ==, G_FILE_TYPE_DIRECTORY);
g_assert_cmpint (ft_programdata, ==, G_FILE_TYPE_DIRECTORY);
g_assert_cmpint (ft_commondata, ==, G_FILE_TYPE_SYMBOLIC_LINK);
allusers_is_symlink = g_file_info_get_attribute_boolean (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK);
allusers_reparse_tag = g_file_info_get_attribute_uint32 (fi_allusers, G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG);
commondata_is_symlink = g_file_info_get_attribute_boolean (fi_commondata, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK);
commondata_is_mount_point = g_file_info_get_attribute_boolean (fi_commondata, G_FILE_ATTRIBUTE_DOS_IS_MOUNTPOINT);
commondata_reparse_tag = g_file_info_get_attribute_uint32 (fi_commondata, G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG);
g_assert_true (allusers_is_symlink);
g_assert_cmpuint (allusers_reparse_tag, ==, IO_REPARSE_TAG_SYMLINK);
g_assert_true (commondata_is_symlink);
g_assert_true (commondata_is_mount_point);
g_assert_cmpuint (commondata_reparse_tag, ==, IO_REPARSE_TAG_MOUNT_POINT);
id_allusers = g_file_info_get_attribute_string (fi_allusers, G_FILE_ATTRIBUTE_ID_FILE);
id_allusers_target = g_file_info_get_attribute_string (fi_allusers_target, G_FILE_ATTRIBUTE_ID_FILE);
id_commondata_target = g_file_info_get_attribute_string (fi_commondata_target, G_FILE_ATTRIBUTE_ID_FILE);
id_programdata = g_file_info_get_attribute_string (fi_programdata, G_FILE_ATTRIBUTE_ID_FILE);
g_assert_cmpstr (id_allusers_target, ==, id_programdata);
g_assert_cmpstr (id_commondata_target, ==, id_programdata);
g_assert_cmpstr (id_allusers, !=, id_programdata);
allusers_target = g_file_info_get_symlink_target (fi_allusers);
g_assert_true (g_str_has_suffix (allusers_target, "ProgramData"));
commondata_target = g_file_info_get_symlink_target (fi_commondata);
g_assert_true (g_str_has_suffix (commondata_target, "ProgramData"));
g_object_unref (fi_allusers);
g_object_unref (fi_allusers_target);
g_object_unref (fi_commondata);
g_object_unref (fi_commondata_target);
g_object_unref (fi_programdata);
g_object_unref (gf_allusers);
g_object_unref (gf_commondata);
g_object_unref (gf_programdata);
g_free (allusers);
g_free (commondata);
g_free (users_dir);
g_free (programdata);
}
@ -325,7 +385,7 @@ test_internal_enhanced_stdio (void)
g_assert_nonnull (f);
h = (HANDLE) _get_osfhandle (fileno (f));
g_assert (h != INVALID_HANDLE_VALUE);
g_assert_cmpuint ((guintptr) h, !=, (guintptr) INVALID_HANDLE_VALUE);
ssb.SetSparse = TRUE;
g_assert_true (DeviceIoControl (h,
@ -364,8 +424,8 @@ test_internal_enhanced_stdio (void)
g_remove (ps);
g_assert (g_file_info_has_attribute (fi_ps, G_FILE_ATTRIBUTE_STANDARD_SIZE));
g_assert (g_file_info_has_attribute (fi_ps, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE));
g_assert_true (g_file_info_has_attribute (fi_ps, G_FILE_ATTRIBUTE_STANDARD_SIZE));
g_assert_true (g_file_info_has_attribute (fi_ps, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE));
size_ps = g_file_info_get_attribute_uint64 (fi_ps, G_FILE_ATTRIBUTE_STANDARD_SIZE);
alsize_ps = g_file_info_get_attribute_uint64 (fi_ps, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE);
@ -404,7 +464,7 @@ test_internal_enhanced_stdio (void)
g_assert_nonnull (f);
h = (HANDLE) _get_osfhandle (fileno (f));
g_assert (h != INVALID_HANDLE_VALUE);
g_assert_cmpuint ((guintptr) h, !=, (guintptr) INVALID_HANDLE_VALUE);
fprintf (f, "1");
fflush (f);
@ -443,15 +503,15 @@ test_internal_enhanced_stdio (void)
G_FILE_QUERY_INFO_NONE,
NULL, NULL);
g_assert (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_STANDARD_SIZE));
g_assert (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE));
g_assert (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_ID_FILE));
g_assert (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED));
g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_STANDARD_SIZE));
g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE));
g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_ID_FILE));
g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED));
g_assert (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_STANDARD_SIZE));
g_assert (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE));
g_assert (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_ID_FILE));
g_assert (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_TIME_MODIFIED));
g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_STANDARD_SIZE));
g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE));
g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_ID_FILE));
g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_TIME_MODIFIED));
size_p0 = g_file_info_get_attribute_uint64 (fi_p0, G_FILE_ATTRIBUTE_STANDARD_SIZE);
alsize_p0 = g_file_info_get_attribute_uint64 (fi_p0, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE);
@ -469,7 +529,7 @@ test_internal_enhanced_stdio (void)
/* st_ino from W32 stat() is useless for file identification.
* It will be either 0, or it will be the same for both files.
*/
g_assert (statbuf_p0.st_ino == statbuf_p1.st_ino);
g_assert_cmpint (statbuf_p0.st_ino, ==, statbuf_p1.st_ino);
g_assert_cmpstr (id_p0, !=, id_p1);
time_p0 = g_file_info_get_attribute_uint64 (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED);
@ -481,7 +541,7 @@ test_internal_enhanced_stdio (void)
* and 64-bit on 64-bit Windows, usually),
* so it *can* pass this test in some cases.
*/
g_assert (time_p0 > G_GUINT64_CONSTANT (0xFFFFFFFF));
g_assert_cmpuint (time_p0, >, G_GUINT64_CONSTANT (0xFFFFFFFF));
g_object_unref (fi_p0);
g_object_unref (fi_p1);

77
glib/gstdio-private.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/* 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;
}

View File

@ -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)
@ -453,8 +466,9 @@ _g_win32_readlink_utf16_raw (const gunichar2 *filename,
* point and use DeviceIoControl() on it.
*/
h = CreateFileW (filename,
FILE_READ_ATTRIBUTES | SYNCHRONIZE | GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_READ_EA,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_OPEN_REPARSE_POINT
| (attributes & FILE_ATTRIBUTE_DIRECTORY ? FILE_FLAG_BACKUP_SEMANTICS : 0),
@ -513,10 +527,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 +544,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 *

View File

@ -1009,6 +1009,139 @@ test_fopen_modes (void)
g_free (path);
}
#ifdef G_OS_WIN32
#include "../gstdio-private.c"
static int
g_wcscmp0 (const gunichar2 *str1,
const gunichar2 *str2)
{
if (!str1)
return -(str1 != str2);
if (!str2)
return str1 != str2;
return wcscmp (str1, str2);
}
#define g_assert_cmpwcs(s1, cmp, s2, s1u8, s2u8) \
G_STMT_START { \
const gunichar2 *__s1 = (s1), *__s2 = (s2); \
if (g_wcscmp0 (__s1, __s2) cmp 0) ; else \
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
#s1u8 " " #cmp " " #s2u8, s1u8, #cmp, s2u8); \
} G_STMT_END
static void
test_win32_pathstrip (void)
{
gunichar2 *buf;
gsize i;
#define IDENTITY_TEST(x) { x, x, FALSE }
struct
{
gunichar2 *in;
gunichar2 *out;
gboolean result;
} testcases[] = {
IDENTITY_TEST (L"\\\\?\\V"),
IDENTITY_TEST (L"\\\\?\\Vo"),
IDENTITY_TEST (L"\\\\?\\Volume{0700f3d3-6d24-11e3-8b2f-806e6f6e6963}\\"),
IDENTITY_TEST (L"\\??\\V"),
IDENTITY_TEST (L"\\??\\Vo"),
IDENTITY_TEST (L"\\??\\Volume{0700f3d3-6d24-11e3-8b2f-806e6f6e6963}\\"),
IDENTITY_TEST (L"\\\\?\\\x0441:\\"),
IDENTITY_TEST (L"\\??\\\x0441:\\"),
IDENTITY_TEST (L"a:\\"),
IDENTITY_TEST (L"a:\\b\\c"),
IDENTITY_TEST (L"x"),
#undef IDENTITY_TEST
{
L"\\\\?\\c:\\",
L"c:\\",
TRUE,
},
{
L"\\\\?\\C:\\",
L"C:\\",
TRUE,
},
{
L"\\\\?\\c:\\",
L"c:\\",
TRUE,
},
{
L"\\\\?\\C:\\",
L"C:\\",
TRUE,
},
{
L"\\\\?\\C:\\",
L"C:\\",
TRUE,
},
{ 0, }
};
for (i = 0; testcases[i].in; i++)
{
gsize str_len = wcslen (testcases[i].in) + 1;
gchar *in_u8 = g_utf16_to_utf8 (testcases[i].in, -1, NULL, NULL, NULL);
gchar *out_u8 = g_utf16_to_utf8 (testcases[i].out, -1, NULL, NULL, NULL);
g_assert_nonnull (in_u8);
g_assert_nonnull (out_u8);
buf = g_new0 (gunichar2, str_len);
memcpy (buf, testcases[i].in, str_len * sizeof (gunichar2));
_g_win32_strip_extended_ntobjm_prefix (buf, &str_len);
g_assert_cmpwcs (buf, ==, testcases[i].out, in_u8, out_u8);
g_free (buf);
g_free (in_u8);
g_free (out_u8);
}
/* Check for correct behaviour on non-NUL-terminated strings */
for (i = 0; testcases[i].in; i++)
{
gsize str_len = wcslen (testcases[i].in) + 1;
wchar_t old_endchar;
gchar *in_u8 = g_utf16_to_utf8 (testcases[i].in, -1, NULL, NULL, NULL);
gchar *out_u8 = g_utf16_to_utf8 (testcases[i].out, -1, NULL, NULL, NULL);
g_assert_nonnull (in_u8);
g_assert_nonnull (out_u8);
buf = g_new0 (gunichar2, str_len);
memcpy (buf, testcases[i].in, (str_len) * sizeof (gunichar2));
old_endchar = buf[wcslen (testcases[i].out)];
str_len -= 1;
if (testcases[i].result)
{
/* Given "\\\\?\\C:\\" (len 7, unterminated),
* we should get "C:\\" (len 3, unterminated).
* Put a character different from "\\" (4-th character of the buffer)
* at the end of the unterminated source buffer, into a position
* where NUL-terminator would normally be. Then later test that 4-th character
* in the buffer is still the old "\\".
* After that terminate the string and use normal g_wcscmp0().
*/
buf[str_len] = old_endchar - 1;
}
_g_win32_strip_extended_ntobjm_prefix (buf, &str_len);
g_assert_cmpuint (old_endchar, ==, buf[wcslen (testcases[i].out)]);
buf[str_len] = L'\0';
g_assert_cmpwcs (buf, ==, testcases[i].out, in_u8, out_u8);
g_free (buf);
g_free (in_u8);
g_free (out_u8);
}
}
#endif
int
main (int argc,
char *argv[])
@ -1018,6 +1151,9 @@ main (int argc,
g_test_bug_base ("https://gitlab.gnome.org/GNOME/glib/merge_requests/");
#ifdef G_OS_WIN32
g_test_add_func ("/fileutils/stdio-win32-pathstrip", test_win32_pathstrip);
#endif
g_test_add_func ("/fileutils/build-path", test_build_path);
g_test_add_func ("/fileutils/build-pathv", test_build_pathv);
g_test_add_func ("/fileutils/build-filename", test_build_filename);