mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-09 19:06:15 +01:00
Merge branch 'win32-symlink-refactoring' into 'master'
Win32 symlink code refactoring See merge request GNOME/glib!269
This commit is contained in:
commit
df62731771
@ -76,6 +76,7 @@ typedef struct _GFileInfoClass GFileInfoClass;
|
||||
* A key in the "standard" namespace for checking if the file is a symlink.
|
||||
* Typically the actual type is something else, if we followed the symlink
|
||||
* to get the type.
|
||||
* On Windows NTFS mountpoints are considered to be symlinks as well.
|
||||
* Corresponding #GFileAttributeType is %G_FILE_ATTRIBUTE_TYPE_BOOLEAN.
|
||||
**/
|
||||
#define G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK "standard::is-symlink" /* boolean */
|
||||
|
@ -361,6 +361,15 @@ typedef enum {
|
||||
* @G_FILE_TYPE_MOUNTABLE: File is a mountable location.
|
||||
*
|
||||
* Indicates the file's on-disk type.
|
||||
*
|
||||
* On Windows systems a file will never have %G_FILE_TYPE_SYMBOLIC_LINK type;
|
||||
* use #GFileInfo and %G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK to determine
|
||||
* whether a file is a symlink or not. This is due to the fact that NTFS does
|
||||
* not have a single filesystem object type for symbolic links - it has
|
||||
* files that symlink to files, and directories that symlink to directories.
|
||||
* #GFileType enumeration cannot precisely represent this important distinction,
|
||||
* which is why all Windows symlinks will continue to be reported as
|
||||
* %G_FILE_TYPE_REGULAR or %G_FILE_TYPE_DIRECTORY.
|
||||
**/
|
||||
typedef enum {
|
||||
G_FILE_TYPE_UNKNOWN = 0,
|
||||
|
@ -161,7 +161,7 @@ _g_local_file_info_create_fs_id (GLocalFileStat *statbuf)
|
||||
static gchar *
|
||||
read_link (const gchar *full_name)
|
||||
{
|
||||
#if defined (HAVE_READLINK) || defined (G_OS_WIN32)
|
||||
#if defined (HAVE_READLINK)
|
||||
gchar *buffer;
|
||||
guint size;
|
||||
|
||||
@ -171,12 +171,8 @@ read_link (const gchar *full_name)
|
||||
while (1)
|
||||
{
|
||||
int read_size;
|
||||
|
||||
#ifndef G_OS_WIN32
|
||||
|
||||
read_size = readlink (full_name, buffer, size);
|
||||
#else
|
||||
read_size = GLIB_PRIVATE_CALL (g_win32_readlink_utf8) (full_name, buffer, size);
|
||||
#endif
|
||||
if (read_size < 0)
|
||||
{
|
||||
g_free (buffer);
|
||||
@ -190,6 +186,17 @@ read_link (const gchar *full_name)
|
||||
size *= 2;
|
||||
buffer = g_realloc (buffer, size);
|
||||
}
|
||||
#elif defined (G_OS_WIN32)
|
||||
gchar *buffer;
|
||||
int read_size;
|
||||
|
||||
read_size = GLIB_PRIVATE_CALL (g_win32_readlink_utf8) (full_name, NULL, 0, &buffer, TRUE);
|
||||
if (read_size < 0)
|
||||
return NULL;
|
||||
else if (read_size == 0)
|
||||
return strdup ("");
|
||||
else
|
||||
return buffer;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
@ -957,8 +964,8 @@ set_info_from_stat (GFileInfo *info,
|
||||
else if (S_ISLNK (statbuf->st_mode))
|
||||
file_type = G_FILE_TYPE_SYMBOLIC_LINK;
|
||||
#elif defined (G_OS_WIN32)
|
||||
if (statbuf->reparse_tag == IO_REPARSE_TAG_SYMLINK ||
|
||||
statbuf->reparse_tag == IO_REPARSE_TAG_MOUNT_POINT)
|
||||
else if (statbuf->reparse_tag == IO_REPARSE_TAG_SYMLINK ||
|
||||
statbuf->reparse_tag == IO_REPARSE_TAG_MOUNT_POINT)
|
||||
file_type = G_FILE_TYPE_SYMBOLIC_LINK;
|
||||
#endif
|
||||
|
||||
@ -966,15 +973,17 @@ set_info_from_stat (GFileInfo *info,
|
||||
g_file_info_set_size (info, statbuf->st_size);
|
||||
|
||||
_g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_DEVICE, statbuf->st_dev);
|
||||
_g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_NLINK, statbuf->st_nlink);
|
||||
#ifndef G_OS_WIN32
|
||||
/* Pointless setting these on Windows even if they exist in the struct */
|
||||
_g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_INODE, statbuf->st_ino);
|
||||
_g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_NLINK, statbuf->st_nlink);
|
||||
_g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_UID, statbuf->st_uid);
|
||||
_g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_GID, statbuf->st_gid);
|
||||
_g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_RDEV, statbuf->st_rdev);
|
||||
#endif
|
||||
/* FIXME: st_mode is mostly pointless on Windows, too. Set the attribute or not? */
|
||||
/* Mostly pointless on Windows.
|
||||
* Still, it allows for S_ISREG/S_ISDIR and IWRITE (read-only) checks.
|
||||
*/
|
||||
_g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_MODE, statbuf->st_mode);
|
||||
#if defined (HAVE_STRUCT_STAT_ST_BLKSIZE)
|
||||
_g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCK_SIZE, statbuf->st_blksize);
|
||||
|
@ -291,10 +291,10 @@ test_internal_enhanced_stdio (void)
|
||||
ft_programdata = g_file_info_get_file_type (fi_programdata);
|
||||
ft_commondata = g_file_info_get_file_type (fi_commondata);
|
||||
|
||||
g_assert_cmpint (ft_allusers, ==, G_FILE_TYPE_SYMBOLIC_LINK);
|
||||
g_assert_cmpint (ft_allusers, ==, G_FILE_TYPE_DIRECTORY);
|
||||
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);
|
||||
g_assert_cmpint (ft_commondata, ==, G_FILE_TYPE_DIRECTORY);
|
||||
|
||||
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);
|
||||
|
@ -2090,7 +2090,7 @@ gchar *
|
||||
g_file_read_link (const gchar *filename,
|
||||
GError **error)
|
||||
{
|
||||
#if defined (HAVE_READLINK) || defined (G_OS_WIN32)
|
||||
#if defined (HAVE_READLINK)
|
||||
gchar *buffer;
|
||||
size_t size;
|
||||
gssize read_size;
|
||||
@ -2103,11 +2103,7 @@ g_file_read_link (const gchar *filename,
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
#ifndef G_OS_WIN32
|
||||
read_size = readlink (filename, buffer, size);
|
||||
#else
|
||||
read_size = g_win32_readlink_utf8 (filename, buffer, size);
|
||||
#endif
|
||||
if (read_size < 0)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
@ -2128,6 +2124,27 @@ g_file_read_link (const gchar *filename,
|
||||
size *= 2;
|
||||
buffer = g_realloc (buffer, size);
|
||||
}
|
||||
#elif defined (G_OS_WIN32)
|
||||
gchar *buffer;
|
||||
gssize read_size;
|
||||
|
||||
g_return_val_if_fail (filename != NULL, NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
|
||||
read_size = g_win32_readlink_utf8 (filename, NULL, 0, &buffer, TRUE);
|
||||
if (read_size < 0)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
set_file_error (error,
|
||||
filename,
|
||||
_("Failed to read the symbolic link “%s”: %s"),
|
||||
saved_errno);
|
||||
return NULL;
|
||||
}
|
||||
else if (read_size == 0)
|
||||
return strdup ("");
|
||||
else
|
||||
return buffer;
|
||||
#else
|
||||
g_return_val_if_fail (filename != NULL, NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
|
@ -61,18 +61,20 @@ typedef struct {
|
||||
|
||||
/* See gstdio.c */
|
||||
#ifdef G_OS_WIN32
|
||||
int (* g_win32_stat_utf8) (const gchar *filename,
|
||||
GWin32PrivateStat *buf);
|
||||
int (* g_win32_stat_utf8) (const gchar *filename,
|
||||
GWin32PrivateStat *buf);
|
||||
|
||||
int (* g_win32_lstat_utf8) (const gchar *filename,
|
||||
GWin32PrivateStat *buf);
|
||||
int (* g_win32_lstat_utf8) (const gchar *filename,
|
||||
GWin32PrivateStat *buf);
|
||||
|
||||
int (* g_win32_readlink_utf8) (const gchar *filename,
|
||||
gchar *buf,
|
||||
gsize buf_size);
|
||||
int (* g_win32_readlink_utf8) (const gchar *filename,
|
||||
gchar *buf,
|
||||
gsize buf_size,
|
||||
gchar **alloc_buf,
|
||||
gboolean terminate);
|
||||
|
||||
int (* g_win32_fstat) (int fd,
|
||||
GWin32PrivateStat *buf);
|
||||
int (* g_win32_fstat) (int fd,
|
||||
GWin32PrivateStat *buf);
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -75,3 +75,92 @@ _g_win32_strip_extended_ntobjm_prefix (gunichar2 *str,
|
||||
|
||||
return do_move;
|
||||
}
|
||||
|
||||
static int
|
||||
_g_win32_copy_and_maybe_terminate (const guchar *data,
|
||||
gsize in_to_copy,
|
||||
gunichar2 *buf,
|
||||
gsize buf_size,
|
||||
gunichar2 **alloc_buf,
|
||||
gboolean terminate)
|
||||
{
|
||||
gsize to_copy = in_to_copy;
|
||||
/* Number of bytes we can use to add extra zeroes for NUL-termination.
|
||||
* 0 means that we can destroy up to 2 bytes of data,
|
||||
* 1 means that we can destroy up to 1 byte of data,
|
||||
* 2 means that we do not perform destructive NUL-termination
|
||||
*/
|
||||
gsize extra_bytes = terminate ? 2 : 0;
|
||||
char *buf_in_chars;
|
||||
|
||||
if (to_copy == 0)
|
||||
return 0;
|
||||
|
||||
/* 2 bytes is sizeof (wchar_t), for an extra NUL-terminator. */
|
||||
if (buf)
|
||||
{
|
||||
if (to_copy >= buf_size)
|
||||
{
|
||||
extra_bytes = 0;
|
||||
to_copy = buf_size;
|
||||
}
|
||||
else if (to_copy > buf_size - 2)
|
||||
{
|
||||
extra_bytes = 1;
|
||||
}
|
||||
|
||||
memcpy (buf, data, to_copy);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Note that SubstituteNameLength is USHORT, so to_copy + 2, being
|
||||
* gsize, never overflows.
|
||||
*/
|
||||
*alloc_buf = g_malloc (to_copy + extra_bytes);
|
||||
memcpy (*alloc_buf, data, to_copy);
|
||||
}
|
||||
|
||||
if (!terminate)
|
||||
return to_copy;
|
||||
|
||||
if (buf)
|
||||
buf_in_chars = (char *) buf;
|
||||
else
|
||||
buf_in_chars = (char *) *alloc_buf;
|
||||
|
||||
if (to_copy >= 2 && buf_in_chars[to_copy - 2] == 0 &&
|
||||
buf_in_chars[to_copy - 1] == 0)
|
||||
{
|
||||
/* Fully NUL-terminated, do nothing */
|
||||
}
|
||||
else if ((to_copy == 1 || buf_in_chars[to_copy - 2] != 0) &&
|
||||
buf_in_chars[to_copy - 1] == 0)
|
||||
{
|
||||
/* Have one zero, try to add another one */
|
||||
if (extra_bytes > 0)
|
||||
{
|
||||
/* Append trailing zero */
|
||||
buf_in_chars[to_copy] = 0;
|
||||
/* Be precise about the number of bytes we return */
|
||||
to_copy += 1;
|
||||
}
|
||||
else if (to_copy >= 2)
|
||||
{
|
||||
/* No space for appending, destroy one byte */
|
||||
buf_in_chars[to_copy - 2] = 0;
|
||||
}
|
||||
/* else there's no space at all (to_copy == 1), do nothing */
|
||||
}
|
||||
else if (extra_bytes > 0 || to_copy >= 2)
|
||||
{
|
||||
buf_in_chars[to_copy - 2 + extra_bytes] = 0;
|
||||
buf_in_chars[to_copy - 1 + extra_bytes] = 0;
|
||||
to_copy += extra_bytes;
|
||||
}
|
||||
else /* extra_bytes == 0 && to_copy == 1 */
|
||||
{
|
||||
buf_in_chars[0] = 0;
|
||||
}
|
||||
|
||||
return to_copy;
|
||||
}
|
||||
|
869
glib/gstdio.c
869
glib/gstdio.c
@ -129,6 +129,8 @@ w32_error_to_errno (DWORD error_code)
|
||||
* FT = UT * 10000000 + 116444736000000000.
|
||||
* Therefore:
|
||||
* UT = (FT - 116444736000000000) / 10000000.
|
||||
* Converts FILETIME to unix epoch time in form
|
||||
* of a signed 64-bit integer (can be negative).
|
||||
*/
|
||||
static gint64
|
||||
_g_win32_filetime_to_unix_time (FILETIME *ft)
|
||||
@ -165,6 +167,9 @@ _g_win32_filetime_to_unix_time (FILETIME *ft)
|
||||
# endif
|
||||
# endif
|
||||
|
||||
/* Uses filename and BHFI to fill a stat64 structure.
|
||||
* Tries to reproduce the behaviour and quirks of MS C runtime stat().
|
||||
*/
|
||||
static int
|
||||
_g_win32_fill_statbuf_from_handle_info (const wchar_t *filename,
|
||||
const wchar_t *filename_target,
|
||||
@ -258,340 +263,157 @@ _g_win32_fill_statbuf_from_handle_info (const wchar_t *filename,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_g_win32_stat_utf16_no_trailing_slashes (const gunichar2 *filename,
|
||||
int fd,
|
||||
GWin32PrivateStat *buf,
|
||||
gboolean for_symlink)
|
||||
/* Fills our private stat-like structure using data from
|
||||
* a normal stat64 struct, BHFI, FSI and a reparse tag.
|
||||
*/
|
||||
static void
|
||||
_g_win32_fill_privatestat (const struct __stat64 *statbuf,
|
||||
const BY_HANDLE_FILE_INFORMATION *handle_info,
|
||||
const FILE_STANDARD_INFO *std_info,
|
||||
DWORD reparse_tag,
|
||||
GWin32PrivateStat *buf)
|
||||
{
|
||||
buf->st_dev = statbuf->st_dev;
|
||||
buf->st_mode = statbuf->st_mode;
|
||||
buf->volume_serial = handle_info->dwVolumeSerialNumber;
|
||||
buf->file_index = (((guint64) handle_info->nFileIndexHigh) << 32) | handle_info->nFileIndexLow;
|
||||
buf->attributes = handle_info->dwFileAttributes;
|
||||
buf->st_nlink = handle_info->nNumberOfLinks;
|
||||
buf->st_size = (((guint64) handle_info->nFileSizeHigh) << 32) | handle_info->nFileSizeLow;
|
||||
buf->allocated_size = std_info->AllocationSize.QuadPart;
|
||||
|
||||
buf->reparse_tag = reparse_tag;
|
||||
|
||||
buf->st_ctime = statbuf->st_ctime;
|
||||
buf->st_atime = statbuf->st_atime;
|
||||
buf->st_mtime = statbuf->st_mtime;
|
||||
}
|
||||
|
||||
/* Read the link data from a symlink/mountpoint represented
|
||||
* by the handle. Also reads reparse tag.
|
||||
* @reparse_tag receives the tag. Can be %NULL if @buf or @alloc_buf
|
||||
* is non-NULL.
|
||||
* @buf receives the link data. Can be %NULL if reparse_tag is non-%NULL.
|
||||
* Mutually-exclusive with @alloc_buf.
|
||||
* @buf_size is the size of the @buf, in bytes.
|
||||
* @alloc_buf points to a location where internally-allocated buffer
|
||||
* pointer will be written. That buffer receives the
|
||||
* link data. Mutually-exclusive with @buf.
|
||||
* @terminate ensures that the buffer is NUL-terminated if
|
||||
* it isn't already. Note that this can erase useful
|
||||
* data if @buf is provided and @buf_size is too small.
|
||||
* Specifically, with @buf_size <= 2 the buffer will
|
||||
* receive an empty string, even if there is some
|
||||
* data in the reparse point.
|
||||
* The contents of @buf or @alloc_buf are presented as-is - could
|
||||
* be non-NUL-terminated (unless @terminate is %TRUE) or even malformed.
|
||||
* Returns the number of bytes (!) placed into @buf or @alloc_buf,
|
||||
* including NUL-terminator (if any).
|
||||
*
|
||||
* Returned value of 0 means that there's no recognizable data in the
|
||||
* reparse point. @alloc_buf will not be allocated in that case,
|
||||
* and @buf will be left unmodified.
|
||||
*
|
||||
* If @buf and @alloc_buf are %NULL, returns 0 to indicate success.
|
||||
* Returns -1 to indicate an error, sets errno.
|
||||
*/
|
||||
static int
|
||||
_g_win32_readlink_handle_raw (HANDLE h,
|
||||
DWORD *reparse_tag,
|
||||
gunichar2 *buf,
|
||||
gsize buf_size,
|
||||
gunichar2 **alloc_buf,
|
||||
gboolean terminate)
|
||||
{
|
||||
HANDLE file_handle;
|
||||
gboolean succeeded_so_far;
|
||||
DWORD error_code;
|
||||
struct __stat64 statbuf;
|
||||
BY_HANDLE_FILE_INFORMATION handle_info;
|
||||
FILE_STANDARD_INFO std_info;
|
||||
WIN32_FIND_DATAW finddata;
|
||||
DWORD immediate_attributes;
|
||||
gboolean is_symlink = FALSE;
|
||||
gboolean is_directory;
|
||||
DWORD open_flags;
|
||||
wchar_t *filename_target = NULL;
|
||||
int result;
|
||||
DWORD returned_bytes = 0;
|
||||
BYTE *data;
|
||||
gsize to_copy;
|
||||
/* This is 16k. It's impossible to make DeviceIoControl() tell us
|
||||
* the required size. NtFsControlFile() does have such a feature,
|
||||
* but for some reason it doesn't work with CreateFile()-returned handles.
|
||||
* The only alternative is to repeatedly call DeviceIoControl()
|
||||
* with bigger and bigger buffers, until it succeeds.
|
||||
* We choose to sacrifice stack space for speed.
|
||||
*/
|
||||
BYTE max_buffer[sizeof (REPARSE_DATA_BUFFER) + MAXIMUM_REPARSE_DATA_BUFFER_SIZE] = {0,};
|
||||
DWORD max_buffer_size = sizeof (REPARSE_DATA_BUFFER) + MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
|
||||
REPARSE_DATA_BUFFER *rep_buf;
|
||||
|
||||
if (fd < 0)
|
||||
g_return_val_if_fail ((buf != NULL || alloc_buf != NULL || reparse_tag != NULL) &&
|
||||
(buf == NULL || alloc_buf == NULL),
|
||||
-1);
|
||||
|
||||
if (!DeviceIoControl (h, FSCTL_GET_REPARSE_POINT, NULL, 0,
|
||||
max_buffer,
|
||||
max_buffer_size,
|
||||
&returned_bytes, NULL))
|
||||
{
|
||||
immediate_attributes = GetFileAttributesW (filename);
|
||||
|
||||
if (immediate_attributes == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
error_code = GetLastError ();
|
||||
errno = w32_error_to_errno (error_code);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
is_symlink = (immediate_attributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
is_directory = (immediate_attributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
|
||||
|
||||
open_flags = FILE_ATTRIBUTE_NORMAL;
|
||||
|
||||
if (for_symlink && is_symlink)
|
||||
open_flags |= FILE_FLAG_OPEN_REPARSE_POINT;
|
||||
|
||||
if (is_directory)
|
||||
open_flags |= FILE_FLAG_BACKUP_SEMANTICS;
|
||||
|
||||
file_handle = CreateFileW (filename, FILE_READ_ATTRIBUTES,
|
||||
FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||
open_flags,
|
||||
NULL);
|
||||
|
||||
if (file_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
error_code = GetLastError ();
|
||||
errno = w32_error_to_errno (error_code);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
file_handle = (HANDLE) _get_osfhandle (fd);
|
||||
|
||||
if (file_handle == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
}
|
||||
|
||||
succeeded_so_far = GetFileInformationByHandle (file_handle,
|
||||
&handle_info);
|
||||
error_code = GetLastError ();
|
||||
|
||||
if (succeeded_so_far)
|
||||
{
|
||||
succeeded_so_far = GetFileInformationByHandleEx (file_handle,
|
||||
FileStandardInfo,
|
||||
&std_info,
|
||||
sizeof (std_info));
|
||||
error_code = GetLastError ();
|
||||
}
|
||||
|
||||
if (!succeeded_so_far)
|
||||
{
|
||||
if (fd < 0)
|
||||
CloseHandle (file_handle);
|
||||
errno = w32_error_to_errno (error_code);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* It's tempting to use GetFileInformationByHandleEx(FileAttributeTagInfo),
|
||||
* but it always reports that the ReparseTag is 0.
|
||||
*/
|
||||
if (fd < 0)
|
||||
rep_buf = (REPARSE_DATA_BUFFER *) max_buffer;
|
||||
|
||||
if (reparse_tag != NULL)
|
||||
*reparse_tag = rep_buf->ReparseTag;
|
||||
|
||||
if (buf == NULL && alloc_buf == NULL)
|
||||
return 0;
|
||||
|
||||
if (rep_buf->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
||||
{
|
||||
memset (&finddata, 0, sizeof (finddata));
|
||||
data = &((BYTE *) rep_buf->SymbolicLinkReparseBuffer.PathBuffer)[rep_buf->SymbolicLinkReparseBuffer.SubstituteNameOffset];
|
||||
|
||||
if (handle_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
{
|
||||
HANDLE tmp = FindFirstFileW (filename,
|
||||
&finddata);
|
||||
|
||||
if (tmp == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
error_code = GetLastError ();
|
||||
errno = w32_error_to_errno (error_code);
|
||||
CloseHandle (file_handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FindClose (tmp);
|
||||
}
|
||||
|
||||
if (is_symlink && !for_symlink)
|
||||
{
|
||||
/* If filename is a symlink, but we need the target.
|
||||
* To get information about the target we need to resolve
|
||||
* the symlink first.
|
||||
*/
|
||||
DWORD filename_target_len;
|
||||
DWORD new_len;
|
||||
|
||||
/* Just in case, give it a real memory location instead of NULL */
|
||||
new_len = GetFinalPathNameByHandleW (file_handle,
|
||||
(wchar_t *) &filename_target_len,
|
||||
0,
|
||||
FILE_NAME_NORMALIZED);
|
||||
|
||||
#define SANE_LIMIT 1024 * 10
|
||||
if (new_len >= SANE_LIMIT)
|
||||
#undef SANE_LIMIT
|
||||
{
|
||||
new_len = 0;
|
||||
error_code = ERROR_BUFFER_OVERFLOW;
|
||||
}
|
||||
else if (new_len == 0)
|
||||
{
|
||||
error_code = GetLastError ();
|
||||
}
|
||||
|
||||
if (new_len > 0)
|
||||
{
|
||||
/* Pretend that new_len doesn't count the terminating NUL char,
|
||||
* and ask for a bit more space than is needed, and allocate even more.
|
||||
*/
|
||||
filename_target_len = new_len + 3;
|
||||
filename_target = g_malloc ((filename_target_len + 1) * sizeof (wchar_t));
|
||||
|
||||
new_len = GetFinalPathNameByHandleW (file_handle,
|
||||
filename_target,
|
||||
filename_target_len,
|
||||
FILE_NAME_NORMALIZED);
|
||||
|
||||
/* filename_target_len is already larger than needed,
|
||||
* new_len should be smaller than that, even if the size
|
||||
* is off by 1 for some reason.
|
||||
*/
|
||||
if (new_len >= filename_target_len - 1)
|
||||
{
|
||||
new_len = 0;
|
||||
error_code = ERROR_BUFFER_OVERFLOW;
|
||||
g_clear_pointer (&filename_target, g_free);
|
||||
}
|
||||
else if (new_len == 0)
|
||||
{
|
||||
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)
|
||||
succeeded_so_far = FALSE;
|
||||
}
|
||||
|
||||
CloseHandle (file_handle);
|
||||
to_copy = rep_buf->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||||
}
|
||||
/* else if fd >= 0 the file_handle was obtained via _get_osfhandle()
|
||||
* and must not be closed, it is owned by fd.
|
||||
*/
|
||||
|
||||
if (!succeeded_so_far)
|
||||
else if (rep_buf->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
|
||||
{
|
||||
errno = w32_error_to_errno (error_code);
|
||||
return -1;
|
||||
}
|
||||
data = &((BYTE *) rep_buf->MountPointReparseBuffer.PathBuffer)[rep_buf->MountPointReparseBuffer.SubstituteNameOffset];
|
||||
|
||||
/*
|
||||
* We can't use _wstat64() here, because with UCRT it now gives
|
||||
* information about the target, even if we want information about
|
||||
* the link itself (unlike MSVCRT, which gave information about
|
||||
* the link, and if we needed information about the target we were
|
||||
* able to resolve it by ourselves prior to calling _wstat64()).
|
||||
*/
|
||||
if (fd < 0)
|
||||
result = _g_win32_fill_statbuf_from_handle_info (filename,
|
||||
filename_target,
|
||||
&handle_info,
|
||||
&statbuf);
|
||||
to_copy = rep_buf->MountPointReparseBuffer.SubstituteNameLength;
|
||||
}
|
||||
else
|
||||
result = _fstat64 (fd, &statbuf);
|
||||
to_copy = 0;
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
int errsv = errno;
|
||||
|
||||
g_free (filename_target);
|
||||
errno = errsv;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_free (filename_target);
|
||||
|
||||
buf->st_dev = statbuf.st_dev;
|
||||
buf->st_mode = statbuf.st_mode;
|
||||
buf->volume_serial = handle_info.dwVolumeSerialNumber;
|
||||
buf->file_index = (((guint64) handle_info.nFileIndexHigh) << 32) | handle_info.nFileIndexLow;
|
||||
/* Note that immediate_attributes is for the symlink
|
||||
* (if it's a symlink), while handle_info contains info
|
||||
* about the symlink or the target, depending on the flags
|
||||
* we used earlier.
|
||||
*/
|
||||
buf->attributes = handle_info.dwFileAttributes;
|
||||
buf->st_nlink = handle_info.nNumberOfLinks;
|
||||
buf->st_size = (((guint64) handle_info.nFileSizeHigh) << 32) | handle_info.nFileSizeLow;
|
||||
buf->allocated_size = std_info.AllocationSize.QuadPart;
|
||||
|
||||
if (fd < 0 && buf->attributes & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
buf->reparse_tag = finddata.dwReserved0;
|
||||
else
|
||||
buf->reparse_tag = 0;
|
||||
|
||||
buf->st_ctime = statbuf.st_ctime;
|
||||
buf->st_atime = statbuf.st_atime;
|
||||
buf->st_mtime = statbuf.st_mtime;
|
||||
|
||||
return 0;
|
||||
return _g_win32_copy_and_maybe_terminate (data, to_copy, buf, buf_size, alloc_buf, terminate);
|
||||
}
|
||||
|
||||
/* Read the link data from a symlink/mountpoint represented
|
||||
* by the @filename.
|
||||
* @filename is the name of the file.
|
||||
* @reparse_tag receives the tag. Can be %NULL if @buf or @alloc_buf
|
||||
* is non-%NULL.
|
||||
* @buf receives the link data. Mutually-exclusive with @alloc_buf.
|
||||
* @buf_size is the size of the @buf, in bytes.
|
||||
* @alloc_buf points to a location where internally-allocated buffer
|
||||
* pointer will be written. That buffer receives the
|
||||
* link data. Mutually-exclusive with @buf.
|
||||
* @terminate ensures that the buffer is NUL-terminated if
|
||||
* it isn't already
|
||||
* The contents of @buf or @alloc_buf are presented as-is - could
|
||||
* be non-NUL-terminated (unless @terminate is TRUE) or even malformed.
|
||||
* Returns the number of bytes (!) placed into @buf or @alloc_buf.
|
||||
* Returned value of 0 means that there's no recognizable data in the
|
||||
* reparse point. @alloc_buf will not be allocated in that case,
|
||||
* and @buf will be left unmodified.
|
||||
* If @buf and @alloc_buf are %NULL, returns 0 to indicate success.
|
||||
* Returns -1 to indicate an error, sets errno.
|
||||
*/
|
||||
static int
|
||||
_g_win32_stat_utf8 (const gchar *filename,
|
||||
GWin32PrivateStat *buf,
|
||||
gboolean for_symlink)
|
||||
_g_win32_readlink_utf16_raw (const gunichar2 *filename,
|
||||
DWORD *reparse_tag,
|
||||
gunichar2 *buf,
|
||||
gsize buf_size,
|
||||
gunichar2 **alloc_buf,
|
||||
gboolean terminate)
|
||||
{
|
||||
wchar_t *wfilename;
|
||||
int result;
|
||||
gsize len;
|
||||
|
||||
if (filename == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = strlen (filename);
|
||||
|
||||
while (len > 0 && G_IS_DIR_SEPARATOR (filename[len - 1]))
|
||||
len--;
|
||||
|
||||
if (len <= 0 ||
|
||||
(g_path_is_absolute (filename) && len <= g_path_skip_root (filename) - filename))
|
||||
len = strlen (filename);
|
||||
|
||||
wfilename = g_utf8_to_utf16 (filename, len, NULL, NULL, NULL);
|
||||
|
||||
if (wfilename == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = _g_win32_stat_utf16_no_trailing_slashes (wfilename, -1, buf, for_symlink);
|
||||
|
||||
g_free (wfilename);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
g_win32_stat_utf8 (const gchar *filename,
|
||||
GWin32PrivateStat *buf)
|
||||
{
|
||||
return _g_win32_stat_utf8 (filename, buf, FALSE);
|
||||
}
|
||||
|
||||
int
|
||||
g_win32_lstat_utf8 (const gchar *filename,
|
||||
GWin32PrivateStat *buf)
|
||||
{
|
||||
return _g_win32_stat_utf8 (filename, buf, TRUE);
|
||||
}
|
||||
|
||||
int
|
||||
g_win32_fstat (int fd,
|
||||
GWin32PrivateStat *buf)
|
||||
{
|
||||
return _g_win32_stat_utf16_no_trailing_slashes (NULL, fd, buf, FALSE);
|
||||
}
|
||||
|
||||
static int
|
||||
_g_win32_readlink_utf16_raw (const gunichar2 *filename,
|
||||
gunichar2 *buf,
|
||||
gsize buf_size)
|
||||
{
|
||||
DWORD returned_bytes;
|
||||
BYTE returned_data[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; /* This is 16k, by the way */
|
||||
HANDLE h;
|
||||
DWORD attributes;
|
||||
REPARSE_DATA_BUFFER *rep_buf;
|
||||
DWORD to_copy;
|
||||
DWORD error_code;
|
||||
|
||||
if (buf_size > G_MAXSIZE / sizeof (wchar_t))
|
||||
{
|
||||
/* "buf_size * sizeof (wchar_t)" overflows */
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((attributes = GetFileAttributesW (filename)) == 0)
|
||||
{
|
||||
error_code = GetLastError ();
|
||||
@ -624,55 +446,59 @@ _g_win32_readlink_utf16_raw (const gunichar2 *filename,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!DeviceIoControl (h, FSCTL_GET_REPARSE_POINT, NULL, 0,
|
||||
returned_data, MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
|
||||
&returned_bytes, NULL))
|
||||
{
|
||||
error_code = GetLastError ();
|
||||
errno = w32_error_to_errno (error_code);
|
||||
CloseHandle (h);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rep_buf = (REPARSE_DATA_BUFFER *) returned_data;
|
||||
to_copy = 0;
|
||||
|
||||
if (rep_buf->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
||||
{
|
||||
to_copy = rep_buf->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||||
|
||||
if (to_copy > buf_size * sizeof (wchar_t))
|
||||
to_copy = buf_size * sizeof (wchar_t);
|
||||
|
||||
memcpy (buf,
|
||||
&((BYTE *) rep_buf->SymbolicLinkReparseBuffer.PathBuffer)[rep_buf->SymbolicLinkReparseBuffer.SubstituteNameOffset],
|
||||
to_copy);
|
||||
}
|
||||
else if (rep_buf->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
|
||||
{
|
||||
to_copy = rep_buf->MountPointReparseBuffer.SubstituteNameLength;
|
||||
|
||||
if (to_copy > buf_size * sizeof (wchar_t))
|
||||
to_copy = buf_size * sizeof (wchar_t);
|
||||
|
||||
memcpy (buf,
|
||||
&((BYTE *) rep_buf->MountPointReparseBuffer.PathBuffer)[rep_buf->MountPointReparseBuffer.SubstituteNameOffset],
|
||||
to_copy);
|
||||
}
|
||||
to_copy = _g_win32_readlink_handle_raw (h, reparse_tag, buf, buf_size, alloc_buf, terminate);
|
||||
|
||||
CloseHandle (h);
|
||||
|
||||
return to_copy;
|
||||
}
|
||||
|
||||
/* Read the link data from a symlink/mountpoint represented
|
||||
* by a UTF-16 filename or a file handle.
|
||||
* @filename is the name of the file. Mutually-exclusive with @file_handle.
|
||||
* @file_handle is the handle of the file. Mutually-exclusive with @filename.
|
||||
* @reparse_tag receives the tag. Can be %NULL if @buf or @alloc_buf
|
||||
* is non-%NULL.
|
||||
* @buf receives the link data. Mutually-exclusive with @alloc_buf.
|
||||
* @buf_size is the size of the @buf, in bytes.
|
||||
* @alloc_buf points to a location where internally-allocated buffer
|
||||
* pointer will be written. That buffer receives the
|
||||
* link data. Mutually-exclusive with @buf.
|
||||
* @terminate ensures that the buffer is NUL-terminated if
|
||||
* it isn't already
|
||||
* The contents of @buf or @alloc_buf are adjusted
|
||||
* (extended or nt object manager prefix is stripped),
|
||||
* but otherwise they are presented as-is - could be non-NUL-terminated
|
||||
* (unless @terminate is TRUE) or even malformed.
|
||||
* Returns the number of bytes (!) placed into @buf or @alloc_buf.
|
||||
* Returned value of 0 means that there's no recognizable data in the
|
||||
* reparse point. @alloc_buf will not be allocated in that case,
|
||||
* and @buf will be left unmodified.
|
||||
* Returns -1 to indicate an error, sets errno.
|
||||
*/
|
||||
static int
|
||||
_g_win32_readlink_utf16 (const gunichar2 *filename,
|
||||
gunichar2 *buf,
|
||||
gsize buf_size)
|
||||
_g_win32_readlink_utf16_handle (const gunichar2 *filename,
|
||||
HANDLE file_handle,
|
||||
DWORD *reparse_tag,
|
||||
gunichar2 *buf,
|
||||
gsize buf_size,
|
||||
gunichar2 **alloc_buf,
|
||||
gboolean terminate)
|
||||
{
|
||||
int result = _g_win32_readlink_utf16_raw (filename, buf, buf_size);
|
||||
int result;
|
||||
gsize string_size;
|
||||
|
||||
g_return_val_if_fail ((buf != NULL || alloc_buf != NULL || reparse_tag != NULL) &&
|
||||
(filename != NULL || file_handle != NULL) &&
|
||||
(buf == NULL || alloc_buf == NULL) &&
|
||||
(filename == NULL || file_handle == NULL),
|
||||
-1);
|
||||
|
||||
if (filename)
|
||||
result = _g_win32_readlink_utf16_raw (filename, reparse_tag, buf, buf_size, alloc_buf, terminate);
|
||||
else
|
||||
result = _g_win32_readlink_handle_raw (file_handle, reparse_tag, buf, buf_size, alloc_buf, terminate);
|
||||
|
||||
if (result <= 0)
|
||||
return result;
|
||||
|
||||
@ -686,19 +512,257 @@ _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
|
||||
* Remove the leading 4-byte "\\??\\" prefix, as glib (as well as many W32 API
|
||||
* 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
|
||||
* as-is, because the path could be "\\??\\Volume{GUID}" - stripping
|
||||
* the prefix will allow it to be confused with relative links
|
||||
* targeting "Volume{GUID}".
|
||||
*/
|
||||
string_size = result / sizeof (gunichar2);
|
||||
_g_win32_strip_extended_ntobjm_prefix (buf, &string_size);
|
||||
_g_win32_strip_extended_ntobjm_prefix (buf ? buf : *alloc_buf, &string_size);
|
||||
|
||||
return string_size * sizeof (gunichar2);
|
||||
}
|
||||
|
||||
/* Works like stat() or lstat(), depending on the value of @for_symlink,
|
||||
* but accepts filename in UTF-16 and fills our custom stat structure.
|
||||
* The @filename must not have trailing slashes.
|
||||
*/
|
||||
static int
|
||||
_g_win32_stat_utf16_no_trailing_slashes (const gunichar2 *filename,
|
||||
GWin32PrivateStat *buf,
|
||||
gboolean for_symlink)
|
||||
{
|
||||
struct __stat64 statbuf;
|
||||
BY_HANDLE_FILE_INFORMATION handle_info;
|
||||
FILE_STANDARD_INFO std_info;
|
||||
gboolean is_symlink = FALSE;
|
||||
wchar_t *filename_target = NULL;
|
||||
DWORD immediate_attributes;
|
||||
DWORD open_flags;
|
||||
gboolean is_directory;
|
||||
DWORD reparse_tag = 0;
|
||||
DWORD error_code;
|
||||
BOOL succeeded_so_far;
|
||||
HANDLE file_handle;
|
||||
|
||||
immediate_attributes = GetFileAttributesW (filename);
|
||||
|
||||
if (immediate_attributes == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
error_code = GetLastError ();
|
||||
errno = w32_error_to_errno (error_code);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
is_symlink = (immediate_attributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
is_directory = (immediate_attributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
|
||||
|
||||
open_flags = FILE_ATTRIBUTE_NORMAL;
|
||||
|
||||
if (for_symlink && is_symlink)
|
||||
open_flags |= FILE_FLAG_OPEN_REPARSE_POINT;
|
||||
|
||||
if (is_directory)
|
||||
open_flags |= FILE_FLAG_BACKUP_SEMANTICS;
|
||||
|
||||
file_handle = CreateFileW (filename, FILE_READ_ATTRIBUTES | FILE_READ_EA,
|
||||
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING,
|
||||
open_flags,
|
||||
NULL);
|
||||
|
||||
if (file_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
error_code = GetLastError ();
|
||||
errno = w32_error_to_errno (error_code);
|
||||
return -1;
|
||||
}
|
||||
|
||||
succeeded_so_far = GetFileInformationByHandle (file_handle,
|
||||
&handle_info);
|
||||
error_code = GetLastError ();
|
||||
|
||||
if (succeeded_so_far)
|
||||
{
|
||||
succeeded_so_far = GetFileInformationByHandleEx (file_handle,
|
||||
FileStandardInfo,
|
||||
&std_info,
|
||||
sizeof (std_info));
|
||||
error_code = GetLastError ();
|
||||
}
|
||||
|
||||
if (!succeeded_so_far)
|
||||
{
|
||||
CloseHandle (file_handle);
|
||||
errno = w32_error_to_errno (error_code);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* It's tempting to use GetFileInformationByHandleEx(FileAttributeTagInfo),
|
||||
* but it always reports that the ReparseTag is 0.
|
||||
* We already have a handle open for symlink, use that.
|
||||
* For the target we have to specify a filename, and the function
|
||||
* will open another handle internally.
|
||||
*/
|
||||
if (is_symlink &&
|
||||
_g_win32_readlink_utf16_handle (for_symlink ? NULL : filename,
|
||||
for_symlink ? file_handle : NULL,
|
||||
&reparse_tag,
|
||||
NULL, 0,
|
||||
for_symlink ? NULL : &filename_target,
|
||||
TRUE) < 0)
|
||||
{
|
||||
CloseHandle (file_handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
CloseHandle (file_handle);
|
||||
|
||||
_g_win32_fill_statbuf_from_handle_info (filename,
|
||||
filename_target,
|
||||
&handle_info,
|
||||
&statbuf);
|
||||
g_free (filename_target);
|
||||
_g_win32_fill_privatestat (&statbuf,
|
||||
&handle_info,
|
||||
&std_info,
|
||||
reparse_tag,
|
||||
buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Works like fstat(), but fills our custom stat structure. */
|
||||
static int
|
||||
_g_win32_stat_fd (int fd,
|
||||
GWin32PrivateStat *buf)
|
||||
{
|
||||
HANDLE file_handle;
|
||||
gboolean succeeded_so_far;
|
||||
DWORD error_code;
|
||||
struct __stat64 statbuf;
|
||||
BY_HANDLE_FILE_INFORMATION handle_info;
|
||||
FILE_STANDARD_INFO std_info;
|
||||
DWORD reparse_tag = 0;
|
||||
gboolean is_symlink = FALSE;
|
||||
|
||||
file_handle = (HANDLE) _get_osfhandle (fd);
|
||||
|
||||
if (file_handle == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
|
||||
succeeded_so_far = GetFileInformationByHandle (file_handle,
|
||||
&handle_info);
|
||||
error_code = GetLastError ();
|
||||
|
||||
if (succeeded_so_far)
|
||||
{
|
||||
succeeded_so_far = GetFileInformationByHandleEx (file_handle,
|
||||
FileStandardInfo,
|
||||
&std_info,
|
||||
sizeof (std_info));
|
||||
error_code = GetLastError ();
|
||||
}
|
||||
|
||||
if (!succeeded_so_far)
|
||||
{
|
||||
errno = w32_error_to_errno (error_code);
|
||||
return -1;
|
||||
}
|
||||
|
||||
is_symlink = (handle_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
|
||||
if (is_symlink &&
|
||||
_g_win32_readlink_handle_raw (file_handle, &reparse_tag, NULL, 0, NULL, FALSE) < 0)
|
||||
return -1;
|
||||
|
||||
if (_fstat64 (fd, &statbuf) != 0)
|
||||
return -1;
|
||||
|
||||
_g_win32_fill_privatestat (&statbuf,
|
||||
&handle_info,
|
||||
&std_info,
|
||||
reparse_tag,
|
||||
buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Works like stat() or lstat(), depending on the value of @for_symlink,
|
||||
* but accepts filename in UTF-8 and fills our custom stat structure.
|
||||
*/
|
||||
static int
|
||||
_g_win32_stat_utf8 (const gchar *filename,
|
||||
GWin32PrivateStat *buf,
|
||||
gboolean for_symlink)
|
||||
{
|
||||
wchar_t *wfilename;
|
||||
int result;
|
||||
gsize len;
|
||||
|
||||
if (filename == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = strlen (filename);
|
||||
|
||||
while (len > 0 && G_IS_DIR_SEPARATOR (filename[len - 1]))
|
||||
len--;
|
||||
|
||||
if (len <= 0 ||
|
||||
(g_path_is_absolute (filename) && len <= g_path_skip_root (filename) - filename))
|
||||
len = strlen (filename);
|
||||
|
||||
wfilename = g_utf8_to_utf16 (filename, len, NULL, NULL, NULL);
|
||||
|
||||
if (wfilename == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = _g_win32_stat_utf16_no_trailing_slashes (wfilename, buf, for_symlink);
|
||||
|
||||
g_free (wfilename);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Works like stat(), but accepts filename in UTF-8
|
||||
* and fills our custom stat structure.
|
||||
*/
|
||||
int
|
||||
g_win32_stat_utf8 (const gchar *filename,
|
||||
GWin32PrivateStat *buf)
|
||||
{
|
||||
return _g_win32_stat_utf8 (filename, buf, FALSE);
|
||||
}
|
||||
|
||||
/* Works like lstat(), but accepts filename in UTF-8
|
||||
* and fills our custom stat structure.
|
||||
*/
|
||||
int
|
||||
g_win32_lstat_utf8 (const gchar *filename,
|
||||
GWin32PrivateStat *buf)
|
||||
{
|
||||
return _g_win32_stat_utf8 (filename, buf, TRUE);
|
||||
}
|
||||
|
||||
/* Works like fstat(), but accepts filename in UTF-8
|
||||
* and fills our custom stat structure.
|
||||
*/
|
||||
int
|
||||
g_win32_fstat (int fd,
|
||||
GWin32PrivateStat *buf)
|
||||
{
|
||||
return _g_win32_stat_fd (fd, buf);
|
||||
}
|
||||
|
||||
static gchar *
|
||||
_g_win32_get_mode_alias (const gchar *mode)
|
||||
{
|
||||
@ -719,13 +783,54 @@ _g_win32_get_mode_alias (const gchar *mode)
|
||||
return alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_win32_readlink_utf8:
|
||||
* @filename: (type filename): a pathname in UTF-8
|
||||
* @buf: (array length=buf_size) : a buffer to receive the reparse point
|
||||
* target path. Mutually-exclusive
|
||||
* with @alloc_buf.
|
||||
* @buf_size: size of the @buf, in bytes
|
||||
* @alloc_buf: points to a location where internally-allocated buffer
|
||||
* pointer will be written. That buffer receives the
|
||||
* link data. Mutually-exclusive with @buf.
|
||||
* @terminate: ensures that the buffer is NUL-terminated if
|
||||
* it isn't already. If %FALSE, the returned string
|
||||
* might not be NUL-terminated (depends entirely on
|
||||
* what the contents of the filesystem are).
|
||||
*
|
||||
* Tries to read the reparse point indicated by @filename, filling
|
||||
* @buf or @alloc_buf with the path that the reparse point redirects to.
|
||||
* The path will be UTF-8-encoded, and an extended path prefix
|
||||
* or a NT object manager prefix will be removed from it, if
|
||||
* possible, but otherwise the path is returned as-is. Specifically,
|
||||
* it could be a "\\\\Volume{GUID}\\" path. It also might use
|
||||
* backslashes as path separators.
|
||||
*
|
||||
* Returns: -1 on error (sets errno), 0 if there's no (recognizable)
|
||||
* path in the reparse point (@alloc_buf will not be allocated in that case,
|
||||
* and @buf will be left unmodified),
|
||||
* or the number of bytes placed into @buf otherwise,
|
||||
* including NUL-terminator (if present or if @terminate is TRUE).
|
||||
* The buffer returned via @alloc_buf should be freed with g_free().
|
||||
*
|
||||
* Since: 2.60
|
||||
*/
|
||||
int
|
||||
g_win32_readlink_utf8 (const gchar *filename,
|
||||
gchar *buf,
|
||||
gsize buf_size)
|
||||
g_win32_readlink_utf8 (const gchar *filename,
|
||||
gchar *buf,
|
||||
gsize buf_size,
|
||||
gchar **alloc_buf,
|
||||
gboolean terminate)
|
||||
{
|
||||
wchar_t *wfilename;
|
||||
int result;
|
||||
wchar_t *buf_utf16;
|
||||
glong tmp_len;
|
||||
gchar *tmp;
|
||||
|
||||
g_return_val_if_fail ((buf != NULL || alloc_buf != NULL) &&
|
||||
(buf == NULL || alloc_buf == NULL),
|
||||
-1);
|
||||
|
||||
wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
|
||||
|
||||
@ -735,39 +840,41 @@ g_win32_readlink_utf8 (const gchar *filename,
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = _g_win32_readlink_utf16 (wfilename, (gunichar2 *) buf, buf_size);
|
||||
result = _g_win32_readlink_utf16_handle (wfilename, NULL, NULL,
|
||||
NULL, 0, &buf_utf16, terminate);
|
||||
|
||||
g_free (wfilename);
|
||||
|
||||
if (result > 0)
|
||||
if (result <= 0)
|
||||
return result;
|
||||
|
||||
tmp = g_utf16_to_utf8 (buf_utf16,
|
||||
result / sizeof (gunichar2),
|
||||
NULL,
|
||||
&tmp_len,
|
||||
NULL);
|
||||
|
||||
g_free (buf_utf16);
|
||||
|
||||
if (tmp == NULL)
|
||||
{
|
||||
glong tmp_len;
|
||||
gchar *tmp = g_utf16_to_utf8 ((const gunichar2 *) buf,
|
||||
result / sizeof (gunichar2),
|
||||
NULL,
|
||||
&tmp_len,
|
||||
NULL);
|
||||
|
||||
if (tmp == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tmp_len > buf_size - 1)
|
||||
tmp_len = buf_size - 1;
|
||||
|
||||
memcpy (buf, tmp, tmp_len);
|
||||
/* readlink() doesn't NUL-terminate, but we do.
|
||||
* To be compliant, however, we return the
|
||||
* number of bytes without the NUL-terminator.
|
||||
*/
|
||||
buf[tmp_len] = '\0';
|
||||
result = tmp_len;
|
||||
g_free (tmp);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
if (alloc_buf)
|
||||
{
|
||||
*alloc_buf = tmp;
|
||||
return tmp_len;
|
||||
}
|
||||
|
||||
if (tmp_len > buf_size)
|
||||
tmp_len = buf_size;
|
||||
|
||||
memcpy (buf, tmp, tmp_len);
|
||||
g_free (tmp);
|
||||
|
||||
return tmp_len;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -51,9 +51,11 @@ int g_win32_stat_utf8 (const gchar *filename,
|
||||
int g_win32_lstat_utf8 (const gchar *filename,
|
||||
GWin32PrivateStat *buf);
|
||||
|
||||
int g_win32_readlink_utf8 (const gchar *filename,
|
||||
gchar *buf,
|
||||
gsize buf_size);
|
||||
int g_win32_readlink_utf8 (const gchar *filename,
|
||||
gchar *buf,
|
||||
gsize buf_size,
|
||||
gchar **alloc_buf,
|
||||
gboolean terminate);
|
||||
|
||||
int g_win32_fstat (int fd,
|
||||
GWin32PrivateStat *buf);
|
||||
|
@ -889,6 +889,7 @@ test_stdio_wrappers (void)
|
||||
struct utimbuf ut;
|
||||
GError *error = NULL;
|
||||
GStatBuf path_statbuf, cwd_statbuf;
|
||||
time_t now;
|
||||
|
||||
/* The permissions tests here don’t work when running as root. */
|
||||
#ifdef G_OS_UNIX
|
||||
@ -958,14 +959,16 @@ test_stdio_wrappers (void)
|
||||
g_assert_cmpint (ret, ==, 0);
|
||||
#endif
|
||||
|
||||
ut.actime = ut.modtime = (time_t)0;
|
||||
now = time (NULL);
|
||||
|
||||
ut.actime = ut.modtime = now;
|
||||
ret = g_utime ("test-create", &ut);
|
||||
g_assert_cmpint (ret, ==, 0);
|
||||
|
||||
ret = g_lstat ("test-create", &buf);
|
||||
g_assert_cmpint (ret, ==, 0);
|
||||
g_assert_cmpint (buf.st_atime, ==, (time_t)0);
|
||||
g_assert_cmpint (buf.st_mtime, ==, (time_t)0);
|
||||
g_assert_cmpint (buf.st_atime, ==, now);
|
||||
g_assert_cmpint (buf.st_mtime, ==, now);
|
||||
|
||||
g_chdir ("..");
|
||||
g_remove ("mkdir-test/test-create");
|
||||
@ -1153,6 +1156,233 @@ test_win32_pathstrip (void)
|
||||
}
|
||||
}
|
||||
|
||||
#define g_assert_memcmp(m1, cmp, m2, memlen, m1hex, m2hex, testcase_num) \
|
||||
G_STMT_START { \
|
||||
if (memcmp (m1, m2, memlen) cmp 0); else \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#m1hex " " #cmp " " #m2hex, m1hex, #cmp, m2hex); \
|
||||
} G_STMT_END
|
||||
|
||||
static gchar *
|
||||
to_hex (const guchar *buf,
|
||||
gsize len)
|
||||
{
|
||||
gsize i;
|
||||
GString *s = g_string_new (NULL);
|
||||
if (len > 0)
|
||||
g_string_append_printf (s, "%02x", buf[0]);
|
||||
for (i = 1; i < len; i++)
|
||||
g_string_append_printf (s, " %02x", buf[i]);
|
||||
return g_string_free (s, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
test_win32_zero_terminate_symlink (void)
|
||||
{
|
||||
gsize i;
|
||||
#define TESTCASE(data, len_mod, use_buf, buf_size, terminate, reported_len, returned_string) \
|
||||
{ (const guchar *) data, wcslen (data) * 2 + len_mod, use_buf, buf_size, terminate, reported_len, (guchar *) returned_string},
|
||||
|
||||
struct
|
||||
{
|
||||
const guchar *data;
|
||||
gsize data_size;
|
||||
gboolean use_buf;
|
||||
gsize buf_size;
|
||||
gboolean terminate;
|
||||
int reported_len;
|
||||
const guchar *returned_string;
|
||||
} testcases[] = {
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 + 4, FALSE, 12 + 2, "f\0o\0o\0b\0a\0r\0\0\0")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 + 3, FALSE, 12 + 2, "f\0o\0o\0b\0a\0r\0\0\0")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 + 2, FALSE, 12 + 2, "f\0o\0o\0b\0a\0r\0\0\0")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 + 1, FALSE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 + 0, FALSE, 12 + 0, "f\0o\0o\0b\0a\0r\0")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 - 1, FALSE, 12 - 1, "f\0o\0o\0b\0a\0r")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 - 2, FALSE, 12 - 2, "f\0o\0o\0b\0a\0")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 - 3, FALSE, 12 - 3, "f\0o\0o\0b\0a")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 + 4, FALSE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 + 3, FALSE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 + 2, FALSE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 + 1, FALSE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 + 0, FALSE, 12 + 0, "f\0o\0o\0b\0a\0r\0")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 - 1, FALSE, 12 - 1, "f\0o\0o\0b\0a\0r")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 - 2, FALSE, 12 - 2, "f\0o\0o\0b\0a\0")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 - 3, FALSE, 12 - 3, "f\0o\0o\0b\0a")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 + 4, FALSE, 12 + 0, "f\0o\0o\0b\0a\0r\0")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 + 3, FALSE, 12 + 0, "f\0o\0o\0b\0a\0r\0")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 + 2, FALSE, 12 + 0, "f\0o\0o\0b\0a\0r\0")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 + 1, FALSE, 12 + 0, "f\0o\0o\0b\0a\0r\0")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 + 0, FALSE, 12 + 0, "f\0o\0o\0b\0a\0r\0")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 - 1, FALSE, 12 - 1, "f\0o\0o\0b\0a\0r")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 - 2, FALSE, 12 - 2, "f\0o\0o\0b\0a\0")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 - 3, FALSE, 12 - 3, "f\0o\0o\0b\0a")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 + 3, FALSE, 12 - 1, "f\0o\0o\0b\0a\0r")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 + 2, FALSE, 12 - 1, "f\0o\0o\0b\0a\0r")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 + 1, FALSE, 12 - 1, "f\0o\0o\0b\0a\0r")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 + 0, FALSE, 12 - 1, "f\0o\0o\0b\0a\0r")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 - 1, FALSE, 12 - 1, "f\0o\0o\0b\0a\0r")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 - 2, FALSE, 12 - 2, "f\0o\0o\0b\0a\0")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 - 3, FALSE, 12 - 3, "f\0o\0o\0b\0a")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 - 4, FALSE, 12 - 4, "f\0o\0o\0b\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 + 2, FALSE, 12 - 2, "f\0o\0o\0b\0a\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 + 1, FALSE, 12 - 2, "f\0o\0o\0b\0a\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 + 0, FALSE, 12 - 2, "f\0o\0o\0b\0a\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 - 1, FALSE, 12 - 2, "f\0o\0o\0b\0a\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 - 2, FALSE, 12 - 2, "f\0o\0o\0b\0a\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 - 3, FALSE, 12 - 3, "f\0o\0o\0b\0a")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 - 4, FALSE, 12 - 4, "f\0o\0o\0b\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 - 5, FALSE, 12 - 5, "f\0o\0o\0b")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 + 4, TRUE, 12 + 2, "f\0o\0o\0b\0a\0r\0\0\0")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 + 3, TRUE, 12 + 2, "f\0o\0o\0b\0a\0r\0\0\0")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 + 2, TRUE, 12 + 2, "f\0o\0o\0b\0a\0r\0\0\0")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 + 1, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 + 0, TRUE, 12 + 0, "f\0o\0o\0b\0a\0\0\0")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 - 1, TRUE, 12 - 1, "f\0o\0o\0b\0a\0\0")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 - 2, TRUE, 12 - 2, "f\0o\0o\0b\0\0\0")
|
||||
TESTCASE (L"foobar", +2, TRUE, 12 - 3, TRUE, 12 - 3, "f\0o\0o\0b\0\0")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 + 4, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 + 3, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 + 2, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 + 1, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 + 0, TRUE, 12 + 0, "f\0o\0o\0b\0a\0\0\0")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 - 1, TRUE, 12 - 1, "f\0o\0o\0b\0a\0\0")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 - 2, TRUE, 12 - 2, "f\0o\0o\0b\0\0\0")
|
||||
TESTCASE (L"foobar", +1, TRUE, 12 - 3, TRUE, 12 - 3, "f\0o\0o\0b\0\0")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 + 4, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 + 3, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 + 2, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 + 1, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 + 0, TRUE, 12 + 0, "f\0o\0o\0b\0a\0\0\0")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 - 1, TRUE, 12 - 1, "f\0o\0o\0b\0a\0\0")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 - 2, TRUE, 12 - 2, "f\0o\0o\0b\0\0\0")
|
||||
TESTCASE (L"foobar", +0, TRUE, 12 - 3, TRUE, 12 - 3, "f\0o\0o\0b\0\0")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 + 3, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 + 2, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 + 1, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 + 0, TRUE, 12 + 0, "f\0o\0o\0b\0a\0\0\0")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 - 1, TRUE, 12 - 1, "f\0o\0o\0b\0a\0\0")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 - 2, TRUE, 12 - 2, "f\0o\0o\0b\0\0\0")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 - 3, TRUE, 12 - 3, "f\0o\0o\0b\0\0")
|
||||
TESTCASE (L"foobar", -1, TRUE, 12 - 4, TRUE, 12 - 4, "f\0o\0o\0\0\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 + 2, TRUE, 12 - 1, "f\0o\0o\0b\0a\0\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 + 1, TRUE, 12 - 1, "f\0o\0o\0b\0a\0\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 + 0, TRUE, 12 - 1, "f\0o\0o\0b\0a\0\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 - 1, TRUE, 12 - 1, "f\0o\0o\0b\0a\0\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 - 2, TRUE, 12 - 2, "f\0o\0o\0b\0\0\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 - 3, TRUE, 12 - 3, "f\0o\0o\0b\0\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 - 4, TRUE, 12 - 4, "f\0o\0o\0\0\0")
|
||||
TESTCASE (L"foobar", -2, TRUE, 12 - 5, TRUE, 12 - 5, "f\0o\0o\0\0")
|
||||
TESTCASE (L"foobar", +2, FALSE, 0, FALSE, 12 + 2, "f\0o\0o\0b\0a\0r\0\0\0")
|
||||
TESTCASE (L"foobar", +1, FALSE, 0, FALSE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +0, FALSE, 0, FALSE, 12 + 0, "f\0o\0o\0b\0a\0r\0")
|
||||
TESTCASE (L"foobar", -1, FALSE, 0, FALSE, 12 - 1, "f\0o\0o\0b\0a\0r")
|
||||
TESTCASE (L"foobar", -2, FALSE, 0, FALSE, 12 - 2, "f\0o\0o\0b\0a\0")
|
||||
TESTCASE (L"foobar", +2, FALSE, 0, TRUE, 12 + 2, "f\0o\0o\0b\0a\0r\0\0\0")
|
||||
TESTCASE (L"foobar", +1, FALSE, 0, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", +0, FALSE, 0, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", -1, FALSE, 0, TRUE, 12 + 1, "f\0o\0o\0b\0a\0r\0\0")
|
||||
TESTCASE (L"foobar", -2, FALSE, 0, TRUE, 12 - 1, "f\0o\0o\0b\0a\0\0")
|
||||
TESTCASE (L"x", +2, TRUE, 2 + 4, FALSE, 2 + 2, "x\0\0\0")
|
||||
TESTCASE (L"x", +2, TRUE, 2 + 3, FALSE, 2 + 2, "x\0\0\0")
|
||||
TESTCASE (L"x", +2, TRUE, 2 + 2, FALSE, 2 + 2, "x\0\0\0")
|
||||
TESTCASE (L"x", +2, TRUE, 2 + 1, FALSE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", +2, TRUE, 2 + 0, FALSE, 2 + 0, "x\0")
|
||||
TESTCASE (L"x", +2, TRUE, 2 - 1, FALSE, 2 - 1, "x")
|
||||
TESTCASE (L"x", +2, TRUE, 2 - 2, FALSE, 2 - 2, "")
|
||||
TESTCASE (L"x", +1, TRUE, 2 + 3, FALSE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", +1, TRUE, 2 + 2, FALSE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", +1, TRUE, 2 + 1, FALSE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", +1, TRUE, 2 + 0, FALSE, 2 + 0, "x\0")
|
||||
TESTCASE (L"x", +1, TRUE, 2 - 1, FALSE, 2 - 1, "x")
|
||||
TESTCASE (L"x", +1, TRUE, 2 - 2, FALSE, 2 - 2, "")
|
||||
TESTCASE (L"x", +0, TRUE, 2 + 2, FALSE, 2 + 0, "x\0")
|
||||
TESTCASE (L"x", +0, TRUE, 2 + 1, FALSE, 2 + 0, "x\0")
|
||||
TESTCASE (L"x", +0, TRUE, 2 + 0, FALSE, 2 + 0, "x\0")
|
||||
TESTCASE (L"x", +0, TRUE, 2 - 1, FALSE, 2 - 1, "x")
|
||||
TESTCASE (L"x", +0, TRUE, 2 - 2, FALSE, 2 - 2, "")
|
||||
TESTCASE (L"x", -1, TRUE, 2 + 1, FALSE, 2 - 1, "x")
|
||||
TESTCASE (L"x", -1, TRUE, 2 + 0, FALSE, 2 - 1, "x")
|
||||
TESTCASE (L"x", -1, TRUE, 2 - 1, FALSE, 2 - 1, "x")
|
||||
TESTCASE (L"x", -1, TRUE, 2 - 2, FALSE, 2 - 2, "")
|
||||
TESTCASE (L"x", -2, TRUE, 2 + 0, FALSE, 2 - 2, "")
|
||||
TESTCASE (L"x", -2, TRUE, 2 - 1, FALSE, 2 - 2, "")
|
||||
TESTCASE (L"x", -2, TRUE, 2 - 2, FALSE, 2 - 2, "")
|
||||
TESTCASE (L"x", +2, TRUE, 2 + 4, TRUE, 2 + 2, "x\0\0\0")
|
||||
TESTCASE (L"x", +2, TRUE, 2 + 3, TRUE, 2 + 2, "x\0\0\0")
|
||||
TESTCASE (L"x", +2, TRUE, 2 + 2, TRUE, 2 + 2, "x\0\0\0")
|
||||
TESTCASE (L"x", +2, TRUE, 2 + 1, TRUE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", +2, TRUE, 2 + 0, TRUE, 2 + 0, "\0\0")
|
||||
TESTCASE (L"x", +2, TRUE, 2 - 1, TRUE, 2 - 1, "\0")
|
||||
TESTCASE (L"x", +2, TRUE, 2 - 2, TRUE, 2 - 2, "")
|
||||
TESTCASE (L"x", +1, TRUE, 2 + 3, TRUE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", +1, TRUE, 2 + 2, TRUE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", +1, TRUE, 2 + 1, TRUE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", +1, TRUE, 2 + 0, TRUE, 2 + 0, "\0\0")
|
||||
TESTCASE (L"x", +1, TRUE, 2 - 1, TRUE, 2 - 1, "\0")
|
||||
TESTCASE (L"x", +1, TRUE, 2 - 2, TRUE, 2 - 2, "")
|
||||
TESTCASE (L"x", +0, TRUE, 2 + 2, TRUE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", +0, TRUE, 2 + 1, TRUE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", +0, TRUE, 2 + 0, TRUE, 2 + 0, "\0\0")
|
||||
TESTCASE (L"x", +0, TRUE, 2 - 1, TRUE, 2 - 1, "\0")
|
||||
TESTCASE (L"x", +0, TRUE, 2 - 2, TRUE, 2 - 2, "")
|
||||
TESTCASE (L"x", -1, TRUE, 2 + 1, TRUE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", -1, TRUE, 2 + 0, TRUE, 2 + 0, "\0\0")
|
||||
TESTCASE (L"x", -1, TRUE, 2 - 1, TRUE, 2 - 1, "\0")
|
||||
TESTCASE (L"x", -1, TRUE, 2 - 2, TRUE, 2 - 2, "")
|
||||
TESTCASE (L"x", -2, TRUE, 2 + 0, TRUE, 2 - 2, "")
|
||||
TESTCASE (L"x", -2, TRUE, 2 - 1, TRUE, 2 - 2, "")
|
||||
TESTCASE (L"x", -2, TRUE, 2 - 2, TRUE, 2 - 2, "")
|
||||
TESTCASE (L"x", +2, FALSE, 0, FALSE, 2 + 2, "x\0\0\0")
|
||||
TESTCASE (L"x", +1, FALSE, 0, FALSE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", +0, FALSE, 0, FALSE, 2 + 0, "x\0")
|
||||
TESTCASE (L"x", -1, FALSE, 0, FALSE, 2 - 1, "x")
|
||||
TESTCASE (L"x", -2, FALSE, 0, FALSE, 2 - 2, "")
|
||||
TESTCASE (L"x", +2, FALSE, 0, TRUE, 2 + 2, "x\0\0\0")
|
||||
TESTCASE (L"x", +1, FALSE, 0, TRUE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", +0, FALSE, 0, TRUE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", -1, FALSE, 0, TRUE, 2 + 1, "x\0\0")
|
||||
TESTCASE (L"x", -2, FALSE, 0, TRUE, 2 - 2, "")
|
||||
{ 0, },
|
||||
};
|
||||
#undef TESTCASE
|
||||
|
||||
for (i = 0; testcases[i].data != NULL; i++)
|
||||
{
|
||||
gunichar2 *buf;
|
||||
int result;
|
||||
gchar *buf_hex, *expected_hex;
|
||||
if (testcases[i].use_buf)
|
||||
buf = g_malloc0 (testcases[i].buf_size + 1); /* +1 to ensure it succeeds with buf_size == 0 */
|
||||
else
|
||||
buf = NULL;
|
||||
result = _g_win32_copy_and_maybe_terminate (testcases[i].data,
|
||||
testcases[i].data_size,
|
||||
testcases[i].use_buf ? buf : NULL,
|
||||
testcases[i].buf_size,
|
||||
testcases[i].use_buf ? NULL : &buf,
|
||||
testcases[i].terminate);
|
||||
if (testcases[i].reported_len != result)
|
||||
g_error ("Test %" G_GSIZE_FORMAT " failed, result %d != %d", i, result, testcases[i].reported_len);
|
||||
if (buf == NULL && testcases[i].buf_size != 0)
|
||||
g_error ("Test %" G_GSIZE_FORMAT " failed, buf == NULL", i);
|
||||
g_assert_cmpint (testcases[i].reported_len, ==, result);
|
||||
if ((testcases[i].use_buf && testcases[i].buf_size != 0) ||
|
||||
(!testcases[i].use_buf && testcases[i].reported_len != 0))
|
||||
{
|
||||
g_assert_nonnull (buf);
|
||||
buf_hex = to_hex ((const guchar *) buf, result);
|
||||
expected_hex = to_hex (testcases[i].returned_string, testcases[i].reported_len);
|
||||
if (memcmp (buf, testcases[i].returned_string, result) != 0)
|
||||
g_error ("Test %" G_GSIZE_FORMAT " failed:\n%s !=\n%s", i, buf_hex, expected_hex);
|
||||
g_assert_memcmp (buf, ==, testcases[i].returned_string, testcases[i].reported_len, buf_hex, expected_hex, testcases[i].line);
|
||||
g_free (buf_hex);
|
||||
g_free (expected_hex);
|
||||
}
|
||||
g_free (buf);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int
|
||||
@ -1166,6 +1396,7 @@ main (int argc,
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
g_test_add_func ("/fileutils/stdio-win32-pathstrip", test_win32_pathstrip);
|
||||
g_test_add_func ("/fileutils/stdio-win32-zero-terminate-symlink", test_win32_zero_terminate_symlink);
|
||||
#endif
|
||||
g_test_add_func ("/fileutils/build-path", test_build_path);
|
||||
g_test_add_func ("/fileutils/build-pathv", test_build_pathv);
|
||||
|
Loading…
Reference in New Issue
Block a user