W32: Add a stat() implementation for private use

This commit adds new W32-only functions to gstdio.c,
and a new header file, gstdioprivate.h.
These functions are:
g_win32_stat_utf8()
g_win32_lstat_utf8()
g_win32_fstat()
and they fill a private structure, GWin32PrivateStat,
which has all the fields that normal stat has, as well as some
extras.

These functions are then used throughout glib and gio to get better
data about the system. Specifically:
* Full, 64-bit size, guaranteed (g_stat() is forced to use 32-bit st_size)
* Full, 64-bit file identifier (st_ino is 0 when normal stat() is used, and still is)
* W32 File attributes (which stat() doesn't report); in particular, this allows
  symlinks to be correctly identified
* Full, 64-bit time, guaranteed (g_stat() uses 32-bit st_*time on 32-bit Windows)
* Allocated file size (as a W32 replacement for the missing st_blocks)

st_mode remains unchanged (thus, no S_ISLNK), so when these are given back to
glib users (via g_stat(), for example, which is now implemented by calling g_win32_stat_utf8),
this field does not contain anything unexpected.

g_lstat() now calls g_win32_lstat_utf8(), which works on symlinks the way it's supposed to.

Also adds the g_win32_readlink_utf8() function, which behaves like readlink()
(including its inability to return 0-terminated strings and inability to say how large
the output buffer should be; these limitations are purely for compatibility with
existing glib code).

Thus, symlink support should now be much better, although far from being complete.

A new W32-only test in gio/tests/file.c highlights the following features:
* allocated size
* 64-bit time
* unique file IDs

https://bugzilla.gnome.org/show_bug.cgi?id=788180
This commit is contained in:
Руслан Ижбулатов
2017-09-29 10:14:41 +00:00
committed by Philip Withnall
parent c74ab4a1db
commit 53bd6a359f
11 changed files with 1105 additions and 102 deletions

View File

@@ -62,11 +62,11 @@
#include "gunixmounts.h"
#include "gioerror.h"
#include <glib/gstdio.h>
#include <glib/gstdioprivate.h>
#include "glibintl.h"
#ifdef G_OS_UNIX
#include "glib-unix.h"
#endif
#include "glib-private.h"
#include "glib-private.h"
@@ -1395,7 +1395,8 @@ g_local_file_read (GFile *file,
#ifdef G_OS_WIN32
if (errsv == EACCES)
{
ret = _stati64 (local->filename, &buf);
/* Exploit the fact that on W32 the glib filename encoding is UTF8 */
ret = GLIB_PRIVATE_CALL (g_win32_stat_utf8) (local->filename, &buf);
if (ret == 0 && S_ISDIR (buf.st_mode))
errsv = EISDIR;
}
@@ -1407,7 +1408,7 @@ g_local_file_read (GFile *file,
}
#ifdef G_OS_WIN32
ret = _fstati64 (fd, &buf);
ret = GLIB_PRIVATE_CALL (g_win32_fstat) (fd, &buf);
#else
ret = fstat (fd, &buf);
#endif
@@ -2677,33 +2678,12 @@ g_local_file_measure_size_of_file (gint parent_fd,
int errsv = errno;
return g_local_file_measure_size_error (state->flags, errsv, name, error);
}
#else
{
const char *filename = (const gchar *) name->data;
wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
int retval;
int save_errno;
int len;
if (wfilename == NULL)
return g_local_file_measure_size_error (state->flags, errno, name, error);
len = wcslen (wfilename);
while (len > 0 && G_IS_DIR_SEPARATOR (wfilename[len-1]))
len--;
if (len > 0 &&
(!g_path_is_absolute (filename) || len > g_path_skip_root (filename) - filename))
wfilename[len] = '\0';
retval = _wstati64 (wfilename, &buf);
save_errno = errno;
g_free (wfilename);
errno = save_errno;
if (retval != 0)
return g_local_file_measure_size_error (state->flags, errno, name, error);
}
#else /* !AT_FDCWD && !HAVE_LSTAT && G_OS_WIN32 */
if (GLIB_PRIVATE_CALL (g_win32_lstat_utf8) (name->data, &buf) != 0)
{
int errsv = errno;
return g_local_file_measure_size_error (state->flags, errsv, name, error);
}
#endif
if (name->next)
@@ -2722,7 +2702,11 @@ g_local_file_measure_size_of_file (gint parent_fd,
state->contained_on = buf.st_dev;
}
#if defined (HAVE_STRUCT_STAT_ST_BLOCKS)
#if defined (G_OS_WIN32)
if (~state->flags & G_FILE_MEASURE_APPARENT_SIZE)
state->disk_usage += buf.allocated_size;
else
#elif defined (HAVE_STRUCT_STAT_ST_BLOCKS)
if (~state->flags & G_FILE_MEASURE_APPARENT_SIZE)
state->disk_usage += buf.st_blocks * G_GUINT64_CONSTANT (512);
else