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 (str_array: 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, type: 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 (str_array: attr_list); |
101 | |
102 | test_assigned_values (info); |
103 | |
104 | /* Test dups */ |
105 | info_dup = g_file_info_dup (other: info); |
106 | g_assert_nonnull (info_dup); |
107 | test_assigned_values (info: info_dup); |
108 | |
109 | info_copy = g_file_info_new (); |
110 | g_file_info_copy_into (src_info: info_dup, dest_info: info_copy); |
111 | g_assert_nonnull (info_copy); |
112 | test_assigned_values (info: 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, attr_value: 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, mask: 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 (object: info); |
140 | g_object_unref (object: info_dup); |
141 | g_object_unref (object: 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 (summary: "Test that getting the modification time of a file works." ); |
155 | |
156 | file = g_file_new_tmp (tmpl: "g-file-info-test-XXXXXX" , iostream: &io_stream, error: &error); |
157 | g_assert_no_error (error); |
158 | |
159 | info = g_file_query_info (file, |
160 | G_FILE_ATTRIBUTE_TIME_MODIFIED, |
161 | flags: G_FILE_QUERY_INFO_NONE, |
162 | NULL, error: &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 | flags: G_FILE_QUERY_INFO_NONE, |
174 | NULL, error: &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 (end: dt_usecs, begin: 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 (datetime: dt_usecs, G_USEC_PER_SEC + 50); |
186 | g_file_info_set_modification_date_time (info, mtime: dt_new); |
187 | |
188 | dt_new_usecs = g_file_info_get_modification_date_time (info); |
189 | ts = g_date_time_difference (end: dt_new_usecs, begin: 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 (datetime: dt); |
199 | g_date_time_unref (datetime: dt_usecs); |
200 | g_date_time_unref (datetime: dt_new); |
201 | g_date_time_unref (datetime: 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 (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 (tmpl: "g-file-info-test-xattrs-XXXXXX" , iostream: &stream, error: &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 (object: stream); |
697 | |
698 | /* Check the existing xattrs. */ |
699 | file_info0 = g_file_query_info (file, attributes: "xattr::*" , flags: G_FILE_QUERY_INFO_NONE, NULL, error: &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 (info: file_info0, attribute: "xattr::escaped" , attr_value: "hello\\x82\\x80\\xbd" ); |
705 | g_file_info_set_attribute_string (info: file_info0, attribute: "xattr::string" , attr_value: "hi there" ); |
706 | g_file_info_set_attribute_string (info: file_info0, attribute: "xattr::embedded-nul" , attr_value: "hi\\x00there" ); |
707 | |
708 | g_file_set_attributes_from_info (file, info: file_info0, flags: G_FILE_QUERY_INFO_NONE, NULL, error: &local_error); |
709 | |
710 | g_object_unref (object: file_info0); |
711 | |
712 | if (g_error_matches (error: local_error, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED)) |
713 | { |
714 | g_test_skip (msg: "xattrs not supported on this file system" ); |
715 | g_clear_error (err: &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, attributes: "xattr::*" , flags: G_FILE_QUERY_INFO_NONE, NULL, error: &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 (object: file_info1); |
733 | } |
734 | |
735 | /* Tidy up. */ |
736 | g_file_delete (file, NULL, NULL); |
737 | |
738 | g_object_unref (object: file); |
739 | } |
740 | |
741 | int |
742 | main (int argc, |
743 | char *argv[]) |
744 | { |
745 | g_test_init (argc: &argc, argv: &argv, NULL); |
746 | |
747 | g_test_add_func (testpath: "/g-file-info/test_g_file_info" , test_func: test_g_file_info); |
748 | g_test_add_func (testpath: "/g-file-info/test_g_file_info/modification-time" , test_func: 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 (testpath: "/g-file-info/xattrs" , test_func: test_xattrs); |
753 | |
754 | return g_test_run(); |
755 | } |
756 | |