Change LGPL-2.1+ to LGPL-2.1-or-later
[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  * SPDX-License-Identifier: LicenseRef-old-glib-tests
6  *
7  * This work is provided "as is"; redistribution and modification
8  * in whole or in part, in any medium, physical or electronic is
9  * permitted without restriction.
10  *
11  * This work is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  *
15  * In no event shall the authors or contributors be liable for any
16  * direct, indirect, incidental, special, exemplary, or consequential
17  * damages (including, but not limited to, procurement of substitute
18  * goods or services; loss of use, data, or profits; or business
19  * interruption) however caused and on any theory of liability, whether
20  * in contract, strict liability, or tort (including negligence or
21  * otherwise) arising in any way out of the use of this software, even
22  * if advised of the possibility of such damage.
23  */
24
25 #include "config.h"
26
27 #include <glib/glib.h>
28 #include <gio/gio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #ifdef G_OS_WIN32
32 #include <stdio.h>
33 #include <glib/gstdio.h>
34 #include <windows.h>
35 #include <shlobj.h>
36 #include <io.h> /* for _get_osfhandle */
37 #endif
38
39 #define TEST_NAME                       "Prilis zlutoucky kun"
40 #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"
41 #define TEST_SIZE                       0xFFFFFFF0
42
43 static void
44 test_assigned_values (GFileInfo *info)
45 {
46   const char *name, *name_filepath, *display_name, *mistake;
47   guint64 size;
48   GFileType type;
49   
50   /*  Test for attributes presence */
51   g_assert_true (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_NAME));
52   g_assert_true (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME));
53   g_assert_true (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE));
54   g_assert_false (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME));
55         
56   /*  Retrieve data back and compare */
57   
58   name = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_NAME);
59   name_filepath = g_file_info_get_attribute_file_path (info, G_FILE_ATTRIBUTE_STANDARD_NAME);
60   display_name = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
61   mistake = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME);
62   size = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
63   type = g_file_info_get_file_type (info);
64   
65   g_assert_cmpstr (name, ==, TEST_NAME);
66   g_assert_cmpstr (name_filepath, ==, name);
67   g_assert_cmpstr (display_name, ==, TEST_DISPLAY_NAME);
68   g_assert_null (mistake);
69   g_assert_cmpint (size, ==, TEST_SIZE);
70   g_assert_cmpstr (name, ==, g_file_info_get_name (info));
71   g_assert_cmpstr (display_name, ==, g_file_info_get_display_name (info));
72   g_assert_cmpint (size, ==, g_file_info_get_size (info)        );
73   g_assert_cmpint (type, ==, G_FILE_TYPE_DIRECTORY);
74 }
75
76
77
78 static void
79 test_g_file_info (void)
80 {
81   GFileInfo *info;
82   GFileInfo *info_dup;
83   GFileInfo *info_copy;
84   char **attr_list;
85   GFileAttributeMatcher *matcher;
86   
87   info = g_file_info_new ();
88   
89   /*  Test for empty instance */
90   attr_list = g_file_info_list_attributes (info, NULL);
91   g_assert_nonnull (attr_list);
92   g_assert_null (*attr_list);
93   g_strfreev (attr_list);
94
95   g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_NAME, TEST_NAME);
96   g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, TEST_DISPLAY_NAME);
97   g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE, TEST_SIZE);
98   g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
99         
100   /*  The attr list should not be empty now */
101   attr_list = g_file_info_list_attributes (info, NULL);
102   g_assert_nonnull (attr_list);
103   g_assert_nonnull (*attr_list);
104   g_strfreev (attr_list);
105
106   test_assigned_values (info);
107
108   /* Test the file path encoding functions */
109   g_file_info_set_attribute_file_path (info, G_FILE_ATTRIBUTE_STANDARD_NAME, "something different");
110   g_assert_cmpstr (g_file_info_get_attribute_file_path (info, G_FILE_ATTRIBUTE_STANDARD_NAME), ==, "something different");
111   g_file_info_set_attribute_file_path (info, G_FILE_ATTRIBUTE_STANDARD_NAME, TEST_NAME);
112
113   /*  Test dups */
114   info_dup = g_file_info_dup (info);
115   g_assert_nonnull (info_dup);
116   test_assigned_values (info_dup);
117   
118   info_copy = g_file_info_new ();
119   g_file_info_copy_into (info_dup, info_copy);
120   g_assert_nonnull (info_copy);
121   test_assigned_values (info_copy);
122
123   /*  Test remove attribute */
124   g_assert_false (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER));
125   g_file_info_set_attribute_int32 (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER, 10);
126   g_assert_true (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER));
127
128   g_assert_cmpint (g_file_info_get_attribute_type (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER), ==, G_FILE_ATTRIBUTE_TYPE_INT32);
129   g_assert_cmpint (g_file_info_get_attribute_status (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER), !=, G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING);
130
131   g_file_info_remove_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER);
132   g_assert_false (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER));
133   g_assert_cmpint (g_file_info_get_attribute_type (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER), ==, G_FILE_ATTRIBUTE_TYPE_INVALID);
134
135   matcher = g_file_attribute_matcher_new (G_FILE_ATTRIBUTE_STANDARD_NAME ","
136                                           G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
137
138   g_assert_true (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_NAME));
139   g_assert_false (g_file_attribute_matcher_matches_only (matcher, G_FILE_ATTRIBUTE_STANDARD_NAME));
140   g_assert_false (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_SIZE));
141
142   g_file_info_set_attribute_mask (info, matcher);
143   g_file_attribute_matcher_unref (matcher);
144
145   g_assert_false (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE));
146   g_assert_true (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_NAME));
147
148   g_object_unref (info);
149   g_object_unref (info_dup);
150   g_object_unref (info_copy);
151 }
152
153 static void
154 test_g_file_info_modification_time (void)
155 {
156   GFile *file = NULL;
157   GFileIOStream *io_stream = NULL;
158   GFileInfo *info = NULL;
159   GDateTime *dt = NULL, *dt_usecs = NULL, *dt_new = NULL, *dt_new_usecs = NULL;
160   GTimeSpan ts;
161   gboolean nsecs_supported;
162   gint usecs;
163   guint32 nsecs;
164   GError *error = NULL;
165
166   g_test_summary ("Test that getting the modification time of a file works.");
167
168   file = g_file_new_tmp ("g-file-info-test-XXXXXX", &io_stream, &error);
169   g_assert_no_error (error);
170
171   info = g_file_query_info (file,
172                             G_FILE_ATTRIBUTE_TIME_MODIFIED,
173                             G_FILE_QUERY_INFO_NONE,
174                             NULL, &error);
175   g_assert_no_error (error);
176
177   /* Check the modification time is retrievable. */
178   dt = g_file_info_get_modification_date_time (info);
179   g_assert_nonnull (dt);
180
181   /* Try again with microsecond precision. */
182   g_clear_object (&info);
183   info = g_file_query_info (file,
184                             G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
185                             G_FILE_QUERY_INFO_NONE,
186                             NULL, &error);
187   g_assert_no_error (error);
188
189   dt_usecs = g_file_info_get_modification_date_time (info);
190   g_assert_nonnull (dt_usecs);
191
192   ts = g_date_time_difference (dt_usecs, dt);
193   g_assert_cmpint (ts, >=, 0);
194   g_assert_cmpint (ts, <, G_USEC_PER_SEC);
195
196   /* Try again with nanosecond precision. */
197   g_clear_object (&info);
198   info = g_file_query_info (file,
199                             G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC "," G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC,
200                             G_FILE_QUERY_INFO_NONE,
201                             NULL, &error);
202   g_assert_no_error (error);
203
204   nsecs_supported = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC);
205   if (nsecs_supported)
206     {
207       usecs = g_date_time_get_microsecond (dt_usecs);
208       nsecs = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC);
209
210       g_assert_cmpuint (nsecs, >=, usecs * 1000);
211       g_assert_cmpuint (nsecs, <, (usecs + 1) * 1000);
212     }
213
214   /* Try round-tripping the modification time. */
215   dt_new = g_date_time_add (dt_usecs, G_USEC_PER_SEC + 50);
216   g_file_info_set_modification_date_time (info, dt_new);
217
218   dt_new_usecs = g_file_info_get_modification_date_time (info);
219   ts = g_date_time_difference (dt_new_usecs, dt_new);
220   g_assert_cmpint (ts, ==, 0);
221
222   /* Setting the modification time with usec-precision should have cleared nsecs. */
223   g_assert_cmpuint (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC), ==, 0);
224
225   /* Try setting the modification time with nsec-precision and it should set the
226    * usecs too. */
227   if (nsecs_supported)
228     {
229       gint new_usecs;
230       guint32 new_nsecs;
231       GDateTime *new_dt_usecs = NULL;
232
233       g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC, nsecs + 100,
234                                    G_FILE_QUERY_INFO_NONE, NULL, &error);
235       g_assert_no_error (error);
236
237       g_clear_object (&info);
238       info = g_file_query_info (file,
239                                 G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC "," G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC,
240                                 G_FILE_QUERY_INFO_NONE,
241                                 NULL, &error);
242       g_assert_no_error (error);
243
244       new_dt_usecs = g_file_info_get_modification_date_time (info);
245       g_assert_nonnull (new_dt_usecs);
246
247       new_usecs = g_date_time_get_microsecond (new_dt_usecs);
248       new_nsecs = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC);
249
250       g_assert_cmpuint (new_nsecs, ==, nsecs + 100);
251       g_assert_cmpuint (new_nsecs, >=, new_usecs * 1000);
252       g_assert_cmpuint (new_nsecs, <, (new_usecs + 1) * 1000);
253
254       g_date_time_unref (new_dt_usecs);
255     }
256
257   /* Clean up. */
258   g_clear_object (&io_stream);
259   g_file_delete (file, NULL, NULL);
260   g_clear_object (&file);
261
262   g_clear_object (&info);
263   g_date_time_unref (dt);
264   g_date_time_unref (dt_usecs);
265   g_date_time_unref (dt_new);
266   g_date_time_unref (dt_new_usecs);
267 }
268
269 static void
270 test_g_file_info_access_time (void)
271 {
272   GFile *file = NULL;
273   GFileIOStream *io_stream = NULL;
274   GFileInfo *info = NULL;
275   GDateTime *dt = NULL, *dt_usecs = NULL, *dt_new = NULL, *dt_new_usecs = NULL,
276             *dt_before_epoch = NULL, *dt_before_epoch_returned = NULL;
277   GTimeSpan ts;
278   gboolean nsecs_supported;
279   gint usecs;
280   guint32 nsecs;
281   GError *error = NULL;
282
283   g_test_summary ("Test that getting the access time of a file works.");
284
285   file = g_file_new_tmp ("g-file-info-test-XXXXXX", &io_stream, &error);
286   g_assert_no_error (error);
287
288   info = g_file_query_info (file,
289                             G_FILE_ATTRIBUTE_TIME_ACCESS,
290                             G_FILE_QUERY_INFO_NONE,
291                             NULL, &error);
292   g_assert_no_error (error);
293
294   if (!g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_ACCESS))
295     {
296       g_test_skip ("Skipping testing access time as it’s not supported by the kernel");
297       g_file_delete (file, NULL, NULL);
298       g_clear_object (&file);
299       g_clear_object (&info);
300       return;
301     }
302
303   /* Check the access time is retrievable. */
304   dt = g_file_info_get_access_date_time (info);
305   g_assert_nonnull (dt);
306
307   /* Try again with microsecond precision. */
308   g_clear_object (&info);
309   info = g_file_query_info (file,
310                             G_FILE_ATTRIBUTE_TIME_ACCESS "," G_FILE_ATTRIBUTE_TIME_ACCESS_USEC,
311                             G_FILE_QUERY_INFO_NONE,
312                             NULL, &error);
313   g_assert_no_error (error);
314
315   dt_usecs = g_file_info_get_access_date_time (info);
316   g_assert_nonnull (dt_usecs);
317
318   ts = g_date_time_difference (dt_usecs, dt);
319   g_assert_cmpint (ts, >=, 0);
320   g_assert_cmpint (ts, <, G_USEC_PER_SEC);
321
322   /* Try again with nanosecond precision. */
323   g_clear_object (&info);
324   info = g_file_query_info (file,
325                             G_FILE_ATTRIBUTE_TIME_ACCESS "," G_FILE_ATTRIBUTE_TIME_ACCESS_USEC "," G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC,
326                             G_FILE_QUERY_INFO_NONE,
327                             NULL, &error);
328   g_assert_no_error (error);
329
330   nsecs_supported = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC);
331   if (nsecs_supported)
332     {
333       usecs = g_date_time_get_microsecond (dt_usecs);
334       nsecs = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC);
335
336       g_assert_cmpuint (nsecs, >=, usecs * 1000);
337       g_assert_cmpuint (nsecs, <, (usecs + 1) * 1000);
338     }
339
340   /* Try round-tripping the access time. */
341   dt_new = g_date_time_add (dt_usecs, G_USEC_PER_SEC + 50);
342   g_file_info_set_access_date_time (info, dt_new);
343
344   dt_new_usecs = g_file_info_get_access_date_time (info);
345   ts = g_date_time_difference (dt_new_usecs, dt_new);
346   g_assert_cmpint (ts, ==, 0);
347
348   // try with a negative timestamp
349   dt_before_epoch = g_date_time_new_from_unix_utc (-10000);
350   g_file_info_set_access_date_time (info, dt_before_epoch);
351   dt_before_epoch_returned = g_file_info_get_access_date_time (info);
352   ts = g_date_time_difference (dt_before_epoch, dt_before_epoch_returned);
353   g_assert_cmpint (ts, ==, 0);
354
355   /* Setting the access time with usec-precision should have cleared nsecs. */
356   g_assert_cmpuint (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC), ==, 0);
357
358   /* Try setting the access time with nsec-precision and it should set the
359    * usecs too. */
360   if (nsecs_supported)
361     {
362       gint new_usecs;
363       guint32 new_nsecs;
364       GDateTime *new_dt_usecs = NULL;
365
366       g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC, nsecs + 100,
367                                    G_FILE_QUERY_INFO_NONE, NULL, &error);
368       g_assert_no_error (error);
369
370       g_clear_object (&info);
371       info = g_file_query_info (file,
372                                 G_FILE_ATTRIBUTE_TIME_ACCESS "," G_FILE_ATTRIBUTE_TIME_ACCESS_USEC "," G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC,
373                                 G_FILE_QUERY_INFO_NONE,
374                                 NULL, &error);
375       g_assert_no_error (error);
376
377       new_dt_usecs = g_file_info_get_access_date_time (info);
378       g_assert_nonnull (new_dt_usecs);
379
380       new_usecs = g_date_time_get_microsecond (new_dt_usecs);
381       new_nsecs = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC);
382
383       g_assert_cmpuint (new_nsecs, ==, nsecs + 100);
384       g_assert_cmpuint (new_nsecs, >=, new_usecs * 1000);
385       g_assert_cmpuint (new_nsecs, <, (new_usecs + 1) * 1000);
386
387       g_date_time_unref (new_dt_usecs);
388     }
389
390   /* Clean up. */
391   g_clear_object (&io_stream);
392   g_file_delete (file, NULL, NULL);
393   g_clear_object (&file);
394
395   g_clear_object (&info);
396   g_date_time_unref (dt);
397   g_date_time_unref (dt_usecs);
398   g_date_time_unref (dt_new);
399   g_date_time_unref (dt_new_usecs);
400   g_date_time_unref (dt_before_epoch);
401   g_date_time_unref (dt_before_epoch_returned);
402 }
403
404 static void
405 test_g_file_info_creation_time (void)
406 {
407   GFile *file = NULL;
408   GFileIOStream *io_stream = NULL;
409   GFileInfo *info = NULL;
410   GDateTime *dt = NULL, *dt_usecs = NULL, *dt_new = NULL, *dt_new_usecs = NULL,
411             *dt_before_epoch = NULL, *dt_before_epoch_returned = NULL;
412   GTimeSpan ts;
413   gboolean nsecs_supported;
414   gint usecs;
415   guint32 nsecs;
416   GError *error = NULL;
417
418   g_test_summary ("Test that getting the creation time of a file works.");
419
420   file = g_file_new_tmp ("g-file-info-test-XXXXXX", &io_stream, &error);
421   g_assert_no_error (error);
422
423   info = g_file_query_info (file,
424                             G_FILE_ATTRIBUTE_TIME_CREATED,
425                             G_FILE_QUERY_INFO_NONE,
426                             NULL, &error);
427   g_assert_no_error (error);
428
429   if (!g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_CREATED))
430     {
431       g_test_skip ("Skipping testing creation time as it’s not supported by the kernel");
432       g_clear_object (&io_stream);
433       g_file_delete (file, NULL, NULL);
434       g_clear_object (&file);
435       g_clear_object (&info);
436       return;
437     }
438
439   /* Check the creation time is retrievable. */
440   dt = g_file_info_get_creation_date_time (info);
441
442   /* Try again with microsecond precision. */
443   g_clear_object (&info);
444   info = g_file_query_info (file,
445                             G_FILE_ATTRIBUTE_TIME_CREATED "," G_FILE_ATTRIBUTE_TIME_CREATED_USEC,
446                             G_FILE_QUERY_INFO_NONE,
447                             NULL, &error);
448   g_assert_no_error (error);
449
450   dt_usecs = g_file_info_get_creation_date_time (info);
451   g_assert_nonnull (dt_usecs);
452
453   ts = g_date_time_difference (dt_usecs, dt);
454   g_assert_cmpint (ts, >=, 0);
455   g_assert_cmpint (ts, <, G_USEC_PER_SEC);
456
457   /* Try again with nanosecond precision. */
458   g_clear_object (&info);
459   info = g_file_query_info (file,
460                             G_FILE_ATTRIBUTE_TIME_CREATED "," G_FILE_ATTRIBUTE_TIME_CREATED_USEC "," G_FILE_ATTRIBUTE_TIME_CREATED_NSEC,
461                             G_FILE_QUERY_INFO_NONE,
462                             NULL, &error);
463   g_assert_no_error (error);
464
465   nsecs_supported = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_CREATED_NSEC);
466   if (nsecs_supported)
467     {
468       usecs = g_date_time_get_microsecond (dt_usecs);
469       nsecs = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CREATED_NSEC);
470
471       g_assert_cmpuint (nsecs, >=, usecs * 1000);
472       g_assert_cmpuint (nsecs, <, (usecs + 1) * 1000);
473     }
474
475   /* Try round-tripping the creation time. */
476   dt_new = g_date_time_add (dt_usecs, G_USEC_PER_SEC + 50);
477   g_file_info_set_creation_date_time (info, dt_new);
478
479   dt_new_usecs = g_file_info_get_creation_date_time (info);
480   ts = g_date_time_difference (dt_new_usecs, dt_new);
481   g_assert_cmpint (ts, ==, 0);
482
483   // try with a negative timestamp
484   dt_before_epoch = g_date_time_new_from_unix_utc (-10000);
485   g_file_info_set_creation_date_time (info, dt_before_epoch);
486   dt_before_epoch_returned = g_file_info_get_creation_date_time (info);
487   ts = g_date_time_difference (dt_before_epoch, dt_before_epoch_returned);
488   g_assert_cmpint (ts, ==, 0);
489
490   /* Setting the creation time with usec-precision should have cleared nsecs. */
491   g_assert_cmpuint (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CREATED_NSEC), ==, 0);
492
493   /* Try setting the creation time with nsec-precision and it should set the
494    * usecs too. */
495   if (nsecs_supported)
496     {
497       gint new_usecs;
498       guint32 new_nsecs;
499       GDateTime *new_dt_usecs = NULL;
500
501       /* This can fail on some platforms, even if reading CREATED_NSEC works */
502       g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_TIME_CREATED_NSEC, nsecs + 100,
503                                    G_FILE_QUERY_INFO_NONE, NULL, &error);
504       if (error == NULL)
505         {
506           g_clear_object (&info);
507           info = g_file_query_info (file,
508                                     G_FILE_ATTRIBUTE_TIME_CREATED "," G_FILE_ATTRIBUTE_TIME_CREATED_USEC "," G_FILE_ATTRIBUTE_TIME_CREATED_NSEC,
509                                     G_FILE_QUERY_INFO_NONE,
510                                     NULL, &error);
511           g_assert_no_error (error);
512
513           new_dt_usecs = g_file_info_get_creation_date_time (info);
514           g_assert_nonnull (new_dt_usecs);
515
516           new_usecs = g_date_time_get_microsecond (new_dt_usecs);
517           new_nsecs = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CREATED_NSEC);
518
519           g_assert_cmpuint (new_nsecs, ==, nsecs + 100);
520           g_assert_cmpuint (new_nsecs, >=, new_usecs * 1000);
521           g_assert_cmpuint (new_nsecs, <, (new_usecs + 1) * 1000);
522
523           g_date_time_unref (new_dt_usecs);
524         }
525       else
526         {
527           if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
528             g_clear_error (&error);
529           g_assert_no_error (error);
530         }
531     }
532
533   /* Clean up. */
534   g_clear_object (&io_stream);
535   g_file_delete (file, NULL, NULL);
536   g_clear_object (&file);
537
538   g_clear_object (&info);
539   g_date_time_unref (dt);
540   g_date_time_unref (dt_usecs);
541   g_date_time_unref (dt_new);
542   g_date_time_unref (dt_new_usecs);
543   g_date_time_unref (dt_before_epoch);
544   g_date_time_unref (dt_before_epoch_returned);
545 }
546
547 #ifdef G_OS_WIN32
548 static void
549 test_internal_enhanced_stdio (void)
550 {
551   char *p0, *p1, *ps;
552   gboolean try_sparse;
553   gchar *tmp_dir_root;
554   wchar_t *tmp_dir_root_w;
555   gchar *c;
556   DWORD fsflags;
557   FILE *f;
558   SYSTEMTIME st;
559   FILETIME ft;
560   HANDLE h;
561   GStatBuf statbuf_p0, statbuf_p1, statbuf_ps;
562   GFile *gf_p0, *gf_p1, *gf_ps;
563   GFileInfo *fi_p0, *fi_p1, *fi_ps;
564   guint64 size_p0, alsize_p0, size_ps, alsize_ps;
565   const gchar *id_p0;
566   const gchar *id_p1;
567   guint64 time_p0;
568   gchar *tmp_dir;
569   wchar_t *programdata_dir_w;
570   wchar_t *users_dir_w;
571   static const GUID folder_id_programdata = 
572     { 0x62AB5D82, 0xFDC1, 0x4DC3, { 0xA9, 0xDD, 0x07, 0x0D, 0x1D, 0x49, 0x5D, 0x97 } };
573   static const GUID folder_id_users = 
574     { 0x0762D272, 0xC50A, 0x4BB0, { 0xA3, 0x82, 0x69, 0x7D, 0xCD, 0x72, 0x9B, 0x80 } };
575   GDateTime *dt = NULL, *dt2 = NULL;
576   GTimeSpan ts;
577   /* Just before SYSTEMTIME limit (Jan 1 30827) */
578   const gint64 one_sec_before_systemtime_limit = 910670515199;
579   gboolean retval;
580   GError *local_error = NULL;
581
582
583   programdata_dir_w = NULL;
584   SHGetKnownFolderPath (&folder_id_programdata, 0, NULL, &programdata_dir_w);
585
586   users_dir_w = NULL;
587   SHGetKnownFolderPath (&folder_id_users, 0, NULL, &users_dir_w);
588
589   if (programdata_dir_w != NULL && users_dir_w != NULL)
590     {
591       gchar *programdata;
592       gchar *users_dir;
593       gchar *allusers;
594       gchar *commondata;
595       GFile *gf_programdata, *gf_allusers, *gf_commondata;
596       GFileInfo *fi_programdata, *fi_allusers, *fi_allusers_target, *fi_commondata, *fi_commondata_target;
597       GFileType ft_allusers;
598       GFileType ft_allusers_target;
599       GFileType ft_programdata;
600       GFileType ft_commondata;
601       gboolean allusers_is_symlink;
602       gboolean commondata_is_symlink;
603       gboolean commondata_is_mount_point;
604       guint32 allusers_reparse_tag;
605       guint32 commondata_reparse_tag;
606       const gchar *id_allusers;
607       const gchar *id_allusers_target;
608       const gchar *id_commondata_target;
609       const gchar *id_programdata;
610       const gchar *allusers_target;
611       const gchar *commondata_target;
612
613       /* C:/ProgramData */
614       programdata = g_utf16_to_utf8 (programdata_dir_w, -1, NULL, NULL, NULL);
615       g_assert_nonnull (programdata);
616       /* C:/Users */
617       users_dir = g_utf16_to_utf8 (users_dir_w, -1, NULL, NULL, NULL);
618       g_assert_nonnull (users_dir);
619       /* "C:/Users/All Users" is a known directory symlink
620        * for "C:/ProgramData".
621        */
622       allusers = g_build_filename (users_dir, "All Users", NULL);
623
624       /* "C:/Users/All Users/Application Data" is a known
625        * junction for "C:/ProgramData"
626        */
627       commondata = g_build_filename (allusers, "Application Data", NULL);
628
629       /* We don't test g_stat() and g_lstat() on these directories,
630        * because it is pointless - there's no way to tell that these
631        * functions behave correctly in this case
632        * (st_ino is useless, so we can't tell apart g_stat() and g_lstat()
633        *  results; st_mode is also useless as it does not support S_ISLNK;
634        *  and these directories have no interesting properties other
635        *  than [not] being symlinks).
636        */
637       gf_programdata = g_file_new_for_path (programdata);
638       gf_allusers = g_file_new_for_path (allusers);
639       gf_commondata = g_file_new_for_path (commondata);
640
641       fi_programdata = g_file_query_info (gf_programdata,
642                                           G_FILE_ATTRIBUTE_ID_FILE ","
643                                           G_FILE_ATTRIBUTE_STANDARD_TYPE,
644                                           G_FILE_QUERY_INFO_NONE,
645                                           NULL, NULL);
646
647       fi_allusers_target = g_file_query_info (gf_allusers,
648                                               G_FILE_ATTRIBUTE_ID_FILE ","
649                                               G_FILE_ATTRIBUTE_STANDARD_TYPE,
650                                               G_FILE_QUERY_INFO_NONE,
651                                               NULL, NULL);
652
653       fi_allusers = g_file_query_info (gf_allusers,
654                                        G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
655                                        G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
656                                        G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG ","
657                                        G_FILE_ATTRIBUTE_ID_FILE ","
658                                        G_FILE_ATTRIBUTE_STANDARD_TYPE,
659                                        G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
660                                        NULL, NULL);
661
662       fi_commondata = g_file_query_info (gf_commondata,
663                                          G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
664                                          G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
665                                          G_FILE_ATTRIBUTE_DOS_IS_MOUNTPOINT ","
666                                          G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG ","
667                                          G_FILE_ATTRIBUTE_ID_FILE ","
668                                          G_FILE_ATTRIBUTE_STANDARD_TYPE,
669                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
670                                          NULL, NULL);
671
672       fi_commondata_target = g_file_query_info (gf_commondata,
673                                                 G_FILE_ATTRIBUTE_ID_FILE ","
674                                                 G_FILE_ATTRIBUTE_STANDARD_TYPE,
675                                                 G_FILE_QUERY_INFO_NONE,
676                                                 NULL, NULL);
677
678       g_assert_true (g_file_info_has_attribute (fi_programdata, G_FILE_ATTRIBUTE_ID_FILE));
679       g_assert_true (g_file_info_has_attribute (fi_programdata, G_FILE_ATTRIBUTE_STANDARD_TYPE));
680
681       g_assert_true (g_file_info_has_attribute (fi_allusers_target, G_FILE_ATTRIBUTE_ID_FILE));
682       g_assert_true (g_file_info_has_attribute (fi_allusers_target, G_FILE_ATTRIBUTE_STANDARD_TYPE));
683       g_assert_true (g_file_info_has_attribute (fi_commondata_target, G_FILE_ATTRIBUTE_ID_FILE));
684       g_assert_true (g_file_info_has_attribute (fi_commondata_target, G_FILE_ATTRIBUTE_STANDARD_TYPE));
685
686       g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_ID_FILE));
687       g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_TYPE));
688       g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK));
689       g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG));
690       g_assert_true (g_file_info_has_attribute (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET));
691
692       g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_ID_FILE));
693       g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_STANDARD_TYPE));
694       g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK));
695       g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_DOS_IS_MOUNTPOINT));
696       g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG));
697       g_assert_true (g_file_info_has_attribute (fi_commondata, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET));
698
699       ft_allusers = g_file_info_get_file_type (fi_allusers);
700       ft_allusers_target = g_file_info_get_file_type (fi_allusers_target);
701       ft_programdata = g_file_info_get_file_type (fi_programdata);
702       ft_commondata = g_file_info_get_file_type (fi_commondata);
703
704       g_assert_cmpint (ft_allusers, ==, G_FILE_TYPE_DIRECTORY);
705       g_assert_cmpint (ft_allusers_target, ==, G_FILE_TYPE_DIRECTORY);
706       g_assert_cmpint (ft_programdata, ==, G_FILE_TYPE_DIRECTORY);
707       g_assert_cmpint (ft_commondata, ==, G_FILE_TYPE_DIRECTORY);
708
709       allusers_is_symlink = g_file_info_get_attribute_boolean (fi_allusers, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK);
710       allusers_reparse_tag = g_file_info_get_attribute_uint32 (fi_allusers, G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG);
711       commondata_is_symlink = g_file_info_get_attribute_boolean (fi_commondata, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK);
712       commondata_is_mount_point = g_file_info_get_attribute_boolean (fi_commondata, G_FILE_ATTRIBUTE_DOS_IS_MOUNTPOINT);
713       commondata_reparse_tag = g_file_info_get_attribute_uint32 (fi_commondata, G_FILE_ATTRIBUTE_DOS_REPARSE_POINT_TAG);
714
715       g_assert_true (allusers_is_symlink);
716       g_assert_cmpuint (allusers_reparse_tag, ==, IO_REPARSE_TAG_SYMLINK);
717       g_assert_true (commondata_is_symlink);
718       g_assert_true (commondata_is_mount_point);
719       g_assert_cmpuint (commondata_reparse_tag, ==, IO_REPARSE_TAG_MOUNT_POINT);
720
721       id_allusers = g_file_info_get_attribute_string (fi_allusers, G_FILE_ATTRIBUTE_ID_FILE);
722       id_allusers_target = g_file_info_get_attribute_string (fi_allusers_target, G_FILE_ATTRIBUTE_ID_FILE);
723       id_commondata_target = g_file_info_get_attribute_string (fi_commondata_target, G_FILE_ATTRIBUTE_ID_FILE);
724       id_programdata = g_file_info_get_attribute_string (fi_programdata, G_FILE_ATTRIBUTE_ID_FILE);
725
726       g_assert_cmpstr (id_allusers_target, ==, id_programdata);
727       g_assert_cmpstr (id_commondata_target, ==, id_programdata);
728       g_assert_cmpstr (id_allusers, !=, id_programdata);
729
730       allusers_target = g_file_info_get_symlink_target (fi_allusers);
731
732       g_assert_true (g_str_has_suffix (allusers_target, "ProgramData"));
733
734       commondata_target = g_file_info_get_symlink_target (fi_commondata);
735
736       g_assert_true (g_str_has_suffix (commondata_target, "ProgramData"));
737
738       g_object_unref (fi_allusers);
739       g_object_unref (fi_allusers_target);
740       g_object_unref (fi_commondata);
741       g_object_unref (fi_commondata_target);
742       g_object_unref (fi_programdata);
743       g_object_unref (gf_allusers);
744       g_object_unref (gf_commondata);
745       g_object_unref (gf_programdata);
746
747       g_free (allusers);
748       g_free (commondata);
749       g_free (users_dir);
750       g_free (programdata);
751     }
752
753   if (programdata_dir_w)
754     CoTaskMemFree (programdata_dir_w);
755
756   if (users_dir_w)
757     CoTaskMemFree (users_dir_w);
758
759   tmp_dir = g_dir_make_tmp ("glib_stdio_testXXXXXX", NULL);
760   g_assert_nonnull (tmp_dir);
761
762   /* Technically, this isn't necessary - we already assume NTFS, because of
763    * symlink support, and NTFS also supports sparse files. Still, given
764    * the amount of unusual I/O APIs called in this test, checking for
765    * sparse file support of the filesystem where temp directory is
766    * doesn't seem to be out of place.
767    */
768   try_sparse = FALSE;
769   tmp_dir_root = g_strdup (tmp_dir);
770   /* We need "C:\\" or "C:/", with a trailing [back]slash */
771   for (c = tmp_dir_root; c && c[0] && c[1]; c++)
772     if (c[0] == ':')
773       {
774         c[2] = '\0';
775         break;
776       }
777   tmp_dir_root_w = g_utf8_to_utf16 (tmp_dir_root, -1, NULL, NULL, NULL);
778   g_assert_nonnull (tmp_dir_root_w);
779   g_free (tmp_dir_root);
780   g_assert_true (GetVolumeInformationW (tmp_dir_root_w, NULL, 0, NULL, NULL, &fsflags, NULL, 0));
781   g_free (tmp_dir_root_w);
782   try_sparse = (fsflags & FILE_SUPPORTS_SPARSE_FILES) == FILE_SUPPORTS_SPARSE_FILES;
783
784   p0 = g_build_filename (tmp_dir, "zool", NULL);
785   p1 = g_build_filename (tmp_dir, "looz", NULL);
786   ps = g_build_filename (tmp_dir, "sparse", NULL);
787
788   if (try_sparse)
789     {
790       FILE_SET_SPARSE_BUFFER ssb;
791       FILE_ZERO_DATA_INFORMATION zdi;
792
793       g_remove (ps);
794
795       f = g_fopen (ps, "wb");
796       g_assert_nonnull (f);
797
798       h = (HANDLE) _get_osfhandle (fileno (f));
799       g_assert_cmpuint ((guintptr) h, !=, (guintptr) INVALID_HANDLE_VALUE);
800
801       ssb.SetSparse = TRUE;
802       g_assert_true (DeviceIoControl (h,
803                      FSCTL_SET_SPARSE,
804                      &ssb, sizeof (ssb),
805                      NULL, 0, NULL, NULL));
806
807       /* Make it a sparse file that starts with 4GBs of zeros */
808       zdi.FileOffset.QuadPart = 0;
809       zdi.BeyondFinalZero.QuadPart = 0xFFFFFFFFULL + 1;
810       g_assert_true (DeviceIoControl (h,
811                      FSCTL_SET_ZERO_DATA,
812                      &zdi, sizeof (zdi),
813                      NULL, 0, NULL, NULL));
814
815       /* Let's not keep this seemingly 4GB monster around
816        * longer than we absolutely need to. Do all operations
817        * without assertions, then remove the file immediately.
818        */
819       _fseeki64 (f, 0xFFFFFFFFULL, SEEK_SET);
820       fprintf (f, "Hello 4GB World!");
821       fflush (f);
822       fclose (f);
823
824       memset (&statbuf_ps, 0, sizeof (statbuf_ps));
825
826       g_stat (ps, &statbuf_ps);
827
828       gf_ps = g_file_new_for_path (ps);
829
830       fi_ps = g_file_query_info (gf_ps,
831                                  G_FILE_ATTRIBUTE_STANDARD_SIZE ","
832                                  G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE,
833                                  G_FILE_QUERY_INFO_NONE,
834                                  NULL, NULL);
835
836       g_remove (ps);
837
838       g_assert_true (g_file_info_has_attribute (fi_ps, G_FILE_ATTRIBUTE_STANDARD_SIZE));
839       g_assert_true (g_file_info_has_attribute (fi_ps, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE));
840
841       size_ps = g_file_info_get_attribute_uint64 (fi_ps, G_FILE_ATTRIBUTE_STANDARD_SIZE);
842       alsize_ps = g_file_info_get_attribute_uint64 (fi_ps, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE);
843
844       /* allocated size should small (usually - size of the FS cluster,
845        * let's assume it's less than 1 gigabyte),
846        * size should be more than 4 gigabytes,
847        * st_size should not exceed its 0xFFFFFFFF 32-bit limit,
848        * and should be nonzero (this also detects a failed g_stat() earlier).
849        */
850       g_assert_cmpuint (alsize_ps, <, 0x40000000);
851       g_assert_cmpuint (size_ps, >, G_GUINT64_CONSTANT (0xFFFFFFFF));
852       g_assert_cmpuint (statbuf_ps.st_size, >, 0);
853 #if defined(_WIN64)
854       g_assert_cmpuint (statbuf_ps.st_size, ==, G_GUINT64_CONSTANT (0x10000000f));
855 #else
856       g_assert_cmpuint (statbuf_ps.st_size, <=, 0xFFFFFFFF);
857 #endif
858
859       g_object_unref (fi_ps);
860       g_object_unref (gf_ps);
861     }
862
863   /* Wa-a-ay past 02/07/2106 @ 6:28am (UTC),
864    * which is the date corresponding to 0xFFFFFFFF + 1.
865    * This is easier to check than Y2038 (0x80000000 + 1),
866    * since there's no need to worry about signedness this way.
867    */
868   st.wYear = 2106;
869   st.wMonth = 2;
870   st.wDay = 9;
871   st.wHour = 0;
872   st.wMinute = 0;
873   st.wSecond = 0;
874   st.wMilliseconds = 0;
875
876   g_assert_true (SystemTimeToFileTime (&st, &ft));
877
878   f = g_fopen (p0, "w");
879   g_assert_nonnull (f);
880
881   h = (HANDLE) _get_osfhandle (fileno (f));
882   g_assert_cmpuint ((guintptr) h, !=, (guintptr) INVALID_HANDLE_VALUE);
883
884   fprintf (f, "1");
885   fflush (f);
886
887   g_assert_true (SetFileTime (h, &ft, &ft, &ft));
888
889   fclose (f);
890
891   f = g_fopen (p1, "w");
892   g_assert_nonnull (f);
893
894   fclose (f);
895
896   memset (&statbuf_p0, 0, sizeof (statbuf_p0));
897   memset (&statbuf_p1, 0, sizeof (statbuf_p1));
898
899   g_assert_cmpint (g_stat (p0, &statbuf_p0), ==, 0);
900   g_assert_cmpint (g_stat (p1, &statbuf_p1), ==, 0);
901
902   gf_p0 = g_file_new_for_path (p0);
903   gf_p1 = g_file_new_for_path (p1);
904
905   fi_p0 = g_file_query_info (gf_p0,
906                              G_FILE_ATTRIBUTE_STANDARD_SIZE ","
907                              G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE ","
908                              G_FILE_ATTRIBUTE_ID_FILE ","
909                              G_FILE_ATTRIBUTE_TIME_MODIFIED ","
910                              G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC ","
911                              G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC,
912                              G_FILE_QUERY_INFO_NONE,
913                              NULL, NULL);
914
915   fi_p1 = g_file_query_info (gf_p1,
916                              G_FILE_ATTRIBUTE_STANDARD_SIZE ","
917                              G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE ","
918                              G_FILE_ATTRIBUTE_ID_FILE ","
919                              G_FILE_ATTRIBUTE_TIME_MODIFIED ","
920                              G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC ","
921                              G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC,
922                              G_FILE_QUERY_INFO_NONE,
923                              NULL, NULL);
924
925   g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_STANDARD_SIZE));
926   g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE));
927   g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_ID_FILE));
928   g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED));
929   g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC));
930   g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC));
931
932   g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_STANDARD_SIZE));
933   g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE));
934   g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_ID_FILE));
935   g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_TIME_MODIFIED));
936   g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC));
937   g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC));
938
939   size_p0 = g_file_info_get_attribute_uint64 (fi_p0, G_FILE_ATTRIBUTE_STANDARD_SIZE);
940   alsize_p0 = g_file_info_get_attribute_uint64 (fi_p0, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE);
941
942   /* size should be 1, allocated size should be something else
943    * (could be 0 or the size of the FS cluster, but never 1).
944    */
945   g_assert_cmpuint (size_p0, ==, statbuf_p0.st_size);
946   g_assert_cmpuint (size_p0, ==, 1);
947   g_assert_cmpuint (alsize_p0, !=, size_p0);
948
949   id_p0 = g_file_info_get_attribute_string (fi_p0, G_FILE_ATTRIBUTE_ID_FILE);
950   id_p1 = g_file_info_get_attribute_string (fi_p1, G_FILE_ATTRIBUTE_ID_FILE);
951
952   /* st_ino from W32 stat() is useless for file identification.
953    * It will be either 0, or it will be the same for both files.
954    */
955   g_assert_cmpint (statbuf_p0.st_ino, ==, statbuf_p1.st_ino);
956   g_assert_cmpstr (id_p0, !=, id_p1);
957
958   time_p0 = g_file_info_get_attribute_uint64 (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED);
959
960   /* Check that GFileInfo doesn't suffer from Y2106 problem.
961    * Don't check stat(), as its contents may vary depending on
962    * the host platform architecture
963    * (time fields are 32-bit on 32-bit Windows,
964    *  and 64-bit on 64-bit Windows, usually),
965    * so it *can* pass this test in some cases.
966    */
967   g_assert_cmpuint (time_p0, >, G_GUINT64_CONSTANT (0xFFFFFFFF));
968
969   dt = g_file_info_get_modification_date_time (fi_p0);
970   g_assert_nonnull (dt);
971   dt2 = g_date_time_add (dt, G_USEC_PER_SEC / 100 * 200);
972   g_object_unref (fi_p0);
973   fi_p0 = g_file_info_new ();
974   g_file_info_set_modification_date_time (fi_p0, dt2);
975
976   g_assert_true (g_file_set_attributes_from_info (gf_p0,
977                                                   fi_p0,
978                                                   G_FILE_QUERY_INFO_NONE,
979                                                   NULL,
980                                                   NULL));
981   g_date_time_unref (dt2);
982   g_object_unref (fi_p0);
983   fi_p0 = g_file_query_info (gf_p0,
984                              G_FILE_ATTRIBUTE_TIME_MODIFIED ","
985                              G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC ","
986                              G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC,
987                              G_FILE_QUERY_INFO_NONE,
988                              NULL, NULL);
989   dt2 = g_file_info_get_modification_date_time (fi_p0);
990   ts = g_date_time_difference (dt2, dt);
991   g_assert_cmpint (ts, >, 0);
992   g_assert_cmpint (ts, <, G_USEC_PER_SEC / 100 * 300);
993
994   g_date_time_unref (dt);
995   g_date_time_unref (dt2);
996
997   g_file_info_set_attribute_uint64 (fi_p0,
998                                     G_FILE_ATTRIBUTE_TIME_MODIFIED,
999                                     one_sec_before_systemtime_limit);
1000   g_file_info_set_attribute_uint32 (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, 0);
1001   g_assert_true (g_file_set_attributes_from_info (gf_p0,
1002                                                   fi_p0,
1003                                                   G_FILE_QUERY_INFO_NONE,
1004                                                   NULL,
1005                                                   NULL));
1006
1007   g_file_info_set_attribute_uint64 (fi_p0,
1008                                    G_FILE_ATTRIBUTE_TIME_MODIFIED,
1009                                    one_sec_before_systemtime_limit + G_USEC_PER_SEC * 2);
1010   g_file_info_set_attribute_uint32 (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, 0);
1011   retval = g_file_set_attributes_from_info (gf_p0,
1012                                             fi_p0,
1013                                             G_FILE_QUERY_INFO_NONE,
1014                                             NULL,
1015                                             &local_error);
1016   g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA);
1017   g_assert_false (retval);
1018   g_clear_error (&local_error);
1019
1020   g_object_unref (fi_p0);
1021   g_object_unref (fi_p1);
1022   g_object_unref (gf_p0);
1023   g_object_unref (gf_p1);
1024   g_remove (p0);
1025   g_remove (p1);
1026   g_free (p0);
1027   g_free (p1);
1028   g_rmdir (tmp_dir);
1029 }
1030 #endif
1031
1032 static void
1033 test_xattrs (void)
1034 {
1035   GFile *file = NULL;
1036   GFileIOStream *stream = NULL;
1037   GFileInfo *file_info0 = NULL, *file_info1 = NULL, *file_info2 = NULL;
1038   GError *local_error = NULL;
1039
1040   g_test_summary ("Test setting and getting escaped xattrs");
1041
1042   /* Create a temporary file; no need to write anything to it. */
1043   file = g_file_new_tmp ("g-file-info-test-xattrs-XXXXXX", &stream, &local_error);
1044   g_assert_no_error (local_error);
1045   g_assert_nonnull (file);
1046
1047   g_io_stream_close (G_IO_STREAM (stream), NULL, NULL);
1048   g_object_unref (stream);
1049
1050   /* Check the existing xattrs. */
1051   file_info0 = g_file_query_info (file, "xattr::*", G_FILE_QUERY_INFO_NONE, NULL, &local_error);
1052   g_assert_no_error (local_error);
1053   g_assert_nonnull (file_info0);
1054
1055   /* Set some new xattrs, with escaping and some embedded nuls. */
1056   g_file_info_set_attribute_string (file_info0, "xattr::escaped", "hello\\x82\\x80\\xbd");
1057   g_file_info_set_attribute_string (file_info0, "xattr::string", "hi there");
1058   g_file_info_set_attribute_string (file_info0, "xattr::embedded-nul", "hi\\x00there");
1059   g_file_info_set_attribute_string (file_info0, "xattr::deleteme", "this attribute will be deleted");
1060
1061   g_file_set_attributes_from_info (file, file_info0, G_FILE_QUERY_INFO_NONE, NULL, &local_error);
1062
1063   g_object_unref (file_info0);
1064
1065   if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
1066     {
1067       g_test_skip ("xattrs not supported on this file system");
1068       g_clear_error (&local_error);
1069     }
1070   else
1071     {
1072       g_assert_no_error (local_error);
1073
1074       /* Check they were set properly. */
1075       file_info1 = g_file_query_info (file, "xattr::*", G_FILE_QUERY_INFO_NONE, NULL, &local_error);
1076       g_assert_no_error (local_error);
1077       g_assert_nonnull (file_info1);
1078
1079       g_assert_true (g_file_info_has_namespace (file_info1, "xattr"));
1080
1081       g_assert_cmpstr (g_file_info_get_attribute_string (file_info1, "xattr::escaped"), ==, "hello\\x82\\x80\\xbd");
1082       g_assert_cmpstr (g_file_info_get_attribute_string (file_info1, "xattr::string"), ==, "hi there");
1083       g_assert_cmpstr (g_file_info_get_attribute_string (file_info1, "xattr::embedded-nul"), ==, "hi\\x00there");
1084       g_assert_cmpstr (g_file_info_get_attribute_string (file_info1, "xattr::deleteme"), ==, "this attribute will be deleted");
1085
1086       g_object_unref (file_info1);
1087
1088       /* Check whether removing extended attributes works. */
1089       g_file_set_attribute (file, "xattr::deleteme", G_FILE_ATTRIBUTE_TYPE_INVALID, NULL, G_FILE_QUERY_INFO_NONE, NULL, &local_error);
1090       g_assert_no_error (local_error);
1091       file_info2 = g_file_query_info (file, "xattr::deleteme", G_FILE_QUERY_INFO_NONE, NULL, &local_error);
1092       g_assert_no_error (local_error);
1093       g_assert_nonnull (file_info2);
1094       g_assert_cmpstr (g_file_info_get_attribute_string (file_info2, "xattr::deleteme"), ==, NULL);
1095
1096       g_object_unref (file_info2);
1097     }
1098
1099   /* Tidy up. */
1100   g_file_delete (file, NULL, NULL);
1101
1102   g_object_unref (file);
1103 }
1104
1105 static void
1106 test_set_modified_date_time_precision (void)
1107 {
1108   GDateTime *modified = NULL;
1109   GFile *file = NULL;
1110   GFileIOStream *stream = NULL;
1111   GFileInfo *info = NULL;
1112   GError *local_error = NULL;
1113
1114
1115   g_test_summary ("Test that g_file_info_set_modified_date_time() preserves microseconds");
1116   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3116");
1117
1118   file = g_file_new_tmp ("g-file-info-test-set-modified-date-time-precision-XXXXXX", &stream, &local_error);
1119   g_assert_no_error (local_error);
1120
1121   modified = g_date_time_new_from_iso8601 ("2000-01-01T00:00:00.123456Z", NULL);
1122
1123   info = g_file_query_info (file,
1124   G_FILE_ATTRIBUTE_TIME_MODIFIED ","
1125         G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC ","
1126         G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC, G_FILE_QUERY_INFO_NONE, NULL, &local_error);
1127   g_assert_no_error (local_error);
1128
1129   g_file_info_set_modification_date_time (info, modified);
1130   g_assert_true (g_file_set_attributes_from_info (file, info, G_FILE_QUERY_INFO_NONE, NULL, &local_error));
1131   g_assert_no_error (local_error);
1132
1133   g_clear_object (&info);
1134   g_clear_pointer (&modified, g_date_time_unref);
1135
1136   info = g_file_query_info (file,
1137   G_FILE_ATTRIBUTE_TIME_MODIFIED ","
1138         G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC ","
1139         G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC, G_FILE_QUERY_INFO_NONE, NULL, &local_error);
1140   g_assert_no_error (local_error);
1141
1142   g_assert_cmpuint (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC), ==, 123456);
1143
1144   g_clear_object (&stream);
1145   g_clear_object (&info);
1146   g_clear_object (&file);
1147 }
1148
1149 int
1150 main (int   argc,
1151       char *argv[])
1152 {
1153   g_test_init (&argc, &argv, NULL);
1154
1155   g_test_add_func ("/g-file-info/test_g_file_info", test_g_file_info);
1156   g_test_add_func ("/g-file-info/test_g_file_info/modification-time", test_g_file_info_modification_time);
1157   g_test_add_func ("/g-file-info/test_g_file_info/access-time", test_g_file_info_access_time);
1158   g_test_add_func ("/g-file-info/test_g_file_info/creation-time", test_g_file_info_creation_time);
1159 #ifdef G_OS_WIN32
1160   g_test_add_func ("/g-file-info/internal-enhanced-stdio", test_internal_enhanced_stdio);
1161 #endif
1162   g_test_add_func ("/g-file-info/xattrs", test_xattrs);
1163   g_test_add_func ("/g-file-info/set-modified-date-time-precision", test_set_modified_date_time_precision);
1164   
1165   return g_test_run();
1166 }