mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-30 04:44:16 +02:00
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:
committed by
Philip Withnall
parent
c74ab4a1db
commit
53bd6a359f
@@ -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
|
||||
|
@@ -53,6 +53,7 @@
|
||||
#endif /* HAVE_XATTR */
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
#include <glib/gstdioprivate.h>
|
||||
#include <gfileattribute-priv.h>
|
||||
#include <gfileinfo-priv.h>
|
||||
#include <gvfs.h>
|
||||
@@ -60,9 +61,10 @@
|
||||
#ifdef G_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#include "glib-unix.h"
|
||||
#include "glib-private.h"
|
||||
#endif
|
||||
|
||||
#include "glib-private.h"
|
||||
|
||||
#include "thumbnail-verify.h"
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
@@ -136,9 +138,15 @@ _g_local_file_info_create_etag (GLocalFileStat *statbuf)
|
||||
static char *
|
||||
_g_local_file_info_create_file_id (GLocalFileStat *statbuf)
|
||||
{
|
||||
guint64 ino;
|
||||
#ifdef G_OS_WIN32
|
||||
ino = statbuf->file_index;
|
||||
#else
|
||||
ino = statbuf->st_ino;
|
||||
#endif
|
||||
return g_strdup_printf ("l%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT,
|
||||
(guint64) statbuf->st_dev,
|
||||
(guint64) statbuf->st_ino);
|
||||
ino);
|
||||
}
|
||||
|
||||
static char *
|
||||
@@ -148,13 +156,12 @@ _g_local_file_info_create_fs_id (GLocalFileStat *statbuf)
|
||||
(guint64) statbuf->st_dev);
|
||||
}
|
||||
|
||||
|
||||
#ifdef S_ISLNK
|
||||
#if defined (S_ISLNK) || defined (G_OS_WIN32)
|
||||
|
||||
static gchar *
|
||||
read_link (const gchar *full_name)
|
||||
{
|
||||
#ifdef HAVE_READLINK
|
||||
#if defined (HAVE_READLINK) || defined (G_OS_WIN32)
|
||||
gchar *buffer;
|
||||
guint size;
|
||||
|
||||
@@ -165,7 +172,11 @@ read_link (const gchar *full_name)
|
||||
{
|
||||
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);
|
||||
@@ -184,7 +195,7 @@ read_link (const gchar *full_name)
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* S_ISLNK */
|
||||
#endif /* S_ISLNK || G_OS_WIN32 */
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
/* Get the SELinux security context */
|
||||
@@ -938,6 +949,9 @@ set_info_from_stat (GFileInfo *info,
|
||||
#ifdef S_ISLNK
|
||||
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)
|
||||
file_type = G_FILE_TYPE_SYMBOLIC_LINK;
|
||||
#endif
|
||||
|
||||
g_file_info_set_file_type (info, file_type);
|
||||
@@ -960,7 +974,11 @@ set_info_from_stat (GFileInfo *info,
|
||||
#if defined (HAVE_STRUCT_STAT_ST_BLOCKS)
|
||||
_g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCKS, statbuf->st_blocks);
|
||||
_g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_ALLOCATED_SIZE,
|
||||
statbuf->st_blocks * G_GUINT64_CONSTANT (512));
|
||||
statbuf->st_blocks * G_GUINT64_CONSTANT (512));
|
||||
#elif defined (G_OS_WIN32)
|
||||
_g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_ALLOCATED_SIZE,
|
||||
statbuf->allocated_size);
|
||||
|
||||
#endif
|
||||
|
||||
_g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED, statbuf->st_mtime);
|
||||
@@ -1711,13 +1729,12 @@ _g_local_file_info_get (const char *basename,
|
||||
GLocalFileStat statbuf;
|
||||
#ifdef S_ISLNK
|
||||
struct stat statbuf2;
|
||||
#elif defined (G_OS_WIN32)
|
||||
GWin32PrivateStat statbuf2;
|
||||
#endif
|
||||
int res;
|
||||
gboolean stat_ok;
|
||||
gboolean is_symlink, symlink_broken;
|
||||
#ifdef G_OS_WIN32
|
||||
DWORD dos_attributes;
|
||||
#endif
|
||||
char *symlink_target;
|
||||
GVfs *vfs;
|
||||
GVfsClass *class;
|
||||
@@ -1739,28 +1756,7 @@ _g_local_file_info_get (const char *basename,
|
||||
#ifndef G_OS_WIN32
|
||||
res = g_lstat (path, &statbuf);
|
||||
#else
|
||||
{
|
||||
wchar_t *wpath = g_utf8_to_utf16 (path, -1, NULL, NULL, error);
|
||||
int len;
|
||||
|
||||
if (wpath == NULL)
|
||||
{
|
||||
g_object_unref (info);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = wcslen (wpath);
|
||||
while (len > 0 && G_IS_DIR_SEPARATOR (wpath[len-1]))
|
||||
len--;
|
||||
if (len > 0 &&
|
||||
(!g_path_is_absolute (path) || len > g_path_skip_root (path) - path))
|
||||
wpath[len] = '\0';
|
||||
|
||||
res = _wstati64 (wpath, &statbuf);
|
||||
dos_attributes = GetFileAttributesW (wpath);
|
||||
|
||||
g_free (wpath);
|
||||
}
|
||||
res = GLIB_PRIVATE_CALL (g_win32_lstat_utf8) (path, &statbuf);
|
||||
#endif
|
||||
|
||||
if (res == -1)
|
||||
@@ -1791,11 +1787,14 @@ _g_local_file_info_get (const char *basename,
|
||||
|
||||
#ifdef S_ISLNK
|
||||
is_symlink = stat_ok && S_ISLNK (statbuf.st_mode);
|
||||
#elif defined (G_OS_WIN32)
|
||||
/* glib already checked the FILE_ATTRIBUTE_REPARSE_POINT for us */
|
||||
is_symlink = stat_ok && statbuf.reparse_tag == IO_REPARSE_TAG_SYMLINK;
|
||||
#else
|
||||
is_symlink = FALSE;
|
||||
#endif
|
||||
symlink_broken = FALSE;
|
||||
#ifdef S_ISLNK
|
||||
|
||||
if (is_symlink)
|
||||
{
|
||||
g_file_info_set_is_symlink (info, TRUE);
|
||||
@@ -1803,7 +1802,11 @@ _g_local_file_info_get (const char *basename,
|
||||
/* Unless NOFOLLOW was set we default to following symlinks */
|
||||
if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))
|
||||
{
|
||||
#ifndef G_OS_WIN32
|
||||
res = stat (path, &statbuf2);
|
||||
#else
|
||||
res = GLIB_PRIVATE_CALL (g_win32_stat_utf8) (path, &statbuf2);
|
||||
#endif
|
||||
|
||||
/* Report broken links as symlinks */
|
||||
if (res != -1)
|
||||
@@ -1815,7 +1818,6 @@ _g_local_file_info_get (const char *basename,
|
||||
symlink_broken = TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (stat_ok)
|
||||
set_info_from_stat (info, &statbuf, attribute_matcher);
|
||||
@@ -1839,27 +1841,28 @@ _g_local_file_info_get (const char *basename,
|
||||
(stat_ok && S_ISREG (statbuf.st_mode)))
|
||||
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP, TRUE);
|
||||
#else
|
||||
if (dos_attributes & FILE_ATTRIBUTE_HIDDEN)
|
||||
if (statbuf.attributes & FILE_ATTRIBUTE_HIDDEN)
|
||||
g_file_info_set_is_hidden (info, TRUE);
|
||||
|
||||
if (dos_attributes & FILE_ATTRIBUTE_ARCHIVE)
|
||||
if (statbuf.attributes & FILE_ATTRIBUTE_ARCHIVE)
|
||||
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_ARCHIVE, TRUE);
|
||||
|
||||
if (dos_attributes & FILE_ATTRIBUTE_SYSTEM)
|
||||
if (statbuf.attributes & FILE_ATTRIBUTE_SYSTEM)
|
||||
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_SYSTEM, TRUE);
|
||||
#endif
|
||||
|
||||
symlink_target = NULL;
|
||||
#ifdef S_ISLNK
|
||||
if (is_symlink)
|
||||
{
|
||||
#if defined (S_ISLNK) || defined (G_OS_WIN32)
|
||||
symlink_target = read_link (path);
|
||||
#endif
|
||||
if (symlink_target &&
|
||||
_g_file_attribute_matcher_matches_id (attribute_matcher,
|
||||
G_FILE_ATTRIBUTE_ID_STANDARD_SYMLINK_TARGET))
|
||||
g_file_info_set_symlink_target (info, symlink_target);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_g_file_attribute_matcher_matches_id (attribute_matcher,
|
||||
G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) ||
|
||||
_g_file_attribute_matcher_matches_id (attribute_matcher,
|
||||
@@ -2014,7 +2017,7 @@ _g_local_file_info_get_from_fd (int fd,
|
||||
GFileInfo *info;
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#define FSTAT _fstati64
|
||||
#define FSTAT GLIB_PRIVATE_CALL (g_win32_fstat)
|
||||
#else
|
||||
#define FSTAT fstat
|
||||
#endif
|
||||
@@ -2148,16 +2151,26 @@ set_unix_mode (char *filename,
|
||||
if (!get_uint32 (value, &val, error))
|
||||
return FALSE;
|
||||
|
||||
#ifdef HAVE_SYMLINK
|
||||
#if defined (HAVE_SYMLINK) || defined (G_OS_WIN32)
|
||||
if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) {
|
||||
#ifdef HAVE_LCHMOD
|
||||
res = lchmod (filename, val);
|
||||
#else
|
||||
gboolean is_symlink;
|
||||
#ifndef G_OS_WIN32
|
||||
struct stat statbuf;
|
||||
/* Calling chmod on a symlink changes permissions on the symlink.
|
||||
* We don't want to do this, so we need to check for a symlink */
|
||||
res = g_lstat (filename, &statbuf);
|
||||
if (res == 0 && S_ISLNK (statbuf.st_mode))
|
||||
is_symlink = (res == 0 && S_ISLNK (statbuf.st_mode));
|
||||
#else
|
||||
/* FIXME: implement lchmod for W32, should be doable */
|
||||
GWin32PrivateStat statbuf;
|
||||
|
||||
res = GLIB_PRIVATE_CALL (g_win32_lstat_utf8) (filename, &statbuf);
|
||||
is_symlink = (res == 0 && statbuf.reparse_tag == IO_REPARSE_TAG_SYMLINK);
|
||||
#endif
|
||||
if (is_symlink)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
|
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <gio/gfileinfo.h>
|
||||
#include <gio/gfile.h>
|
||||
#include <glib/gstdioprivate.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -40,8 +41,8 @@ typedef struct
|
||||
} GLocalParentFileInfo;
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
/* We want 64-bit file size support */
|
||||
#define GLocalFileStat struct _stati64
|
||||
/* We want 64-bit file size, file ID and symlink support */
|
||||
#define GLocalFileStat GWin32PrivateStat
|
||||
#else
|
||||
#define GLocalFileStat struct stat
|
||||
#endif
|
||||
|
@@ -40,6 +40,8 @@
|
||||
#include "gfiledescriptorbased.h"
|
||||
#endif
|
||||
|
||||
#include "glib-private.h"
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <io.h>
|
||||
#ifndef S_ISDIR
|
||||
@@ -234,7 +236,7 @@ _g_local_file_output_stream_really_close (GLocalFileOutputStream *file,
|
||||
/* Must close before renaming on Windows, so just do the close first
|
||||
* in all cases for now.
|
||||
*/
|
||||
if (_fstati64 (file->priv->fd, &final_stat) == 0)
|
||||
if (GLIB_PRIVATE_CALL (g_win32_fstat) (file->priv->fd, &final_stat) == 0)
|
||||
file->priv->etag = _g_local_file_info_create_etag (&final_stat);
|
||||
|
||||
if (!g_close (file->priv->fd, NULL))
|
||||
@@ -797,7 +799,7 @@ handle_overwrite_open (const char *filename,
|
||||
}
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
res = _fstati64 (fd, &original_stat);
|
||||
res = GLIB_PRIVATE_CALL (g_win32_fstat) (fd, &original_stat);
|
||||
#else
|
||||
res = fstat (fd, &original_stat);
|
||||
#endif
|
||||
@@ -891,7 +893,7 @@ handle_overwrite_open (const char *filename,
|
||||
int tres;
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
tres = _fstati64 (tmpfd, &tmp_statbuf);
|
||||
tres = GLIB_PRIVATE_CALL (g_win32_fstat) (tmpfd, &tmp_statbuf);
|
||||
#else
|
||||
tres = fstat (tmpfd, &tmp_statbuf);
|
||||
#endif
|
||||
|
@@ -20,10 +20,18 @@
|
||||
* if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib/glib.h>
|
||||
#include <gio/gio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef G_OS_WIN32
|
||||
#include <stdio.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <Windows.h>
|
||||
#include <Shlobj.h>
|
||||
#endif
|
||||
|
||||
#define TEST_NAME "Prilis zlutoucky kun"
|
||||
#define TEST_DISPLAY_NAME "UTF-8 p\xc5\x99\xc3\xadli\xc5\xa1 \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88"
|
||||
@@ -132,6 +140,362 @@ test_g_file_info (void)
|
||||
g_object_unref (info_copy);
|
||||
}
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
static void
|
||||
test_internal_enhanced_stdio (void)
|
||||
{
|
||||
char *p0, *p1, *ps;
|
||||
gboolean try_sparse;
|
||||
gchar *tmp_dir_root;
|
||||
wchar_t *tmp_dir_root_w;
|
||||
gchar *c;
|
||||
DWORD fsflags;
|
||||
FILE *f;
|
||||
SYSTEMTIME st;
|
||||
FILETIME ft;
|
||||
HANDLE h;
|
||||
GStatBuf statbuf_p0, statbuf_p1, statbuf_ps;
|
||||
GFile *gf_p0, *gf_p1, *gf_ps;
|
||||
GFileInfo *fi_p0, *fi_p1, *fi_ps;
|
||||
guint64 size_p0, alsize_p0, size_ps, alsize_ps;
|
||||
const gchar *id_p0;
|
||||
const gchar *id_p1;
|
||||
volatile guint64 time_p0;
|
||||
gchar *tmp_dir;
|
||||
wchar_t *programdata_dir_w;
|
||||
wchar_t *users_dir_w;
|
||||
static const GUID folder_id_programdata =
|
||||
{ 0x62AB5D82, 0xFDC1, 0x4DC3, { 0xA9, 0xDD, 0x07, 0x0D, 0x1D, 0x49, 0x5D, 0x97 } };
|
||||
static const GUID folder_id_users =
|
||||
{ 0x0762D272, 0xC50A, 0x4BB0, { 0xA3, 0x82, 0x69, 0x7D, 0xCD, 0x72, 0x9B, 0x80 } };
|
||||
|
||||
programdata_dir_w = NULL;
|
||||
SHGetKnownFolderPath (&folder_id_programdata, 0, NULL, &programdata_dir_w);
|
||||
|
||||
users_dir_w = NULL;
|
||||
SHGetKnownFolderPath (&folder_id_users, 0, NULL, &users_dir_w);
|
||||
|
||||
if (programdata_dir_w != NULL && users_dir_w != NULL)
|
||||
{
|
||||
gchar *programdata;
|
||||
gchar *users_dir;
|
||||
gchar *allusers;
|
||||
GFile *gf_programdata, *gf_allusers;
|
||||
GFileInfo *fi_programdata, *fi_allusers, *fi_allusers_target;
|
||||
GFileType ft_allusers;
|
||||
GFileType ft_allusers_target;
|
||||
GFileType ft_programdata;
|
||||
gboolean allusers_is_symlink;
|
||||
const gchar *id_allusers;
|
||||
const gchar *id_allusers_target;
|
||||
const gchar *id_programdata;
|
||||
const gchar *allusers_target;
|
||||
|
||||
/* C:/ProgramData */
|
||||
programdata = g_utf16_to_utf8 (programdata_dir_w, -1, NULL, NULL, NULL);
|
||||
g_assert_nonnull (programdata);
|
||||
/* C:/Users */
|
||||
users_dir = g_utf16_to_utf8 (users_dir_w, -1, NULL, NULL, NULL);
|
||||
g_assert_nonnull (users_dir);
|
||||
/* "C:/Users/All Users" is a known directory symlink
|
||||
* for "C:/ProgramData".
|
||||
*/
|
||||
allusers = g_build_filename (users_dir, "All Users", NULL);
|
||||
g_assert_nonnull (allusers);
|
||||
|
||||
/* We don't test g_stat() and g_lstat() on these directories,
|
||||
* because it is pointless - there's no way to tell that these
|
||||
* functions behave correctly in this case
|
||||
* (st_ino is useless, so we can't tell apart g_stat() and g_lstat()
|
||||
* results; st_mode is also useless as it does not support S_ISLNK;
|
||||
* and these directories have no interesting properties other
|
||||
* than [not] being symlinks).
|
||||
*/
|
||||
gf_programdata = g_file_new_for_path (programdata);
|
||||
gf_allusers = g_file_new_for_path (allusers);
|
||||
|
||||
fi_programdata = g_file_query_info (gf_programdata,
|
||||
G_FILE_ATTRIBUTE_ID_FILE ","
|
||||
G_FILE_ATTRIBUTE_STANDARD_TYPE,
|
||||
G_FILE_QUERY_INFO_NONE,
|
||||
NULL, NULL);
|
||||
|
||||
fi_allusers_target = g_file_query_info (gf_allusers,
|
||||
G_FILE_ATTRIBUTE_ID_FILE ","
|
||||
G_FILE_ATTRIBUTE_STANDARD_TYPE,
|
||||
G_FILE_QUERY_INFO_NONE,
|
||||
NULL, NULL);
|
||||
|
||||
fi_allusers = g_file_query_info (gf_allusers,
|
||||
G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
|
||||
G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
allusers_is_symlink = g_file_info_get_attribute_boolean (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK);
|
||||
|
||||
g_assert_true (allusers_is_symlink);
|
||||
|
||||
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_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_allusers, !=, id_programdata);
|
||||
|
||||
allusers_target = g_file_info_get_symlink_target (fi_allusers);
|
||||
|
||||
g_assert_true (g_str_has_suffix (allusers_target, "ProgramData"));
|
||||
|
||||
g_object_unref (fi_allusers);
|
||||
g_object_unref (fi_allusers_target);
|
||||
g_object_unref (fi_programdata);
|
||||
g_object_unref (gf_allusers);
|
||||
g_object_unref (gf_programdata);
|
||||
|
||||
g_free (allusers);
|
||||
g_free (users_dir);
|
||||
g_free (programdata);
|
||||
}
|
||||
|
||||
if (programdata_dir_w)
|
||||
CoTaskMemFree (programdata_dir_w);
|
||||
|
||||
if (users_dir_w)
|
||||
CoTaskMemFree (users_dir_w);
|
||||
|
||||
tmp_dir = g_dir_make_tmp ("glib_stdio_testXXXXXX", NULL);
|
||||
g_assert_nonnull (tmp_dir);
|
||||
|
||||
/* Technically, this isn't necessary - we already assume NTFS, because of
|
||||
* symlink support, and NTFS also supports sparse files. Still, given
|
||||
* the amount of unusual I/O APIs called in this test, checking for
|
||||
* sparse file support of the filesystem where temp directory is
|
||||
* doesn't seem to be out of place.
|
||||
*/
|
||||
try_sparse = FALSE;
|
||||
tmp_dir_root = g_strdup (tmp_dir);
|
||||
/* We need "C:\\" or "C:/", with a trailing [back]slash */
|
||||
for (c = tmp_dir_root; c && c[0] && c[1]; c++)
|
||||
if (c[0] == ':')
|
||||
{
|
||||
c[2] = '\0';
|
||||
break;
|
||||
}
|
||||
tmp_dir_root_w = g_utf8_to_utf16 (tmp_dir_root, -1, NULL, NULL, NULL);
|
||||
g_assert_nonnull (tmp_dir_root_w);
|
||||
g_free (tmp_dir_root);
|
||||
g_assert_true (GetVolumeInformationW (tmp_dir_root_w, NULL, 0, NULL, NULL, &fsflags, NULL, 0));
|
||||
g_free (tmp_dir_root_w);
|
||||
try_sparse = (fsflags & FILE_SUPPORTS_SPARSE_FILES) == FILE_SUPPORTS_SPARSE_FILES;
|
||||
|
||||
p0 = g_build_filename (tmp_dir, "zool", NULL);
|
||||
p1 = g_build_filename (tmp_dir, "looz", NULL);
|
||||
ps = g_build_filename (tmp_dir, "sparse", NULL);
|
||||
|
||||
if (try_sparse)
|
||||
{
|
||||
FILE_SET_SPARSE_BUFFER ssb;
|
||||
FILE_ZERO_DATA_INFORMATION zdi;
|
||||
|
||||
g_remove (ps);
|
||||
|
||||
f = g_fopen (ps, "wb");
|
||||
g_assert_nonnull (f);
|
||||
|
||||
h = (HANDLE) _get_osfhandle (fileno (f));
|
||||
g_assert (h != INVALID_HANDLE_VALUE);
|
||||
|
||||
ssb.SetSparse = TRUE;
|
||||
g_assert_true (DeviceIoControl (h,
|
||||
FSCTL_SET_SPARSE,
|
||||
&ssb, sizeof (ssb),
|
||||
NULL, 0, NULL, NULL));
|
||||
|
||||
/* Make it a sparse file that starts with 4GBs of zeros */
|
||||
zdi.FileOffset.QuadPart = 0;
|
||||
zdi.BeyondFinalZero.QuadPart = 0xFFFFFFFFULL + 1;
|
||||
g_assert_true (DeviceIoControl (h,
|
||||
FSCTL_SET_ZERO_DATA,
|
||||
&zdi, sizeof (zdi),
|
||||
NULL, 0, NULL, NULL));
|
||||
|
||||
/* Let's not keep this seemingly 4GB monster around
|
||||
* longer than we absolutely need to. Do all operations
|
||||
* without assertions, then remove the file immediately.
|
||||
*/
|
||||
_fseeki64 (f, 0xFFFFFFFFULL, SEEK_SET);
|
||||
fprintf (f, "Hello 4GB World!");
|
||||
fflush (f);
|
||||
fclose (f);
|
||||
|
||||
memset (&statbuf_ps, 0, sizeof (statbuf_ps));
|
||||
|
||||
g_stat (ps, &statbuf_ps);
|
||||
|
||||
gf_ps = g_file_new_for_path (ps);
|
||||
|
||||
fi_ps = g_file_query_info (gf_ps,
|
||||
G_FILE_ATTRIBUTE_STANDARD_SIZE ","
|
||||
G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE,
|
||||
G_FILE_QUERY_INFO_NONE,
|
||||
NULL, NULL);
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
|
||||
/* allocated size should small (usually - size of the FS cluster,
|
||||
* let's assume it's less than 1 gigabyte),
|
||||
* size should be more than 4 gigabytes,
|
||||
* st_size should not exceed its 0xFFFFFFFF 32-bit limit,
|
||||
* and should be nonzero (this also detects a failed g_stat() earlier).
|
||||
*/
|
||||
g_assert_cmpuint (alsize_ps, <, 0x40000000);
|
||||
g_assert_cmpuint (size_ps, >, G_GUINT64_CONSTANT (0xFFFFFFFF));
|
||||
g_assert_cmpuint (statbuf_ps.st_size, >, 0);
|
||||
g_assert_cmpuint (statbuf_ps.st_size, <=, 0xFFFFFFFF);
|
||||
|
||||
g_object_unref (fi_ps);
|
||||
g_object_unref (gf_ps);
|
||||
}
|
||||
|
||||
/* Wa-a-ay past 02/07/2106 @ 6:28am (UTC),
|
||||
* which is the date corresponding to 0xFFFFFFFF + 1.
|
||||
* This is easier to check than Y2038 (0x80000000 + 1),
|
||||
* since there's no need to worry about signedness this way.
|
||||
*/
|
||||
st.wYear = 2106;
|
||||
st.wMonth = 2;
|
||||
st.wDay = 9;
|
||||
st.wHour = 0;
|
||||
st.wMinute = 0;
|
||||
st.wSecond = 0;
|
||||
st.wMilliseconds = 0;
|
||||
|
||||
g_assert_true (SystemTimeToFileTime (&st, &ft));
|
||||
|
||||
f = g_fopen (p0, "w");
|
||||
g_assert_nonnull (f);
|
||||
|
||||
h = (HANDLE) _get_osfhandle (fileno (f));
|
||||
g_assert (h != INVALID_HANDLE_VALUE);
|
||||
|
||||
fprintf (f, "1");
|
||||
fflush (f);
|
||||
|
||||
g_assert_true (SetFileTime (h, &ft, &ft, &ft));
|
||||
|
||||
fclose (f);
|
||||
|
||||
f = g_fopen (p1, "w");
|
||||
g_assert_nonnull (f);
|
||||
|
||||
fclose (f);
|
||||
|
||||
memset (&statbuf_p0, 0, sizeof (statbuf_p0));
|
||||
memset (&statbuf_p1, 0, sizeof (statbuf_p1));
|
||||
|
||||
g_assert_cmpint (g_stat (p0, &statbuf_p0), ==, 0);
|
||||
g_assert_cmpint (g_stat (p1, &statbuf_p1), ==, 0);
|
||||
|
||||
gf_p0 = g_file_new_for_path (p0);
|
||||
gf_p1 = g_file_new_for_path (p1);
|
||||
|
||||
fi_p0 = g_file_query_info (gf_p0,
|
||||
G_FILE_ATTRIBUTE_STANDARD_SIZE ","
|
||||
G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE ","
|
||||
G_FILE_ATTRIBUTE_ID_FILE ","
|
||||
G_FILE_ATTRIBUTE_TIME_MODIFIED,
|
||||
G_FILE_QUERY_INFO_NONE,
|
||||
NULL, NULL);
|
||||
|
||||
fi_p1 = g_file_query_info (gf_p1,
|
||||
G_FILE_ATTRIBUTE_STANDARD_SIZE ","
|
||||
G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE ","
|
||||
G_FILE_ATTRIBUTE_ID_FILE ","
|
||||
G_FILE_ATTRIBUTE_TIME_MODIFIED,
|
||||
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 (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));
|
||||
|
||||
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);
|
||||
|
||||
/* size should be 1, allocated size should be something else
|
||||
* (could be 0 or the size of the FS cluster, but never 1).
|
||||
*/
|
||||
g_assert_cmpuint (size_p0, ==, statbuf_p0.st_size);
|
||||
g_assert_cmpuint (size_p0, ==, 1);
|
||||
g_assert_cmpuint (alsize_p0, !=, size_p0);
|
||||
|
||||
id_p0 = g_file_info_get_attribute_string (fi_p0, G_FILE_ATTRIBUTE_ID_FILE);
|
||||
id_p1 = g_file_info_get_attribute_string (fi_p1, G_FILE_ATTRIBUTE_ID_FILE);
|
||||
|
||||
/* 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_cmpstr (id_p0, !=, id_p1);
|
||||
|
||||
time_p0 = g_file_info_get_attribute_uint64 (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED);
|
||||
|
||||
/* Check that GFileInfo doesn't suffer from Y2106 problem.
|
||||
* Don't check stat(), as its contents may vary depending on
|
||||
* the host platform architecture
|
||||
* (time fields are 32-bit on 32-bit Windows,
|
||||
* 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_object_unref (fi_p0);
|
||||
g_object_unref (fi_p1);
|
||||
g_object_unref (gf_p0);
|
||||
g_object_unref (gf_p1);
|
||||
g_remove (p0);
|
||||
g_remove (p1);
|
||||
g_free (p0);
|
||||
g_free (p1);
|
||||
g_rmdir (tmp_dir);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
@@ -139,6 +503,9 @@ main (int argc,
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/g-file-info/test_g_file_info", test_g_file_info);
|
||||
#ifdef G_OS_WIN32
|
||||
g_test_add_func ("/g-file-info/internal-enhanced-stdio", test_internal_enhanced_stdio);
|
||||
#endif
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
|
Reference in New Issue
Block a user