diff options
Diffstat (limited to 'gio/tests/g-file-info.c')
-rw-r--r-- | gio/tests/g-file-info.c | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/gio/tests/g-file-info.c b/gio/tests/g-file-info.c index 7ed874a03..5b3def91c 100644 --- a/gio/tests/g-file-info.c +++ b/gio/tests/g-file-info.c @@ -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(); } |