Imported Upstream version 2.67.1
[platform/upstream/glib.git] / gio / tests / g-file-info.c
1 /* GLib testing framework examples and tests
2  * Copyright (C) 2008 Red Hat, Inc.
3  * Authors: Tomas Bzatek <tbzatek@redhat.com>
4  *
5  * This work is provided "as is"; redistribution and modification
6  * in whole or in part, in any medium, physical or electronic is
7  * permitted without restriction.
8  *
9  * This work is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  *
13  * In no event shall the authors or contributors be liable for any
14  * direct, indirect, incidental, special, exemplary, or consequential
15  * damages (including, but not limited to, procurement of substitute
16  * goods or services; loss of use, data, or profits; or business
17  * interruption) however caused and on any theory of liability, whether
18  * in contract, strict liability, or tort (including negligence or
19  * otherwise) arising in any way out of the use of this software, even
20  * if advised of the possibility of such damage.
21  */
22
23 #include "config.h"
24
25 #include <glib/glib.h>
26 #include <gio/gio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #ifdef G_OS_WIN32
30 #include <stdio.h>
31 #include <glib/gstdio.h>
32 #include <windows.h>
33 #include <shlobj.h>
34 #include <io.h> /* for _get_osfhandle */
35 #endif
36
37 #define TEST_NAME                       "Prilis zlutoucky kun"
38 #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"
39 #define TEST_SIZE                       0xFFFFFFF0
40
41 static void
42 test_assigned_values (GFileInfo *info)
43 {
44   const char *name, *display_name, *mistake;
45   guint64 size;
46   GFileType type;
47   
48   /*  Test for attributes presence */
49   g_assert_true (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_NAME));
50   g_assert_true (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME));
51   g_assert_true (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE));
52   g_assert_false (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME));
53         
54   /*  Retrieve data back and compare */
55   
56   name = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_NAME);
57   display_name = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
58   mistake = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME);
59   size = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
60   type = g_file_info_get_file_type (info);
61   
62   g_assert_cmpstr (name, ==, TEST_NAME);
63   g_assert_cmpstr (display_name, ==, TEST_DISPLAY_NAME);
64   g_assert_null (mistake);
65   g_assert_cmpint (size, ==, TEST_SIZE);
66   g_assert_cmpstr (name, ==, g_file_info_get_name (info));
67   g_assert_cmpstr (display_name, ==, g_file_info_get_display_name (info));
68   g_assert_cmpint (size, ==, g_file_info_get_size (info)        );
69   g_assert_cmpint (type, ==, G_FILE_TYPE_DIRECTORY);
70 }
71
72
73
74 static void
75 test_g_file_info (void)
76 {
77   GFileInfo *info;
78   GFileInfo *info_dup;
79   GFileInfo *info_copy;
80   char **attr_list;
81   GFileAttributeMatcher *matcher;
82   
83   info = g_file_info_new ();
84   
85   /*  Test for empty instance */
86   attr_list = g_file_info_list_attributes (info, NULL);
87   g_assert_nonnull (attr_list);
88   g_assert_null (*attr_list);
89   g_strfreev (attr_list);
90
91   g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_NAME, TEST_NAME);
92   g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, TEST_DISPLAY_NAME);
93   g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE, TEST_SIZE);
94   g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
95         
96   /*  The attr list should not be empty now */
97   attr_list = g_file_info_list_attributes (info, NULL);
98   g_assert_nonnull (attr_list);
99   g_assert_nonnull (*attr_list);
100   g_strfreev (attr_list);
101
102   test_assigned_values (info);
103         
104   /*  Test dups */
105   info_dup = g_file_info_dup (info);
106   g_assert_nonnull (info_dup);
107   test_assigned_values (info_dup);
108   
109   info_copy = g_file_info_new ();
110   g_file_info_copy_into (info_dup, info_copy);
111   g_assert_nonnull (info_copy);
112   test_assigned_values (info_copy);
113
114   /*  Test remove attribute */
115   g_assert_false (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER));
116   g_file_info_set_attribute_int32 (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER, 10);
117   g_assert_true (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER));
118
119   g_assert_cmpint (g_file_info_get_attribute_type (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER), ==, G_FILE_ATTRIBUTE_TYPE_INT32);
120   g_assert_cmpint (g_file_info_get_attribute_status (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER), !=, G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING);
121
122   g_file_info_remove_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER);
123   g_assert_false (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER));
124   g_assert_cmpint (g_file_info_get_attribute_type (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER), ==, G_FILE_ATTRIBUTE_TYPE_INVALID);
125
126   matcher = g_file_attribute_matcher_new (G_FILE_ATTRIBUTE_STANDARD_NAME ","
127                                           G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
128
129   g_assert_true (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_NAME));
130   g_assert_false (g_file_attribute_matcher_matches_only (matcher, G_FILE_ATTRIBUTE_STANDARD_NAME));
131   g_assert_false (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_SIZE));
132
133   g_file_info_set_attribute_mask (info, matcher);
134   g_file_attribute_matcher_unref (matcher);
135
136   g_assert_false (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE));
137   g_assert_true (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_NAME));
138
139   g_object_unref (info);
140   g_object_unref (info_dup);
141   g_object_unref (info_copy);
142 }
143
144 static void
145 test_g_file_info_modification_time (void)
146 {
147   GFile *file = NULL;
148   GFileIOStream *io_stream = NULL;
149   GFileInfo *info = NULL;
150   GDateTime *dt = NULL, *dt_usecs = NULL, *dt_new = NULL, *dt_new_usecs = NULL;
151   GTimeSpan ts;
152   GError *error = NULL;
153
154   g_test_summary ("Test that getting the modification time of a file works.");
155
156   file = g_file_new_tmp ("g-file-info-test-XXXXXX", &io_stream, &error);
157   g_assert_no_error (error);
158
159   info = g_file_query_info (file,
160                             G_FILE_ATTRIBUTE_TIME_MODIFIED,
161                             G_FILE_QUERY_INFO_NONE,
162                             NULL, &error);
163   g_assert_no_error (error);
164
165   /* Check the modification time is retrievable. */
166   dt = g_file_info_get_modification_date_time (info);
167   g_assert_nonnull (dt);
168
169   /* Try again with microsecond precision. */
170   g_clear_object (&info);
171   info = g_file_query_info (file,
172                             G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
173                             G_FILE_QUERY_INFO_NONE,
174                             NULL, &error);
175   g_assert_no_error (error);
176
177   dt_usecs = g_file_info_get_modification_date_time (info);
178   g_assert_nonnull (dt_usecs);
179
180   ts = g_date_time_difference (dt_usecs, dt);
181   g_assert_cmpint (ts, >, 0);
182   g_assert_cmpint (ts, <, G_USEC_PER_SEC);
183
184   /* Try round-tripping the modification time. */
185   dt_new = g_date_time_add (dt_usecs, G_USEC_PER_SEC + 50);
186   g_file_info_set_modification_date_time (info, dt_new);
187
188   dt_new_usecs = g_file_info_get_modification_date_time (info);
189   ts = g_date_time_difference (dt_new_usecs, dt_new);
190   g_assert_cmpint (ts, ==, 0);
191
192   /* Clean up. */
193   g_clear_object (&io_stream);
194   g_file_delete (file, NULL, NULL);
195   g_clear_object (&file);
196
197   g_clear_object (&info);
198   g_date_time_unref (dt);
199   g_date_time_unref (dt_usecs);
200   g_date_time_unref (dt_new);
201   g_date_time_unref (dt_new_usecs);
202 }
203
204 #ifdef G_OS_WIN32
205 static void
206 test_internal_enhanced_stdio (void)
207 {
208   char *p0, *p1, *ps;
209   gboolean try_sparse;
210   gchar *tmp_dir_root;
211   wchar_t *tmp_dir_root_w;
212   gchar *c;
213   DWORD fsflags;
214   FILE *f;
215   SYSTEMTIME st;
216   FILETIME ft;
217   HANDLE h;
218   GStatBuf statbuf_p0, statbuf_p1, statbuf_ps;
219   GFile *gf_p0, *gf_p1, *gf_ps;
220   GFileInfo *fi_p0, *fi_p1, *fi_ps;
221   guint64 size_p0, alsize_p0, size_ps, alsize_ps;
222   const gchar *id_p0;
223   const gchar *id_p1;
224   guint64 time_p0;
225   gchar *tmp_dir;
226   wchar_t *programdata_dir_w;
227   wchar_t *users_dir_w;
228   static const GUID folder_id_programdata = 
229     { 0x62AB5D82, 0xFDC1, 0x4DC3, { 0xA9, 0xDD, 0x07, 0x0D, 0x1D, 0x49, 0x5D, 0x97 } };
230   static const GUID folder_id_users = 
231     { 0x0762D272, 0xC50A, 0x4BB0, { 0xA3, 0x82, 0x69, 0x7D, 0xCD, 0x72, 0x9B, 0x80 } };
232   GDateTime *dt = NULL, *dt2 = NULL;
233   GTimeSpan ts;
234   /* Just before SYSTEMTIME limit (Jan 1 30827) */
235   const gint64 one_sec_before_systemtime_limit = 910670515199;
236   gboolean retval;
237   GError *local_error = NULL;
238
239
240   programdata_dir_w = NULL;
241   SHGetKnownFolderPath (&folder_id_programdata, 0, NULL, &programdata_dir_w);
242
243   users_dir_w = NULL;
244   SHGetKnownFolderPath (&folder_id_users, 0, NULL, &users_dir_w);
245
246   if (programdata_dir_w != NULL && users_dir_w != NULL)
247     {
248       gchar *programdata;
249       gchar *users_dir;
250       gchar *allusers;
251       gchar *commondata;
252       GFile *gf_programdata, *gf_allusers, *gf_commondata;
253       GFileInfo *fi_programdata, *fi_allusers, *fi_allusers_target, *fi_commondata, *fi_commondata_target;
254       GFileType ft_allusers;
255       GFileType ft_allusers_target;
256       GFileType ft_programdata;
257       GFileType ft_commondata;
258       gboolean allusers_is_symlink;
259       gboolean commondata_is_symlink;
260       gboolean commondata_is_mount_point;
261       guint32 allusers_reparse_tag;
262       guint32 commondata_reparse_tag;
263       const gchar *id_allusers;
264       const gchar *id_allusers_target;
265       const gchar *id_commondata_target;
266       const gchar *id_programdata;
267       const gchar *allusers_target;
268       const gchar *commondata_target;
269
270       /* C:/ProgramData */
271       programdata = g_utf16_to_utf8 (programdata_dir_w, -1, NULL, NULL, NULL);
272       g_assert_nonnull (programdata);
273       /* C:/Users */
274       users_dir = g_utf16_to_utf8 (users_dir_w, -1, NULL, NULL, NULL);
275       g_assert_nonnull (users_dir);
276       /* "C:/Users/All Users" is a known directory symlink
277        * for "C:/ProgramData".
278        */
279       allusers = g_build_filename (users_dir, "All Users", NULL);
280
281       /* "C:/Users/All Users/Application Data" is a known
282        * junction for "C:/ProgramData"
283        */
284       commondata = g_build_filename (allusers, "Application Data", NULL);
285
286       /* We don't test g_stat() and g_lstat() on these directories,
287        * because it is pointless - there's no way to tell that these
288        * functions behave correctly in this case
289        * (st_ino is useless, so we can't tell apart g_stat() and g_lstat()
290        *  results; st_mode is also useless as it does not support S_ISLNK;
291        *  and these directories have no interesting properties other
292        *  than [not] being symlinks).
293        */
294       gf_programdata = g_file_new_for_path (programdata);
295       gf_allusers = g_file_new_for_path (allusers);
296       gf_commondata = g_file_new_for_path (commondata);
297
298       fi_programdata = g_file_query_info (gf_programdata,
299                                           G_FILE_ATTRIBUTE_ID_FILE ","
300                                           G_FILE_ATTRIBUTE_STANDARD_TYPE,
301                                           G_FILE_QUERY_INFO_NONE,
302                                           NULL, NULL);
303
304       fi_allusers_target = g_file_query_info (gf_allusers,
305                                               G_FILE_ATTRIBUTE_ID_FILE ","
306                                               G_FILE_ATTRIBUTE_STANDARD_TYPE,
307                                               G_FILE_QUERY_INFO_NONE,
308                                               NULL, NULL);
309
310       fi_allusers = g_file_query_info (gf_allusers,
311                                        G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
312                                        G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
313                                        G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG ","
314                                        G_FILE_ATTRIBUTE_ID_FILE ","
315                                        G_FILE_ATTRIBUTE_STANDARD_TYPE,
316                                        G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
317                                        NULL, NULL);
318
319       fi_commondata = g_file_query_info (gf_commondata,
320                                          G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
321                                          G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
322                                          G_FILE_ATTRIBUTE_DOS_IS_MOUNTPOINT ","
323                                          G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG ","
324                                          G_FILE_ATTRIBUTE_ID_FILE ","
325                                          G_FILE_ATTRIBUTE_STANDARD_TYPE,
326                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
327                                          NULL, NULL);
328
329       fi_commondata_target = g_file_query_info (gf_commondata,
330                                                 G_FILE_ATTRIBUTE_ID_FILE ","
331                                                 G_FILE_ATTRIBUTE_STANDARD_TYPE,
332                                                 G_FILE_QUERY_INFO_NONE,
333                                                 NULL, NULL);
334
335       g_assert_true (g_file_info_has_attribute (fi_programdata, G_FILE_ATTRIBUTE_ID_FILE));
336       g_assert_true (g_file_info_has_attribute (fi_programdata, G_FILE_ATTRIBUTE_STANDARD_TYPE));
337
338       g_assert_true (g_file_info_has_attribute (fi_allusers_target, G_FILE_ATTRIBUTE_ID_FILE));
339       g_assert_true (g_file_info_has_attribute (fi_allusers_target, G_FILE_ATTRIBUTE_STANDARD_TYPE));
340       g_assert_true (g_file_info_has_attribute (fi_commondata_target, G_FILE_ATTRIBUTE_ID_FILE));
341       g_assert_true (g_file_info_has_attribute (fi_commondata_target, G_FILE_ATTRIBUTE_STANDARD_TYPE));
342
343       g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_ID_FILE));
344       g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_TYPE));
345       g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK));
346       g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG));
347       g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET));
348
349       g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_ID_FILE));
350       g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_STANDARD_TYPE));
351       g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK));
352       g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_DOS_IS_MOUNTPOINT));
353       g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG));
354       g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET));
355
356       ft_allusers = g_file_info_get_file_type (fi_allusers);
357       ft_allusers_target = g_file_info_get_file_type (fi_allusers_target);
358       ft_programdata = g_file_info_get_file_type (fi_programdata);
359       ft_commondata = g_file_info_get_file_type (fi_commondata);
360
361       g_assert_cmpint (ft_allusers, ==, G_FILE_TYPE_DIRECTORY);
362       g_assert_cmpint (ft_allusers_target, ==, G_FILE_TYPE_DIRECTORY);
363       g_assert_cmpint (ft_programdata, ==, G_FILE_TYPE_DIRECTORY);
364       g_assert_cmpint (ft_commondata, ==, G_FILE_TYPE_DIRECTORY);
365
366       allusers_is_symlink = g_file_info_get_attribute_boolean (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK);
367       allusers_reparse_tag = g_file_info_get_attribute_uint32 (fi_allusers, G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG);
368       commondata_is_symlink = g_file_info_get_attribute_boolean (fi_commondata, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK);
369       commondata_is_mount_point = g_file_info_get_attribute_boolean (fi_commondata, G_FILE_ATTRIBUTE_DOS_IS_MOUNTPOINT);
370       commondata_reparse_tag = g_file_info_get_attribute_uint32 (fi_commondata, G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG);
371
372       g_assert_true (allusers_is_symlink);
373       g_assert_cmpuint (allusers_reparse_tag, ==, IO_REPARSE_TAG_SYMLINK);
374       g_assert_true (commondata_is_symlink);
375       g_assert_true (commondata_is_mount_point);
376       g_assert_cmpuint (commondata_reparse_tag, ==, IO_REPARSE_TAG_MOUNT_POINT);
377
378       id_allusers = g_file_info_get_attribute_string (fi_allusers, G_FILE_ATTRIBUTE_ID_FILE);
379       id_allusers_target = g_file_info_get_attribute_string (fi_allusers_target, G_FILE_ATTRIBUTE_ID_FILE);
380       id_commondata_target = g_file_info_get_attribute_string (fi_commondata_target, G_FILE_ATTRIBUTE_ID_FILE);
381       id_programdata = g_file_info_get_attribute_string (fi_programdata, G_FILE_ATTRIBUTE_ID_FILE);
382
383       g_assert_cmpstr (id_allusers_target, ==, id_programdata);
384       g_assert_cmpstr (id_commondata_target, ==, id_programdata);
385       g_assert_cmpstr (id_allusers, !=, id_programdata);
386
387       allusers_target = g_file_info_get_symlink_target (fi_allusers);
388
389       g_assert_true (g_str_has_suffix (allusers_target, "ProgramData"));
390
391       commondata_target = g_file_info_get_symlink_target (fi_commondata);
392
393       g_assert_true (g_str_has_suffix (commondata_target, "ProgramData"));
394
395       g_object_unref (fi_allusers);
396       g_object_unref (fi_allusers_target);
397       g_object_unref (fi_commondata);
398       g_object_unref (fi_commondata_target);
399       g_object_unref (fi_programdata);
400       g_object_unref (gf_allusers);
401       g_object_unref (gf_commondata);
402       g_object_unref (gf_programdata);
403
404       g_free (allusers);
405       g_free (commondata);
406       g_free (users_dir);
407       g_free (programdata);
408     }
409
410   if (programdata_dir_w)
411     CoTaskMemFree (programdata_dir_w);
412
413   if (users_dir_w)
414     CoTaskMemFree (users_dir_w);
415
416   tmp_dir = g_dir_make_tmp ("glib_stdio_testXXXXXX", NULL);
417   g_assert_nonnull (tmp_dir);
418
419   /* Technically, this isn't necessary - we already assume NTFS, because of
420    * symlink support, and NTFS also supports sparse files. Still, given
421    * the amount of unusual I/O APIs called in this test, checking for
422    * sparse file support of the filesystem where temp directory is
423    * doesn't seem to be out of place.
424    */
425   try_sparse = FALSE;
426   tmp_dir_root = g_strdup (tmp_dir);
427   /* We need "C:\\" or "C:/", with a trailing [back]slash */
428   for (c = tmp_dir_root; c && c[0] && c[1]; c++)
429     if (c[0] == ':')
430       {
431         c[2] = '\0';
432         break;
433       }
434   tmp_dir_root_w = g_utf8_to_utf16 (tmp_dir_root, -1, NULL, NULL, NULL);
435   g_assert_nonnull (tmp_dir_root_w);
436   g_free (tmp_dir_root);
437   g_assert_true (GetVolumeInformationW (tmp_dir_root_w, NULL, 0, NULL, NULL, &fsflags, NULL, 0));
438   g_free (tmp_dir_root_w);
439   try_sparse = (fsflags & FILE_SUPPORTS_SPARSE_FILES) == FILE_SUPPORTS_SPARSE_FILES;
440
441   p0 = g_build_filename (tmp_dir, "zool", NULL);
442   p1 = g_build_filename (tmp_dir, "looz", NULL);
443   ps = g_build_filename (tmp_dir, "sparse", NULL);
444
445   if (try_sparse)
446     {
447       FILE_SET_SPARSE_BUFFER ssb;
448       FILE_ZERO_DATA_INFORMATION zdi;
449
450       g_remove (ps);
451
452       f = g_fopen (ps, "wb");
453       g_assert_nonnull (f);
454
455       h = (HANDLE) _get_osfhandle (fileno (f));
456       g_assert_cmpuint ((guintptr) h, !=, (guintptr) INVALID_HANDLE_VALUE);
457
458       ssb.SetSparse = TRUE;
459       g_assert_true (DeviceIoControl (h,
460                      FSCTL_SET_SPARSE,
461                      &ssb, sizeof (ssb),
462                      NULL, 0, NULL, NULL));
463
464       /* Make it a sparse file that starts with 4GBs of zeros */
465       zdi.FileOffset.QuadPart = 0;
466       zdi.BeyondFinalZero.QuadPart = 0xFFFFFFFFULL + 1;
467       g_assert_true (DeviceIoControl (h,
468                      FSCTL_SET_ZERO_DATA,
469                      &zdi, sizeof (zdi),
470                      NULL, 0, NULL, NULL));
471
472       /* Let's not keep this seemingly 4GB monster around
473        * longer than we absolutely need to. Do all operations
474        * without assertions, then remove the file immediately.
475        */
476       _fseeki64 (f, 0xFFFFFFFFULL, SEEK_SET);
477       fprintf (f, "Hello 4GB World!");
478       fflush (f);
479       fclose (f);
480
481       memset (&statbuf_ps, 0, sizeof (statbuf_ps));
482
483       g_stat (ps, &statbuf_ps);
484
485       gf_ps = g_file_new_for_path (ps);
486
487       fi_ps = g_file_query_info (gf_ps,
488                                  G_FILE_ATTRIBUTE_STANDARD_SIZE ","
489                                  G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE,
490                                  G_FILE_QUERY_INFO_NONE,
491                                  NULL, NULL);
492
493       g_remove (ps);
494
495       g_assert_true (g_file_info_has_attribute (fi_ps, G_FILE_ATTRIBUTE_STANDARD_SIZE));
496       g_assert_true (g_file_info_has_attribute (fi_ps, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE));
497
498       size_ps = g_file_info_get_attribute_uint64 (fi_ps, G_FILE_ATTRIBUTE_STANDARD_SIZE);
499       alsize_ps = g_file_info_get_attribute_uint64 (fi_ps, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE);
500
501       /* allocated size should small (usually - size of the FS cluster,
502        * let's assume it's less than 1 gigabyte),
503        * size should be more than 4 gigabytes,
504        * st_size should not exceed its 0xFFFFFFFF 32-bit limit,
505        * and should be nonzero (this also detects a failed g_stat() earlier).
506        */
507       g_assert_cmpuint (alsize_ps, <, 0x40000000);
508       g_assert_cmpuint (size_ps, >, G_GUINT64_CONSTANT (0xFFFFFFFF));
509       g_assert_cmpuint (statbuf_ps.st_size, >, 0);
510       g_assert_cmpuint (statbuf_ps.st_size, <=, 0xFFFFFFFF);
511
512       g_object_unref (fi_ps);
513       g_object_unref (gf_ps);
514     }
515
516   /* Wa-a-ay past 02/07/2106 @ 6:28am (UTC),
517    * which is the date corresponding to 0xFFFFFFFF + 1.
518    * This is easier to check than Y2038 (0x80000000 + 1),
519    * since there's no need to worry about signedness this way.
520    */
521   st.wYear = 2106;
522   st.wMonth = 2;
523   st.wDay = 9;
524   st.wHour = 0;
525   st.wMinute = 0;
526   st.wSecond = 0;
527   st.wMilliseconds = 0;
528
529   g_assert_true (SystemTimeToFileTime (&st, &ft));
530
531   f = g_fopen (p0, "w");
532   g_assert_nonnull (f);
533
534   h = (HANDLE) _get_osfhandle (fileno (f));
535   g_assert_cmpuint ((guintptr) h, !=, (guintptr) INVALID_HANDLE_VALUE);
536
537   fprintf (f, "1");
538   fflush (f);
539
540   g_assert_true (SetFileTime (h, &ft, &ft, &ft));
541
542   fclose (f);
543
544   f = g_fopen (p1, "w");
545   g_assert_nonnull (f);
546
547   fclose (f);
548
549   memset (&statbuf_p0, 0, sizeof (statbuf_p0));
550   memset (&statbuf_p1, 0, sizeof (statbuf_p1));
551
552   g_assert_cmpint (g_stat (p0, &statbuf_p0), ==, 0);
553   g_assert_cmpint (g_stat (p1, &statbuf_p1), ==, 0);
554
555   gf_p0 = g_file_new_for_path (p0);
556   gf_p1 = g_file_new_for_path (p1);
557
558   fi_p0 = g_file_query_info (gf_p0,
559                              G_FILE_ATTRIBUTE_STANDARD_SIZE ","
560                              G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE ","
561                              G_FILE_ATTRIBUTE_ID_FILE ","
562                              G_FILE_ATTRIBUTE_TIME_MODIFIED ","
563                              G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
564                              G_FILE_QUERY_INFO_NONE,
565                              NULL, NULL);
566
567   fi_p1 = g_file_query_info (gf_p1,
568                              G_FILE_ATTRIBUTE_STANDARD_SIZE ","
569                              G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE ","
570                              G_FILE_ATTRIBUTE_ID_FILE ","
571                              G_FILE_ATTRIBUTE_TIME_MODIFIED ","
572                              G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
573                              G_FILE_QUERY_INFO_NONE,
574                              NULL, NULL);
575
576   g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_STANDARD_SIZE));
577   g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE));
578   g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_ID_FILE));
579   g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED));
580   g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC));
581
582   g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_STANDARD_SIZE));
583   g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE));
584   g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_ID_FILE));
585   g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_TIME_MODIFIED));
586   g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC));
587
588   size_p0 = g_file_info_get_attribute_uint64 (fi_p0, G_FILE_ATTRIBUTE_STANDARD_SIZE);
589   alsize_p0 = g_file_info_get_attribute_uint64 (fi_p0, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE);
590
591   /* size should be 1, allocated size should be something else
592    * (could be 0 or the size of the FS cluster, but never 1).
593    */
594   g_assert_cmpuint (size_p0, ==, statbuf_p0.st_size);
595   g_assert_cmpuint (size_p0, ==, 1);
596   g_assert_cmpuint (alsize_p0, !=, size_p0);
597
598   id_p0 = g_file_info_get_attribute_string (fi_p0, G_FILE_ATTRIBUTE_ID_FILE);
599   id_p1 = g_file_info_get_attribute_string (fi_p1, G_FILE_ATTRIBUTE_ID_FILE);
600
601   /* st_ino from W32 stat() is useless for file identification.
602    * It will be either 0, or it will be the same for both files.
603    */
604   g_assert_cmpint (statbuf_p0.st_ino, ==, statbuf_p1.st_ino);
605   g_assert_cmpstr (id_p0, !=, id_p1);
606
607   time_p0 = g_file_info_get_attribute_uint64 (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED);
608
609   /* Check that GFileInfo doesn't suffer from Y2106 problem.
610    * Don't check stat(), as its contents may vary depending on
611    * the host platform architecture
612    * (time fields are 32-bit on 32-bit Windows,
613    *  and 64-bit on 64-bit Windows, usually),
614    * so it *can* pass this test in some cases.
615    */
616   g_assert_cmpuint (time_p0, >, G_GUINT64_CONSTANT (0xFFFFFFFF));
617
618   dt = g_file_info_get_modification_date_time (fi_p0);
619   g_assert_nonnull (dt);
620   dt2 = g_date_time_add (dt, G_USEC_PER_SEC / 100 * 200);
621   g_object_unref (fi_p0);
622   fi_p0 = g_file_info_new ();
623   g_file_info_set_modification_date_time (fi_p0, dt2);
624
625   g_assert_true (g_file_set_attributes_from_info (gf_p0,
626                                                   fi_p0,
627                                                   G_FILE_QUERY_INFO_NONE,
628                                                   NULL,
629                                                   NULL));
630   g_date_time_unref (dt2);
631   g_object_unref (fi_p0);
632   fi_p0 = g_file_query_info (gf_p0,
633                              G_FILE_ATTRIBUTE_TIME_MODIFIED ","
634                              G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
635                              G_FILE_QUERY_INFO_NONE,
636                              NULL, NULL);
637   dt2 = g_file_info_get_modification_date_time (fi_p0);
638   ts = g_date_time_difference (dt2, dt);
639   g_assert_cmpint (ts, >, 0);
640   g_assert_cmpint (ts, <, G_USEC_PER_SEC / 100 * 300);
641
642   g_date_time_unref (dt);
643   g_date_time_unref (dt2);
644
645   g_file_info_set_attribute_uint64 (fi_p0,
646                                     G_FILE_ATTRIBUTE_TIME_MODIFIED,
647                                     one_sec_before_systemtime_limit);
648   g_file_info_set_attribute_uint32 (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, 0);
649   g_assert_true (g_file_set_attributes_from_info (gf_p0,
650                                                   fi_p0,
651                                                   G_FILE_QUERY_INFO_NONE,
652                                                   NULL,
653                                                   NULL));
654
655   g_file_info_set_attribute_uint64 (fi_p0,
656                                    G_FILE_ATTRIBUTE_TIME_MODIFIED,
657                                    one_sec_before_systemtime_limit + G_USEC_PER_SEC * 2);
658   g_file_info_set_attribute_uint32 (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, 0);
659   retval = g_file_set_attributes_from_info (gf_p0,
660                                             fi_p0,
661                                             G_FILE_QUERY_INFO_NONE,
662                                             NULL,
663                                             &local_error);
664   g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA);
665   g_assert_false (retval);
666   g_clear_error (&local_error);
667
668   g_object_unref (fi_p0);
669   g_object_unref (fi_p1);
670   g_object_unref (gf_p0);
671   g_object_unref (gf_p1);
672   g_remove (p0);
673   g_remove (p1);
674   g_free (p0);
675   g_free (p1);
676   g_rmdir (tmp_dir);
677 }
678 #endif
679
680 static void
681 test_xattrs (void)
682 {
683   GFile *file = NULL;
684   GFileIOStream *stream = NULL;
685   GFileInfo *file_info0 = NULL, *file_info1 = NULL;
686   GError *local_error = NULL;
687
688   g_test_summary ("Test setting and getting escaped xattrs");
689
690   /* Create a temporary file; no need to write anything to it. */
691   file = g_file_new_tmp ("g-file-info-test-xattrs-XXXXXX", &stream, &local_error);
692   g_assert_no_error (local_error);
693   g_assert_nonnull (file);
694
695   g_io_stream_close (G_IO_STREAM (stream), NULL, NULL);
696   g_object_unref (stream);
697
698   /* Check the existing xattrs. */
699   file_info0 = g_file_query_info (file, "xattr::*", G_FILE_QUERY_INFO_NONE, NULL, &local_error);
700   g_assert_no_error (local_error);
701   g_assert_nonnull (file_info0);
702
703   /* Set some new xattrs, with escaping and some embedded nuls. */
704   g_file_info_set_attribute_string (file_info0, "xattr::escaped", "hello\\x82\\x80\\xbd");
705   g_file_info_set_attribute_string (file_info0, "xattr::string", "hi there");
706   g_file_info_set_attribute_string (file_info0, "xattr::embedded-nul", "hi\\x00there");
707
708   g_file_set_attributes_from_info (file, file_info0, G_FILE_QUERY_INFO_NONE, NULL, &local_error);
709
710   g_object_unref (file_info0);
711
712   if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
713     {
714       g_test_skip ("xattrs not supported on this file system");
715       g_clear_error (&local_error);
716     }
717   else
718     {
719       g_assert_no_error (local_error);
720
721       /* Check they were set properly. */
722       file_info1 = g_file_query_info (file, "xattr::*", G_FILE_QUERY_INFO_NONE, NULL, &local_error);
723       g_assert_no_error (local_error);
724       g_assert_nonnull (file_info1);
725
726       g_assert_true (g_file_info_has_namespace (file_info1, "xattr"));
727
728       g_assert_cmpstr (g_file_info_get_attribute_string (file_info1, "xattr::escaped"), ==, "hello\\x82\\x80\\xbd");
729       g_assert_cmpstr (g_file_info_get_attribute_string (file_info1, "xattr::string"), ==, "hi there");
730       g_assert_cmpstr (g_file_info_get_attribute_string (file_info1, "xattr::embedded-nul"), ==, "hi\\x00there");
731
732       g_object_unref (file_info1);
733     }
734
735   /* Tidy up. */
736   g_file_delete (file, NULL, NULL);
737
738   g_object_unref (file);
739 }
740
741 int
742 main (int   argc,
743       char *argv[])
744 {
745   g_test_init (&argc, &argv, NULL);
746
747   g_test_add_func ("/g-file-info/test_g_file_info", test_g_file_info);
748   g_test_add_func ("/g-file-info/test_g_file_info/modification-time", test_g_file_info_modification_time);
749 #ifdef G_OS_WIN32
750   g_test_add_func ("/g-file-info/internal-enhanced-stdio", test_internal_enhanced_stdio);
751 #endif
752   g_test_add_func ("/g-file-info/xattrs", test_xattrs);
753   
754   return g_test_run();
755 }