Change LGPL-2.1+ to LGPL-2.1-or-later
[platform/upstream/glib.git] / gio / tests / file.c
1 #include "config.h"
2
3 #include <locale.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <gio/gio.h>
8 #include <gio/gfiledescriptorbased.h>
9 #ifdef G_OS_UNIX
10 #include <sys/stat.h>
11 #endif
12
13 typedef struct
14 {
15   GMainLoop *loop;
16   GError **error;
17 } AsyncErrorData;
18
19 static void
20 test_basic_for_file (GFile       *file,
21                      const gchar *suffix)
22 {
23   gchar *s;
24
25   s = g_file_get_basename (file);
26   g_assert_cmpstr (s, ==, "testfile");
27   g_free (s);
28
29   s = g_file_get_uri (file);
30   g_assert_true (g_str_has_prefix (s, "file://"));
31   g_assert_true (g_str_has_suffix (s, suffix));
32   g_free (s);
33
34   g_assert_true (g_file_has_uri_scheme (file, "file"));
35   s = g_file_get_uri_scheme (file);
36   g_assert_cmpstr (s, ==, "file");
37   g_free (s);
38 }
39
40 static void
41 test_basic (void)
42 {
43   GFile *file;
44
45   file = g_file_new_for_path ("./some/directory/testfile");
46   test_basic_for_file (file, "/some/directory/testfile");
47   g_object_unref (file);
48 }
49
50 static void
51 test_build_filename (void)
52 {
53   GFile *file;
54
55   file = g_file_new_build_filename (".", "some", "directory", "testfile", NULL);
56   test_basic_for_file (file, "/some/directory/testfile");
57   g_object_unref (file);
58
59   file = g_file_new_build_filename ("testfile", NULL);
60   test_basic_for_file (file, "/testfile");
61   g_object_unref (file);
62 }
63
64 static void
65 test_build_filenamev (void)
66 {
67   GFile *file;
68
69   const gchar *args[] = { ".", "some", "directory", "testfile", NULL };
70   file = g_file_new_build_filenamev (args);
71   test_basic_for_file (file, "/some/directory/testfile");
72   g_object_unref (file);
73
74   const gchar *brgs[] = { "testfile", NULL };
75   file = g_file_new_build_filenamev (brgs);
76   test_basic_for_file (file, "/testfile");
77   g_object_unref (file);
78 }
79
80 static void
81 test_parent (void)
82 {
83   GFile *file;
84   GFile *file2;
85   GFile *parent;
86   GFile *root;
87
88   file = g_file_new_for_path ("./some/directory/testfile");
89   file2 = g_file_new_for_path ("./some/directory");
90   root = g_file_new_for_path ("/");
91
92   g_assert_true (g_file_has_parent (file, file2));
93
94   parent = g_file_get_parent (file);
95   g_assert_true (g_file_equal (parent, file2));
96   g_object_unref (parent);
97
98   g_assert_null (g_file_get_parent (root));
99
100   g_object_unref (file);
101   g_object_unref (file2);
102   g_object_unref (root);
103 }
104
105 static void
106 test_child (void)
107 {
108   GFile *file;
109   GFile *child;
110   GFile *child2;
111
112   file = g_file_new_for_path ("./some/directory");
113   child = g_file_get_child (file, "child");
114   g_assert_true (g_file_has_parent (child, file));
115
116   child2 = g_file_get_child_for_display_name (file, "child2", NULL);
117   g_assert_true (g_file_has_parent (child2, file));
118
119   g_object_unref (child);
120   g_object_unref (child2);
121   g_object_unref (file);
122 }
123
124 static void
125 test_empty_path (void)
126 {
127   GFile *file = NULL;
128
129   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2328");
130   g_test_summary ("Check that creating a file with an empty path results in errors");
131
132   /* Creating the file must always succeed. */
133   file = g_file_new_for_path ("");
134   g_assert_nonnull (file);
135
136   /* But then querying its path should indicate it’s invalid. */
137   g_assert_null (g_file_get_path (file));
138   g_assert_null (g_file_get_basename (file));
139   g_assert_null (g_file_get_parent (file));
140
141   g_object_unref (file);
142 }
143
144 static void
145 test_type (void)
146 {
147   GFile *datapath_f;
148   GFile *file;
149   GFileType type;
150   GError *error = NULL;
151
152   datapath_f = g_file_new_for_path (g_test_get_dir (G_TEST_DIST));
153
154   file = g_file_get_child (datapath_f, "g-icon.c");
155   type = g_file_query_file_type (file, 0, NULL);
156   g_assert_cmpint (type, ==, G_FILE_TYPE_REGULAR);
157   g_object_unref (file);
158
159   file = g_file_get_child (datapath_f, "cert-tests");
160   type = g_file_query_file_type (file, 0, NULL);
161   g_assert_cmpint (type, ==, G_FILE_TYPE_DIRECTORY);
162
163   g_file_read (file, NULL, &error);
164   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY);
165   g_error_free (error);
166   g_object_unref (file);
167
168   g_object_unref (datapath_f);
169 }
170
171 static void
172 test_parse_name (void)
173 {
174   GFile *file;
175   gchar *name;
176
177   file = g_file_new_for_uri ("file://somewhere");
178   name = g_file_get_parse_name (file);
179   g_assert_cmpstr (name, ==, "file://somewhere");
180   g_object_unref (file);
181   g_free (name);
182
183   file = g_file_parse_name ("~foo");
184   name = g_file_get_parse_name (file);
185   g_assert_nonnull (name);
186   g_object_unref (file);
187   g_free (name);
188 }
189
190 typedef struct
191 {
192   GMainContext *context;
193   GFile *file;
194   GFileMonitor *monitor;
195   GOutputStream *ostream;
196   GInputStream *istream;
197   gint buffersize;
198   gint monitor_created;
199   gint monitor_deleted;
200   gint monitor_changed;
201   gchar *monitor_path;
202   gsize pos;
203   const gchar *data;
204   gchar *buffer;
205   guint timeout;
206   gboolean file_deleted;
207   gboolean timed_out;
208 } CreateDeleteData;
209
210 static void
211 monitor_changed (GFileMonitor      *monitor,
212                  GFile             *file,
213                  GFile             *other_file,
214                  GFileMonitorEvent  event_type,
215                  gpointer           user_data)
216 {
217   CreateDeleteData *data = user_data;
218   gchar *path;
219   const gchar *peeked_path;
220
221   path = g_file_get_path (file);
222   peeked_path = g_file_peek_path (file);
223   g_assert_cmpstr (data->monitor_path, ==, path);
224   g_assert_cmpstr (path, ==, peeked_path);
225   g_free (path);
226
227   if (event_type == G_FILE_MONITOR_EVENT_CREATED)
228     data->monitor_created++;
229   if (event_type == G_FILE_MONITOR_EVENT_DELETED)
230     data->monitor_deleted++;
231   if (event_type == G_FILE_MONITOR_EVENT_CHANGED)
232     data->monitor_changed++;
233
234   g_main_context_wakeup (data->context);
235 }
236
237 static void
238 iclosed_cb (GObject      *source,
239             GAsyncResult *res,
240             gpointer      user_data)
241 {
242   CreateDeleteData *data = user_data;
243   GError *error;
244   gboolean ret;
245
246   error = NULL;
247   ret = g_input_stream_close_finish (data->istream, res, &error);
248   g_assert_no_error (error);
249   g_assert_true (ret);
250
251   g_assert_true (g_input_stream_is_closed (data->istream));
252
253   ret = g_file_delete (data->file, NULL, &error);
254   g_assert_true (ret);
255   g_assert_no_error (error);
256
257   data->file_deleted = TRUE;
258   g_main_context_wakeup (data->context);
259 }
260
261 static void
262 read_cb (GObject      *source,
263          GAsyncResult *res,
264          gpointer      user_data)
265 {
266   CreateDeleteData *data = user_data;
267   GError *error;
268   gssize size;
269
270   error = NULL;
271   size = g_input_stream_read_finish (data->istream, res, &error);
272   g_assert_no_error (error);
273
274   data->pos += size;
275   if (data->pos < strlen (data->data))
276     {
277       g_input_stream_read_async (data->istream,
278                                  data->buffer + data->pos,
279                                  strlen (data->data) - data->pos,
280                                  0,
281                                  NULL,
282                                  read_cb,
283                                  data);
284     }
285   else
286     {
287       g_assert_cmpstr (data->buffer, ==, data->data);
288       g_assert_false (g_input_stream_is_closed (data->istream));
289       g_input_stream_close_async (data->istream, 0, NULL, iclosed_cb, data);
290     }
291 }
292
293 static void
294 ipending_cb (GObject      *source,
295              GAsyncResult *res,
296              gpointer      user_data)
297 {
298   CreateDeleteData *data = user_data;
299   GError *error;
300
301   error = NULL;
302   g_input_stream_read_finish (data->istream, res, &error);
303   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING);
304   g_error_free (error);
305 }
306
307 static void
308 skipped_cb (GObject      *source,
309             GAsyncResult *res,
310             gpointer      user_data)
311 {
312   CreateDeleteData *data = user_data;
313   GError *error;
314   gssize size;
315
316   error = NULL;
317   size = g_input_stream_skip_finish (data->istream, res, &error);
318   g_assert_no_error (error);
319   g_assert_cmpint (size, ==, data->pos);
320
321   g_input_stream_read_async (data->istream,
322                              data->buffer + data->pos,
323                              strlen (data->data) - data->pos,
324                              0,
325                              NULL,
326                              read_cb,
327                              data);
328   /* check that we get a pending error */
329   g_input_stream_read_async (data->istream,
330                              data->buffer + data->pos,
331                              strlen (data->data) - data->pos,
332                              0,
333                              NULL,
334                              ipending_cb,
335                              data);
336 }
337
338 static void
339 opened_cb (GObject      *source,
340            GAsyncResult *res,
341            gpointer      user_data)
342 {
343   GFileInputStream *base;
344   CreateDeleteData *data = user_data;
345   GError *error;
346
347   error = NULL;
348   base = g_file_read_finish (data->file, res, &error);
349   g_assert_no_error (error);
350
351   if (data->buffersize == 0)
352     data->istream = G_INPUT_STREAM (g_object_ref (base));
353   else
354     data->istream = g_buffered_input_stream_new_sized (G_INPUT_STREAM (base), data->buffersize);
355   g_object_unref (base);
356
357   data->buffer = g_new0 (gchar, strlen (data->data) + 1);
358
359   /* copy initial segment directly, then skip */
360   memcpy (data->buffer, data->data, 10);
361   data->pos = 10;
362
363   g_input_stream_skip_async (data->istream,
364                              10,
365                              0,
366                              NULL,
367                              skipped_cb,
368                              data);
369 }
370
371 static void
372 oclosed_cb (GObject      *source,
373             GAsyncResult *res,
374             gpointer      user_data)
375 {
376   CreateDeleteData *data = user_data;
377   GError *error;
378   gboolean ret;
379
380   error = NULL;
381   ret = g_output_stream_close_finish (data->ostream, res, &error);
382   g_assert_no_error (error);
383   g_assert_true (ret);
384   g_assert_true (g_output_stream_is_closed (data->ostream));
385
386   g_file_read_async (data->file, 0, NULL, opened_cb, data);
387 }
388
389 static void
390 written_cb (GObject      *source,
391             GAsyncResult *res,
392             gpointer      user_data)
393 {
394   CreateDeleteData *data = user_data;
395   gssize size;
396   GError *error;
397
398   error = NULL;
399   size = g_output_stream_write_finish (data->ostream, res, &error);
400   g_assert_no_error (error);
401
402   data->pos += size;
403   if (data->pos < strlen (data->data))
404     {
405       g_output_stream_write_async (data->ostream,
406                                    data->data + data->pos,
407                                    strlen (data->data) - data->pos,
408                                    0,
409                                    NULL,
410                                    written_cb,
411                                    data);
412     }
413   else
414     {
415       g_assert_false (g_output_stream_is_closed (data->ostream));
416       g_output_stream_close_async (data->ostream, 0, NULL, oclosed_cb, data);
417     }
418 }
419
420 static void
421 opending_cb (GObject      *source,
422              GAsyncResult *res,
423              gpointer      user_data)
424 {
425   CreateDeleteData *data = user_data;
426   GError *error;
427
428   error = NULL;
429   g_output_stream_write_finish (data->ostream, res, &error);
430   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING);
431   g_error_free (error);
432 }
433
434 static void
435 created_cb (GObject      *source,
436             GAsyncResult *res,
437             gpointer      user_data)
438 {
439   GFileOutputStream *base;
440   CreateDeleteData *data = user_data;
441   GError *error;
442
443   error = NULL;
444   base = g_file_create_finish (G_FILE (source), res, &error);
445   g_assert_no_error (error);
446   g_assert_true (g_file_query_exists (data->file, NULL));
447
448   if (data->buffersize == 0)
449     data->ostream = G_OUTPUT_STREAM (g_object_ref (base));
450   else
451     data->ostream = g_buffered_output_stream_new_sized (G_OUTPUT_STREAM (base), data->buffersize);
452   g_object_unref (base);
453
454   g_output_stream_write_async (data->ostream,
455                                data->data,
456                                strlen (data->data),
457                                0,
458                                NULL,
459                                written_cb,
460                                data);
461   /* check that we get a pending error */
462   g_output_stream_write_async (data->ostream,
463                                data->data,
464                                strlen (data->data),
465                                0,
466                                NULL,
467                                opending_cb,
468                                data);
469 }
470
471 static void
472 stop_timeout (gpointer user_data)
473 {
474   CreateDeleteData *data = user_data;
475
476   data->timed_out = TRUE;
477   g_main_context_wakeup (data->context);
478 }
479
480 /*
481  * This test does a fully async create-write-read-delete.
482  * Callbackistan.
483  */
484 static void
485 test_create_delete (gconstpointer d)
486 {
487   GError *error;
488   CreateDeleteData *data;
489   GFileIOStream *iostream;
490
491   data = g_new0 (CreateDeleteData, 1);
492
493   data->buffersize = GPOINTER_TO_INT (d);
494   data->data = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ0123456789";
495   data->pos = 0;
496
497   data->file = g_file_new_tmp ("g_file_create_delete_XXXXXX",
498                                &iostream, NULL);
499   g_assert_nonnull (data->file);
500   g_object_unref (iostream);
501
502   data->monitor_path = g_file_get_path (data->file);
503   remove (data->monitor_path);
504
505   g_assert_false (g_file_query_exists (data->file, NULL));
506
507   error = NULL;
508   data->monitor = g_file_monitor_file (data->file, 0, NULL, &error);
509   g_assert_no_error (error);
510
511   /* This test doesn't work with GPollFileMonitor, because it assumes
512    * that the monitor will notice a create immediately followed by a
513    * delete, rather than coalescing them into nothing.
514    */
515   /* This test also doesn't work with GKqueueFileMonitor because of
516    * the same reason. Kqueue is able to return a kevent when a file is
517    * created or deleted in a directory. However, the kernel doesn't tell
518    * the program file names, so GKqueueFileMonitor has to calculate the
519    * difference itself. This is usually too slow for rapid file creation
520    * and deletion tests.
521    */
522   if (strcmp (G_OBJECT_TYPE_NAME (data->monitor), "GPollFileMonitor") == 0 ||
523       strcmp (G_OBJECT_TYPE_NAME (data->monitor), "GKqueueFileMonitor") == 0)
524     {
525       g_test_skip ("skipping test for this GFileMonitor implementation");
526       goto skip;
527     }
528
529   g_file_monitor_set_rate_limit (data->monitor, 100);
530
531   g_signal_connect (data->monitor, "changed", G_CALLBACK (monitor_changed), data);
532
533   /* Use the global default main context */
534   data->context = NULL;
535   data->timeout = g_timeout_add_seconds_once (10, stop_timeout, data);
536
537   g_file_create_async (data->file, 0, 0, NULL, created_cb, data);
538
539   while (!data->timed_out &&
540          (data->monitor_created == 0 ||
541           data->monitor_deleted == 0 ||
542           data->monitor_changed == 0 ||
543           !data->file_deleted))
544     g_main_context_iteration (data->context, TRUE);
545
546   g_source_remove (data->timeout);
547
548   g_assert_false (data->timed_out);
549   g_assert_true (data->file_deleted);
550   g_assert_cmpint (data->monitor_created, ==, 1);
551   g_assert_cmpint (data->monitor_deleted, ==, 1);
552   g_assert_cmpint (data->monitor_changed, >, 0);
553
554   g_assert_false (g_file_monitor_is_cancelled (data->monitor));
555   g_file_monitor_cancel (data->monitor);
556   g_assert_true (g_file_monitor_is_cancelled (data->monitor));
557
558   g_clear_pointer (&data->context, g_main_context_unref);
559   g_object_unref (data->ostream);
560   g_object_unref (data->istream);
561
562  skip:
563   g_object_unref (data->monitor);
564   g_object_unref (data->file);
565   g_free (data->monitor_path);
566   g_free (data->buffer);
567   g_free (data);
568 }
569
570 static const gchar *original_data =
571     "/**\n"
572     " * g_file_replace_contents_async:\n"
573     "**/\n";
574
575 static const gchar *replace_data =
576     "/**\n"
577     " * g_file_replace_contents_async:\n"
578     " * @file: input #GFile.\n"
579     " * @contents: string of contents to replace the file with.\n"
580     " * @length: the length of @contents in bytes.\n"
581     " * @etag: (nullable): a new <link linkend=\"gfile-etag\">entity tag</link> for the @file, or %NULL\n"
582     " * @make_backup: %TRUE if a backup should be created.\n"
583     " * @flags: a set of #GFileCreateFlags.\n"
584     " * @cancellable: optional #GCancellable object, %NULL to ignore.\n"
585     " * @callback: a #GAsyncReadyCallback to call when the request is satisfied\n"
586     " * @user_data: the data to pass to callback function\n"
587     " * \n"
588     " * Starts an asynchronous replacement of @file with the given \n"
589     " * @contents of @length bytes. @etag will replace the document's\n"
590     " * current entity tag.\n"
591     " * \n"
592     " * When this operation has completed, @callback will be called with\n"
593     " * @user_user data, and the operation can be finalized with \n"
594     " * g_file_replace_contents_finish().\n"
595     " * \n"
596     " * If @cancellable is not %NULL, then the operation can be cancelled by\n"
597     " * triggering the cancellable object from another thread. If the operation\n"
598     " * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. \n"
599     " * \n"
600     " * If @make_backup is %TRUE, this function will attempt to \n"
601     " * make a backup of @file.\n"
602     " **/\n";
603
604 typedef struct
605 {
606   GFile *file;
607   const gchar *data;
608   GMainLoop *loop;
609   gboolean again;
610 } ReplaceLoadData;
611
612 static void replaced_cb (GObject      *source,
613                          GAsyncResult *res,
614                          gpointer      user_data);
615
616 static void
617 loaded_cb (GObject      *source,
618            GAsyncResult *res,
619            gpointer      user_data)
620 {
621   ReplaceLoadData *data = user_data;
622   gboolean ret;
623   GError *error;
624   gchar *contents;
625   gsize length;
626
627   error = NULL;
628   ret = g_file_load_contents_finish (data->file, res, &contents, &length, NULL, &error);
629   g_assert_true (ret);
630   g_assert_no_error (error);
631   g_assert_cmpint (length, ==, strlen (data->data));
632   g_assert_cmpstr (contents, ==, data->data);
633
634   g_free (contents);
635
636   if (data->again)
637     {
638       data->again = FALSE;
639       data->data = "pi pa po";
640
641       g_file_replace_contents_async (data->file,
642                                      data->data,
643                                      strlen (data->data),
644                                      NULL,
645                                      FALSE,
646                                      0,
647                                      NULL,
648                                      replaced_cb,
649                                      data);
650     }
651   else
652     {
653        error = NULL;
654        ret = g_file_delete (data->file, NULL, &error);
655        g_assert_no_error (error);
656        g_assert_true (ret);
657        g_assert_false (g_file_query_exists (data->file, NULL));
658
659        g_main_loop_quit (data->loop);
660     }
661 }
662
663 static void
664 replaced_cb (GObject      *source,
665              GAsyncResult *res,
666              gpointer      user_data)
667 {
668   ReplaceLoadData *data = user_data;
669   GError *error;
670
671   error = NULL;
672   g_file_replace_contents_finish (data->file, res, NULL, &error);
673   g_assert_no_error (error);
674
675   g_file_load_contents_async (data->file, NULL, loaded_cb, data);
676 }
677
678 static void
679 test_replace_load (void)
680 {
681   ReplaceLoadData *data;
682   const gchar *path;
683   GFileIOStream *iostream;
684
685   data = g_new0 (ReplaceLoadData, 1);
686   data->again = TRUE;
687   data->data = replace_data;
688
689   data->file = g_file_new_tmp ("g_file_replace_load_XXXXXX",
690                                &iostream, NULL);
691   g_assert_nonnull (data->file);
692   g_object_unref (iostream);
693
694   path = g_file_peek_path (data->file);
695   remove (path);
696
697   g_assert_false (g_file_query_exists (data->file, NULL));
698
699   data->loop = g_main_loop_new (NULL, FALSE);
700
701   g_file_replace_contents_async (data->file,
702                                  data->data,
703                                  strlen (data->data),
704                                  NULL,
705                                  FALSE,
706                                  0,
707                                  NULL,
708                                  replaced_cb,
709                                  data);
710
711   g_main_loop_run (data->loop);
712
713   g_main_loop_unref (data->loop);
714   g_object_unref (data->file);
715   g_free (data);
716 }
717
718 static void
719 test_replace_cancel (void)
720 {
721   GFile *tmpdir, *file;
722   GFileOutputStream *ostream;
723   GFileEnumerator *fenum;
724   GFileInfo *info;
725   GCancellable *cancellable;
726   gchar *path;
727   gchar *contents;
728   gsize nwrote, length;
729   guint count;
730   GError *error = NULL;
731
732   g_test_bug ("https://bugzilla.gnome.org/629301");
733
734   path = g_dir_make_tmp ("g_file_replace_cancel_XXXXXX", &error);
735   g_assert_no_error (error);
736   tmpdir = g_file_new_for_path (path);
737   g_free (path);
738
739   file = g_file_get_child (tmpdir, "file");
740   g_file_replace_contents (file,
741                            original_data,
742                            strlen (original_data),
743                            NULL, FALSE, 0, NULL,
744                            NULL, &error);
745   g_assert_no_error (error);
746
747   ostream = g_file_replace (file, NULL, TRUE, 0, NULL, &error);
748   g_assert_no_error (error);
749
750   g_output_stream_write_all (G_OUTPUT_STREAM (ostream),
751                              replace_data, strlen (replace_data),
752                              &nwrote, NULL, &error);
753   g_assert_no_error (error);
754   g_assert_cmpint (nwrote, ==, strlen (replace_data));
755
756   /* At this point there should be two files; the original and the
757    * temporary.
758    */
759   fenum = g_file_enumerate_children (tmpdir, NULL, 0, NULL, &error);
760   g_assert_no_error (error);
761
762   info = g_file_enumerator_next_file (fenum, NULL, &error);
763   g_assert_no_error (error);
764   g_assert_nonnull (info);
765   g_object_unref (info);
766   info = g_file_enumerator_next_file (fenum, NULL, &error);
767   g_assert_no_error (error);
768   g_assert_nonnull (info);
769   g_object_unref (info);
770
771   g_file_enumerator_close (fenum, NULL, &error);
772   g_assert_no_error (error);
773   g_object_unref (fenum);
774
775   /* Also test the g_file_enumerator_iterate() API */
776   fenum = g_file_enumerate_children (tmpdir, NULL, 0, NULL, &error);
777   g_assert_no_error (error);
778   count = 0;
779
780   while (TRUE)
781     {
782       gboolean ret = g_file_enumerator_iterate (fenum, &info, NULL, NULL, &error);
783       g_assert_true (ret);
784       g_assert_no_error (error);
785       if (!info)
786         break;
787       count++;
788     }
789   g_assert_cmpint (count, ==, 2);
790
791   g_file_enumerator_close (fenum, NULL, &error);
792   g_assert_no_error (error);
793   g_object_unref (fenum);
794
795   /* Now test just getting child from the g_file_enumerator_iterate() API */
796   fenum = g_file_enumerate_children (tmpdir, "standard::name", 0, NULL, &error);
797   g_assert_no_error (error);
798   count = 0;
799
800   while (TRUE)
801     {
802       GFile *child;
803       gboolean ret = g_file_enumerator_iterate (fenum, NULL, &child, NULL, &error);
804
805       g_assert_true (ret);
806       g_assert_no_error (error);
807
808       if (!child)
809         break;
810
811       g_assert_true (G_IS_FILE (child));
812       count++;
813     }
814   g_assert_cmpint (count, ==, 2);
815
816   g_file_enumerator_close (fenum, NULL, &error);
817   g_assert_no_error (error);
818   g_object_unref (fenum);
819
820   /* Make sure the temporary gets deleted even if we cancel. */
821   cancellable = g_cancellable_new ();
822   g_cancellable_cancel (cancellable);
823   g_output_stream_close (G_OUTPUT_STREAM (ostream), cancellable, &error);
824   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
825   g_clear_error (&error);
826
827   g_object_unref (cancellable);
828   g_object_unref (ostream);
829
830   /* Make sure that file contents wasn't actually replaced. */
831   g_file_load_contents (file,
832                         NULL,
833                         &contents,
834                         &length,
835                         NULL,
836                         &error);
837   g_assert_no_error (error);
838   g_assert_cmpstr (contents, ==, original_data);
839   g_free (contents);
840
841   g_file_delete (file, NULL, &error);
842   g_assert_no_error (error);
843   g_object_unref (file);
844
845   /* This will only succeed if the temp file was deleted. */
846   g_file_delete (tmpdir, NULL, &error);
847   g_assert_no_error (error);
848   g_object_unref (tmpdir);
849 }
850
851 static void
852 test_replace_symlink (void)
853 {
854 #ifdef G_OS_UNIX
855   gchar *tmpdir_path = NULL;
856   GFile *tmpdir = NULL, *source_file = NULL, *target_file = NULL;
857   GFileOutputStream *stream = NULL;
858   const gchar *new_contents = "this is a test message which should be written to source and not target";
859   gsize n_written;
860   GFileEnumerator *enumerator = NULL;
861   GFileInfo *info = NULL;
862   gchar *contents = NULL;
863   gsize length = 0;
864   GError *local_error = NULL;
865
866   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2325");
867   g_test_summary ("Test that G_FILE_CREATE_REPLACE_DESTINATION doesn’t follow symlinks");
868
869   /* Create a fresh, empty working directory. */
870   tmpdir_path = g_dir_make_tmp ("g_file_replace_symlink_XXXXXX", &local_error);
871   g_assert_no_error (local_error);
872   tmpdir = g_file_new_for_path (tmpdir_path);
873
874   g_test_message ("Using temporary directory %s", tmpdir_path);
875   g_free (tmpdir_path);
876
877   source_file = g_file_get_child (tmpdir, "source");
878   g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
879                          "*assertion*symlink_value*failed*");
880   g_assert_false (g_file_make_symbolic_link (source_file, NULL, NULL, &local_error));
881   g_assert_no_error (local_error);
882   g_test_assert_expected_messages ();
883
884   g_assert_false (g_file_make_symbolic_link (source_file, "", NULL, &local_error));
885   g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
886   g_clear_object (&source_file);
887   g_clear_error (&local_error);
888
889   /* Create symlink `source` which points to `target`. */
890   source_file = g_file_get_child (tmpdir, "source");
891   target_file = g_file_get_child (tmpdir, "target");
892   g_file_make_symbolic_link (source_file, "target", NULL, &local_error);
893   g_assert_no_error (local_error);
894
895   /* Ensure that `target` doesn’t exist */
896   g_assert_false (g_file_query_exists (target_file, NULL));
897
898   /* Replace the `source` symlink with a regular file using
899    * %G_FILE_CREATE_REPLACE_DESTINATION, which should replace it *without*
900    * following the symlink */
901   stream = g_file_replace (source_file, NULL, FALSE  /* no backup */,
902                            G_FILE_CREATE_REPLACE_DESTINATION, NULL, &local_error);
903   g_assert_no_error (local_error);
904
905   g_output_stream_write_all (G_OUTPUT_STREAM (stream), new_contents, strlen (new_contents),
906                              &n_written, NULL, &local_error);
907   g_assert_no_error (local_error);
908   g_assert_cmpint (n_written, ==, strlen (new_contents));
909
910   g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &local_error);
911   g_assert_no_error (local_error);
912
913   g_clear_object (&stream);
914
915   /* At this point, there should still only be one file: `source`. It should
916    * now be a regular file. `target` should not exist. */
917   enumerator = g_file_enumerate_children (tmpdir,
918                                           G_FILE_ATTRIBUTE_STANDARD_NAME ","
919                                           G_FILE_ATTRIBUTE_STANDARD_TYPE,
920                                           G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error);
921   g_assert_no_error (local_error);
922
923   info = g_file_enumerator_next_file (enumerator, NULL, &local_error);
924   g_assert_no_error (local_error);
925   g_assert_nonnull (info);
926
927   g_assert_cmpstr (g_file_info_get_name (info), ==, "source");
928   g_assert_cmpint (g_file_info_get_file_type (info), ==, G_FILE_TYPE_REGULAR);
929
930   g_clear_object (&info);
931
932   info = g_file_enumerator_next_file (enumerator, NULL, &local_error);
933   g_assert_no_error (local_error);
934   g_assert_null (info);
935
936   g_file_enumerator_close (enumerator, NULL, &local_error);
937   g_assert_no_error (local_error);
938   g_clear_object (&enumerator);
939
940   /* Double-check that `target` doesn’t exist */
941   g_assert_false (g_file_query_exists (target_file, NULL));
942
943   /* Check the content of `source`. */
944   g_file_load_contents (source_file,
945                         NULL,
946                         &contents,
947                         &length,
948                         NULL,
949                         &local_error);
950   g_assert_no_error (local_error);
951   g_assert_cmpstr (contents, ==, new_contents);
952   g_assert_cmpuint (length, ==, strlen (new_contents));
953   g_free (contents);
954
955   /* Tidy up. */
956   g_file_delete (source_file, NULL, &local_error);
957   g_assert_no_error (local_error);
958
959   g_file_delete (tmpdir, NULL, &local_error);
960   g_assert_no_error (local_error);
961
962   g_clear_object (&target_file);
963   g_clear_object (&source_file);
964   g_clear_object (&tmpdir);
965 #else  /* if !G_OS_UNIX */
966   g_test_skip ("Symlink replacement tests can only be run on Unix")
967 #endif
968 }
969
970 static void
971 test_replace_symlink_using_etag (void)
972 {
973 #ifdef G_OS_UNIX
974   gchar *tmpdir_path = NULL;
975   GFile *tmpdir = NULL, *source_file = NULL, *target_file = NULL;
976   GFileOutputStream *stream = NULL;
977   const gchar *old_contents = "this is a test message which should be written to target and then overwritten";
978   gchar *old_etag = NULL;
979   const gchar *new_contents = "this is an updated message";
980   gsize n_written;
981   gchar *contents = NULL;
982   gsize length = 0;
983   GFileInfo *info = NULL;
984   GError *local_error = NULL;
985
986   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2417");
987   g_test_summary ("Test that ETag checks work when replacing a file through a symlink");
988
989   /* Create a fresh, empty working directory. */
990   tmpdir_path = g_dir_make_tmp ("g_file_replace_symlink_using_etag_XXXXXX", &local_error);
991   g_assert_no_error (local_error);
992   tmpdir = g_file_new_for_path (tmpdir_path);
993
994   g_test_message ("Using temporary directory %s", tmpdir_path);
995   g_free (tmpdir_path);
996
997   /* Create symlink `source` which points to `target`. */
998   source_file = g_file_get_child (tmpdir, "source");
999   target_file = g_file_get_child (tmpdir, "target");
1000   g_file_make_symbolic_link (source_file, "target", NULL, &local_error);
1001   g_assert_no_error (local_error);
1002
1003   /* Sleep for at least 1s to ensure the mtimes of `source` and `target` differ,
1004    * as that’s what _g_local_file_info_create_etag() uses to create the ETag,
1005    * and one failure mode we’re testing for is that the ETags of `source` and
1006    * `target` are conflated. */
1007   sleep (1);
1008
1009   /* Create `target` with some arbitrary content. */
1010   stream = g_file_create (target_file, G_FILE_CREATE_NONE, NULL, &local_error);
1011   g_assert_no_error (local_error);
1012   g_output_stream_write_all (G_OUTPUT_STREAM (stream), old_contents, strlen (old_contents),
1013                              &n_written, NULL, &local_error);
1014   g_assert_no_error (local_error);
1015   g_assert_cmpint (n_written, ==, strlen (old_contents));
1016
1017   g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &local_error);
1018   g_assert_no_error (local_error);
1019
1020   old_etag = g_file_output_stream_get_etag (stream);
1021   g_assert_nonnull (old_etag);
1022   g_assert_cmpstr (old_etag, !=, "");
1023
1024   g_clear_object (&stream);
1025
1026   /* Sleep again to ensure the ETag changes again. */
1027   sleep (1);
1028
1029   /* Write out a new copy of the `target`, checking its ETag first. This should
1030    * replace `target` by following the symlink. */
1031   stream = g_file_replace (source_file, old_etag, FALSE  /* no backup */,
1032                            G_FILE_CREATE_NONE, NULL, &local_error);
1033   g_assert_no_error (local_error);
1034
1035   g_output_stream_write_all (G_OUTPUT_STREAM (stream), new_contents, strlen (new_contents),
1036                              &n_written, NULL, &local_error);
1037   g_assert_no_error (local_error);
1038   g_assert_cmpint (n_written, ==, strlen (new_contents));
1039
1040   g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &local_error);
1041   g_assert_no_error (local_error);
1042
1043   g_clear_object (&stream);
1044
1045   /* At this point, there should be a regular file, `target`, containing
1046    * @new_contents; and a symlink `source` which points to `target`. */
1047   g_assert_cmpint (g_file_query_file_type (source_file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL), ==, G_FILE_TYPE_SYMBOLIC_LINK);
1048   g_assert_cmpint (g_file_query_file_type (target_file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL), ==, G_FILE_TYPE_REGULAR);
1049
1050   /* Check the content of `target`. */
1051   g_file_load_contents (target_file,
1052                         NULL,
1053                         &contents,
1054                         &length,
1055                         NULL,
1056                         &local_error);
1057   g_assert_no_error (local_error);
1058   g_assert_cmpstr (contents, ==, new_contents);
1059   g_assert_cmpuint (length, ==, strlen (new_contents));
1060   g_free (contents);
1061
1062   /* And check its ETag value has changed. */
1063   info = g_file_query_info (target_file, G_FILE_ATTRIBUTE_ETAG_VALUE,
1064                             G_FILE_QUERY_INFO_NONE, NULL, &local_error);
1065   g_assert_no_error (local_error);
1066   g_assert_cmpstr (g_file_info_get_etag (info), !=, old_etag);
1067
1068   g_clear_object (&info);
1069   g_free (old_etag);
1070
1071   /* Tidy up. */
1072   g_file_delete (target_file, NULL, &local_error);
1073   g_assert_no_error (local_error);
1074
1075   g_file_delete (source_file, NULL, &local_error);
1076   g_assert_no_error (local_error);
1077
1078   g_file_delete (tmpdir, NULL, &local_error);
1079   g_assert_no_error (local_error);
1080
1081   g_clear_object (&target_file);
1082   g_clear_object (&source_file);
1083   g_clear_object (&tmpdir);
1084 #else  /* if !G_OS_UNIX */
1085   g_test_skip ("Symlink replacement tests can only be run on Unix")
1086 #endif
1087 }
1088
1089 /* FIXME: These tests have only been checked on Linux. Most of them are probably
1090  * applicable on Windows, too, but that has not been tested yet.
1091  * See https://gitlab.gnome.org/GNOME/glib/-/issues/2325 */
1092 #ifdef __linux__
1093
1094 /* Different kinds of file which create_test_file() can create. */
1095 typedef enum
1096 {
1097   FILE_TEST_SETUP_TYPE_NONEXISTENT,
1098   FILE_TEST_SETUP_TYPE_REGULAR_EMPTY,
1099   FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY,
1100   FILE_TEST_SETUP_TYPE_DIRECTORY,
1101   FILE_TEST_SETUP_TYPE_SOCKET,
1102   FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING,
1103   FILE_TEST_SETUP_TYPE_SYMLINK_VALID,
1104 } FileTestSetupType;
1105
1106 /* Create file `tmpdir/basename`, of type @setup_type, and chmod it to
1107  * @setup_mode. Return the #GFile representing it. Abort on any errors. */
1108 static GFile *
1109 create_test_file (GFile             *tmpdir,
1110                   const gchar       *basename,
1111                   FileTestSetupType  setup_type,
1112                   guint              setup_mode)
1113 {
1114   GFile *test_file = g_file_get_child (tmpdir, basename);
1115   gchar *target_basename = g_strdup_printf ("%s-target", basename);  /* for symlinks */
1116   GFile *target_file = g_file_get_child (tmpdir, target_basename);
1117   GError *local_error = NULL;
1118
1119   switch (setup_type)
1120     {
1121     case FILE_TEST_SETUP_TYPE_NONEXISTENT:
1122       /* Nothing to do here. */
1123       g_assert (setup_mode == 0);
1124       break;
1125     case FILE_TEST_SETUP_TYPE_REGULAR_EMPTY:
1126     case FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY:
1127         {
1128           gchar *contents = NULL;
1129
1130           if (setup_type == FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY)
1131             contents = g_strdup_printf ("this is some test content in %s", basename);
1132           else
1133             contents = g_strdup ("");
1134
1135           g_file_set_contents (g_file_peek_path (test_file), contents, -1, &local_error);
1136           g_assert_no_error (local_error);
1137
1138           g_file_set_attribute_uint32 (test_file, G_FILE_ATTRIBUTE_UNIX_MODE,
1139                                        setup_mode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
1140                                        NULL, &local_error);
1141           g_assert_no_error (local_error);
1142
1143           g_free (contents);
1144           break;
1145         }
1146     case FILE_TEST_SETUP_TYPE_DIRECTORY:
1147       g_assert (setup_mode == 0);
1148
1149       g_file_make_directory (test_file, NULL, &local_error);
1150       g_assert_no_error (local_error);
1151       break;
1152     case FILE_TEST_SETUP_TYPE_SOCKET:
1153       g_assert_no_errno (mknod (g_file_peek_path (test_file), S_IFSOCK | setup_mode, 0));
1154       break;
1155     case FILE_TEST_SETUP_TYPE_SYMLINK_VALID:
1156       g_file_set_contents (g_file_peek_path (target_file), "target file", -1, &local_error);
1157       g_assert_no_error (local_error);
1158       G_GNUC_FALLTHROUGH;
1159     case FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING:
1160       /* Permissions on a symlink are not used by the kernel, so are only
1161        * applicable if the symlink is valid (and are applied to the target) */
1162       g_assert (setup_type != FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING || setup_mode == 0);
1163
1164       g_file_make_symbolic_link (test_file, target_basename, NULL, &local_error);
1165       g_assert_no_error (local_error);
1166
1167       if (setup_type == FILE_TEST_SETUP_TYPE_SYMLINK_VALID)
1168         {
1169           g_file_set_attribute_uint32 (test_file, G_FILE_ATTRIBUTE_UNIX_MODE,
1170                                        setup_mode, G_FILE_QUERY_INFO_NONE,
1171                                        NULL, &local_error);
1172           g_assert_no_error (local_error);
1173         }
1174
1175       if (setup_type == FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING)
1176         {
1177           /* Ensure that the target doesn’t exist */
1178           g_assert_false (g_file_query_exists (target_file, NULL));
1179         }
1180       break;
1181     default:
1182       g_assert_not_reached ();
1183     }
1184
1185   g_clear_object (&target_file);
1186   g_free (target_basename);
1187
1188   return g_steal_pointer (&test_file);
1189 }
1190
1191 /* Check that @test_file is of the @expected_type, has the @expected_mode, and
1192  * (if it’s a regular file) has the @expected_contents or (if it’s a symlink)
1193  * has the symlink target given by @expected_contents.
1194  *
1195  * @test_file must point to the file `tmpdir/basename`.
1196  *
1197  * Aborts on any errors or mismatches against the expectations.
1198  */
1199 static void
1200 check_test_file (GFile             *test_file,
1201                  GFile             *tmpdir,
1202                  const gchar       *basename,
1203                  FileTestSetupType  expected_type,
1204                  guint              expected_mode,
1205                  const gchar       *expected_contents)
1206 {
1207   gchar *target_basename = g_strdup_printf ("%s-target", basename);  /* for symlinks */
1208   GFile *target_file = g_file_get_child (tmpdir, target_basename);
1209   GFileType test_file_type = g_file_query_file_type (test_file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL);
1210   GFileInfo *info = NULL;
1211   GError *local_error = NULL;
1212
1213   switch (expected_type)
1214     {
1215     case FILE_TEST_SETUP_TYPE_NONEXISTENT:
1216       g_assert (expected_mode == 0);
1217       g_assert (expected_contents == NULL);
1218
1219       g_assert_false (g_file_query_exists (test_file, NULL));
1220       g_assert_cmpint (test_file_type, ==, G_FILE_TYPE_UNKNOWN);
1221       break;
1222     case FILE_TEST_SETUP_TYPE_REGULAR_EMPTY:
1223     case FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY:
1224       g_assert (expected_type != FILE_TEST_SETUP_TYPE_REGULAR_EMPTY || expected_contents == NULL);
1225       g_assert (expected_type != FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY || expected_contents != NULL);
1226
1227       g_assert_cmpint (test_file_type, ==, G_FILE_TYPE_REGULAR);
1228
1229       info = g_file_query_info (test_file,
1230                                 G_FILE_ATTRIBUTE_STANDARD_SIZE ","
1231                                 G_FILE_ATTRIBUTE_UNIX_MODE,
1232                                 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error);
1233       g_assert_no_error (local_error);
1234
1235       if (expected_type == FILE_TEST_SETUP_TYPE_REGULAR_EMPTY)
1236         g_assert_cmpint (g_file_info_get_size (info), ==, 0);
1237       else
1238         g_assert_cmpint (g_file_info_get_size (info), >, 0);
1239
1240       if (expected_contents != NULL)
1241         {
1242           gchar *contents = NULL;
1243           gsize length = 0;
1244
1245           g_file_get_contents (g_file_peek_path (test_file), &contents, &length, &local_error);
1246           g_assert_no_error (local_error);
1247
1248           g_assert_cmpstr (contents, ==, expected_contents);
1249           g_free (contents);
1250         }
1251
1252       g_assert_cmpuint (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE) & 0777, ==, expected_mode);
1253
1254       break;
1255     case FILE_TEST_SETUP_TYPE_DIRECTORY:
1256       g_assert (expected_mode == 0);
1257       g_assert (expected_contents == NULL);
1258
1259       g_assert_cmpint (test_file_type, ==, G_FILE_TYPE_DIRECTORY);
1260       break;
1261     case FILE_TEST_SETUP_TYPE_SOCKET:
1262       g_assert (expected_contents == NULL);
1263
1264       g_assert_cmpint (test_file_type, ==, G_FILE_TYPE_SPECIAL);
1265
1266       info = g_file_query_info (test_file,
1267                                 G_FILE_ATTRIBUTE_UNIX_MODE,
1268                                 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error);
1269       g_assert_no_error (local_error);
1270
1271       g_assert_cmpuint (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE) & 0777, ==, expected_mode);
1272       break;
1273     case FILE_TEST_SETUP_TYPE_SYMLINK_VALID:
1274     case FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING:
1275         {
1276           GFile *symlink_target_file = NULL;
1277
1278           /* Permissions on a symlink are not used by the kernel, so are only
1279            * applicable if the symlink is valid (and are applied to the target) */
1280           g_assert (expected_type != FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING || expected_mode == 0);
1281           g_assert (expected_contents != NULL);
1282
1283           g_assert_cmpint (test_file_type, ==, G_FILE_TYPE_SYMBOLIC_LINK);
1284
1285           info = g_file_query_info (test_file,
1286                                     G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
1287                                     G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error);
1288           g_assert_no_error (local_error);
1289
1290           g_assert_cmpstr (g_file_info_get_symlink_target (info), ==, expected_contents);
1291
1292           symlink_target_file = g_file_get_child (tmpdir, g_file_info_get_symlink_target (info));
1293           if (expected_type == FILE_TEST_SETUP_TYPE_SYMLINK_VALID)
1294             g_assert_true (g_file_query_exists (symlink_target_file, NULL));
1295           else
1296             g_assert_false (g_file_query_exists (symlink_target_file, NULL));
1297
1298           if (expected_type == FILE_TEST_SETUP_TYPE_SYMLINK_VALID)
1299             {
1300               GFileInfo *target_info = NULL;
1301
1302               /* Need to re-query the info so we follow symlinks */
1303               target_info = g_file_query_info (test_file,
1304                                                G_FILE_ATTRIBUTE_UNIX_MODE,
1305                                                G_FILE_QUERY_INFO_NONE, NULL, &local_error);
1306               g_assert_no_error (local_error);
1307
1308               g_assert_cmpuint (g_file_info_get_attribute_uint32 (target_info, G_FILE_ATTRIBUTE_UNIX_MODE) & 0777, ==, expected_mode);
1309
1310               g_clear_object (&target_info);
1311             }
1312
1313           g_clear_object (&symlink_target_file);
1314           break;
1315         }
1316     default:
1317       g_assert_not_reached ();
1318     }
1319
1320   g_clear_object (&info);
1321   g_clear_object (&target_file);
1322   g_free (target_basename);
1323 }
1324
1325 #endif  /* __linux__ */
1326
1327 #ifdef __linux__
1328 /*
1329  * check_cap_dac_override:
1330  * @tmpdir: A temporary directory in which we can create and delete files
1331  *
1332  * Check whether the current process can bypass DAC permissions.
1333  *
1334  * Traditionally, "privileged" processes (those with effective uid 0)
1335  * could do this (and bypass many other checks), and "unprivileged"
1336  * processes could not.
1337  *
1338  * In Linux, the special powers of euid 0 are divided into many
1339  * capabilities: see `capabilities(7)`. The one we are interested in
1340  * here is `CAP_DAC_OVERRIDE`.
1341  *
1342  * We do this generically instead of actually looking at the capability
1343  * bits, so that the right thing will happen on non-Linux Unix
1344  * implementations, in particular if they have something equivalent to
1345  * but not identical to Linux permissions.
1346  *
1347  * Returns: %TRUE if we have Linux `CAP_DAC_OVERRIDE` or equivalent
1348  *  privileges
1349  */
1350 static gboolean
1351 check_cap_dac_override (const char *tmpdir)
1352 {
1353   gchar *dac_denies_write;
1354   gchar *inside;
1355   gboolean have_cap;
1356
1357   dac_denies_write = g_build_filename (tmpdir, "dac-denies-write", NULL);
1358   inside = g_build_filename (dac_denies_write, "inside", NULL);
1359
1360   g_assert_no_errno (mkdir (dac_denies_write, S_IRWXU));
1361   g_assert_no_errno (chmod (dac_denies_write, 0));
1362
1363   if (mkdir (inside, S_IRWXU) == 0)
1364     {
1365       g_test_message ("Looks like we have CAP_DAC_OVERRIDE or equivalent");
1366       g_assert_no_errno (rmdir (inside));
1367       have_cap = TRUE;
1368     }
1369   else
1370     {
1371       int saved_errno = errno;
1372
1373       g_test_message ("We do not have CAP_DAC_OVERRIDE or equivalent");
1374       g_assert_cmpint (saved_errno, ==, EACCES);
1375       have_cap = FALSE;
1376     }
1377
1378   g_assert_no_errno (chmod (dac_denies_write, S_IRWXU));
1379   g_assert_no_errno (rmdir (dac_denies_write));
1380   g_free (dac_denies_write);
1381   g_free (inside);
1382   return have_cap;
1383 }
1384 #endif
1385
1386 /* A big test for g_file_replace() and g_file_replace_readwrite(). The
1387  * @test_data is a boolean: %TRUE to test g_file_replace_readwrite(), %FALSE to
1388  * test g_file_replace(). The test setup and checks are identical for both
1389  * functions; in the case of testing g_file_replace_readwrite(), only the output
1390  * stream side of the returned #GIOStream is tested. i.e. We test the write
1391  * behaviour of both functions is identical.
1392  *
1393  * This is intended to test all static behaviour of the function: for each test
1394  * scenario, a temporary directory is set up with a source file (and maybe some
1395  * other files) in a set configuration, g_file_replace{,_readwrite}() is called,
1396  * and the final state of the directory is checked.
1397  *
1398  * This test does not check dynamic behaviour or race conditions. For example,
1399  * it does not test what happens if the source file is deleted from another
1400  * process half-way through a call to g_file_replace().
1401  */
1402 static void
1403 test_replace (gconstpointer test_data)
1404 {
1405 #ifdef __linux__
1406   gboolean read_write = GPOINTER_TO_UINT (test_data);
1407   const gchar *new_contents = "this is a new test message which should be written to source";
1408   const gchar *original_source_contents = "this is some test content in source";
1409   const gchar *original_backup_contents = "this is some test content in source~";
1410   mode_t current_umask = umask (0);
1411   guint32 default_public_mode = 0666 & ~current_umask;
1412   guint32 default_private_mode = 0600;
1413
1414   const struct
1415     {
1416       /* Arguments to pass to g_file_replace(). */
1417       gboolean replace_make_backup;
1418       GFileCreateFlags replace_flags;
1419       const gchar *replace_etag;  /* (nullable) */
1420
1421       /* File system setup. */
1422       FileTestSetupType setup_source_type;
1423       guint setup_source_mode;
1424       FileTestSetupType setup_backup_type;
1425       guint setup_backup_mode;
1426       gboolean skip_if_cap_dac_override;
1427
1428       /* Expected results. */
1429       gboolean expected_success;
1430       GQuark expected_error_domain;
1431       gint expected_error_code;
1432
1433       /* Expected final file system state. */
1434       guint expected_n_files;
1435       FileTestSetupType expected_source_type;
1436       guint expected_source_mode;
1437       const gchar *expected_source_contents;  /* content for a regular file, or target for a symlink; NULL otherwise */
1438       FileTestSetupType expected_backup_type;
1439       guint expected_backup_mode;
1440       const gchar *expected_backup_contents;  /* content for a regular file, or target for a symlink; NULL otherwise */
1441     }
1442   tests[] =
1443     {
1444       /* replace_make_backup == FALSE, replace_flags == NONE, replace_etag == NULL,
1445        * all the different values of setup_source_type, mostly with a backup
1446        * file created to check it’s not modified */
1447       {
1448         FALSE, G_FILE_CREATE_NONE, NULL,
1449         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1450         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
1451         TRUE, 0, 0,
1452         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1453         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1454       },
1455       {
1456         FALSE, G_FILE_CREATE_NONE, NULL,
1457         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode,
1458         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1459         TRUE, 0, 0,
1460         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1461         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1462       },
1463       {
1464         FALSE, G_FILE_CREATE_NONE, NULL,
1465         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1466         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1467         TRUE, 0, 0,
1468         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1469         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1470       },
1471       {
1472         FALSE, G_FILE_CREATE_NONE, NULL,
1473         FILE_TEST_SETUP_TYPE_DIRECTORY, 0,
1474         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1475         FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
1476         2, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL,
1477         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1478       },
1479       {
1480         FALSE, G_FILE_CREATE_NONE, NULL,
1481         FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode,
1482         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1483         FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
1484         2, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, NULL,
1485         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1486       },
1487       {
1488         FALSE, G_FILE_CREATE_NONE, NULL,
1489         FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0,
1490         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1491         TRUE, 0, 0,
1492         3, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target",
1493         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1494       },
1495       {
1496         FALSE, G_FILE_CREATE_NONE, NULL,
1497         FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
1498         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1499         TRUE, 0, 0,
1500         3, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target",
1501         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1502       },
1503
1504       /* replace_etag set to an invalid value, with setup_source_type as a
1505        * regular non-empty file; replacement should fail */
1506       {
1507         FALSE, G_FILE_CREATE_NONE, "incorrect etag",
1508         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1509         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1510         FALSE, G_IO_ERROR, G_IO_ERROR_WRONG_ETAG,
1511         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1512         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1513       },
1514
1515       /* replace_make_backup == TRUE, replace_flags == NONE, replace_etag == NULL,
1516        * all the different values of setup_source_type, with a backup
1517        * file created to check it’s either replaced or the operation fails */
1518       {
1519         TRUE, G_FILE_CREATE_NONE, NULL,
1520         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1521         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
1522         TRUE, 0, 0,
1523         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1524         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1525       },
1526       {
1527         TRUE, G_FILE_CREATE_NONE, NULL,
1528         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode,
1529         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1530         TRUE, 0, 0,
1531         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1532         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, NULL,
1533       },
1534       {
1535         TRUE, G_FILE_CREATE_NONE, NULL,
1536         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1537         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1538         TRUE, 0, 0,
1539         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1540         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1541       },
1542       {
1543         TRUE, G_FILE_CREATE_NONE, NULL,
1544         FILE_TEST_SETUP_TYPE_DIRECTORY, 0,
1545         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1546         FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
1547         2, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL,
1548         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1549       },
1550       {
1551         TRUE, G_FILE_CREATE_NONE, NULL,
1552         FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode,
1553         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1554         FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
1555         2, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, NULL,
1556         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1557       },
1558       {
1559         TRUE, G_FILE_CREATE_NONE, NULL,
1560         FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0,
1561         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1562         TRUE, 0, 0,
1563         /* The final situation here is a bit odd; the backup file is a bit
1564          * pointless as the original source file was a dangling symlink.
1565          * Theoretically the backup file should be that symlink, pointing to
1566          * `source-target`, and hence no longer dangling, as that file has now
1567          * been created as the new source content, since REPLACE_DESTINATION was
1568          * not specified. However, the code instead creates an empty regular
1569          * file as the backup. FIXME: This seems acceptable for now, but not
1570          * entirely ideal and would be good to fix at some point. */
1571         3, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target",
1572         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, 0777 & ~current_umask, NULL,
1573       },
1574       {
1575         TRUE, G_FILE_CREATE_NONE, NULL,
1576         FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
1577         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1578         TRUE, 0, 0,
1579         /* FIXME: The permissions for the backup file are just the default umask,
1580          * but should probably be the same as the permissions for the source
1581          * file (`default_public_mode`). This probably arises from the fact that
1582          * symlinks don’t have permissions. */
1583         3, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target",
1584         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, 0777 & ~current_umask, "target file",
1585       },
1586
1587       /* replace_make_backup == TRUE, replace_flags == NONE, replace_etag == NULL,
1588        * setup_source_type is a regular file, with a backup file of every type
1589        * created to check it’s either replaced or the operation fails */
1590       {
1591         TRUE, G_FILE_CREATE_NONE, NULL,
1592         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1593         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
1594         TRUE, 0, 0,
1595         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1596         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1597       },
1598       {
1599         TRUE, G_FILE_CREATE_NONE, NULL,
1600         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1601         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, FALSE,
1602         TRUE, 0, 0,
1603         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1604         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1605       },
1606       {
1607         TRUE, G_FILE_CREATE_NONE, NULL,
1608         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1609         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1610         TRUE, 0, 0,
1611         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1612         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1613       },
1614       {
1615         TRUE, G_FILE_CREATE_NONE, NULL,
1616         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1617         FILE_TEST_SETUP_TYPE_DIRECTORY, 0, FALSE,
1618         FALSE, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP,
1619         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1620         FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL,
1621       },
1622       {
1623         TRUE, G_FILE_CREATE_NONE, NULL,
1624         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1625         FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, FALSE,
1626         TRUE, 0, 0,
1627         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1628         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1629       },
1630       {
1631         TRUE, G_FILE_CREATE_NONE, NULL,
1632         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1633         FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, FALSE,
1634         TRUE, 0, 0,
1635         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1636         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1637       },
1638       {
1639         TRUE, G_FILE_CREATE_NONE, NULL,
1640         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1641         FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, FALSE,
1642         TRUE, 0, 0,
1643         /* the third file is `source~-target`, the original target of the old
1644          * backup symlink */
1645         3, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1646         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1647       },
1648
1649       /* replace_make_backup == FALSE, replace_flags == REPLACE_DESTINATION,
1650        * replace_etag == NULL, all the different values of setup_source_type,
1651        * mostly with a backup file created to check it’s not modified */
1652       {
1653         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1654         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1655         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
1656         TRUE, 0, 0,
1657         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1658         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1659       },
1660       {
1661         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1662         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode,
1663         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1664         TRUE, 0, 0,
1665         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1666         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1667       },
1668       {
1669         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1670         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1671         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1672         TRUE, 0, 0,
1673         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1674         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1675       },
1676       {
1677         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1678         FILE_TEST_SETUP_TYPE_DIRECTORY, 0,
1679         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1680         FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
1681         2, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL,
1682         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1683       },
1684       {
1685         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1686         FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode,
1687         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1688         FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
1689         2, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, NULL,
1690         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1691       },
1692       {
1693         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1694         FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0,
1695         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1696         TRUE, 0, 0,
1697         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1698         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1699       },
1700       {
1701         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1702         FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
1703         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1704         TRUE, 0, 0,
1705         /* the third file is `source-target`, the original target of the old
1706          * source file */
1707         3, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1708         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1709       },
1710
1711       /* replace_flags == REPLACE_DESTINATION, replace_etag set to an invalid
1712        * value, with setup_source_type as a regular non-empty file; replacement
1713        * should fail */
1714       {
1715         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, "incorrect etag",
1716         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1717         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1718         FALSE, G_IO_ERROR, G_IO_ERROR_WRONG_ETAG,
1719         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1720         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1721       },
1722
1723       /* replace_make_backup == TRUE, replace_flags == REPLACE_DESTINATION,
1724        * replace_etag == NULL, all the different values of setup_source_type,
1725        * with a backup file created to check it’s either replaced or the
1726        * operation fails */
1727       {
1728         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1729         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1730         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
1731         TRUE, 0, 0,
1732         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1733         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1734       },
1735       {
1736         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1737         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode,
1738         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1739         TRUE, 0, 0,
1740         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1741         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, NULL,
1742       },
1743       {
1744         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1745         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1746         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1747         TRUE, 0, 0,
1748         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1749         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1750       },
1751       {
1752         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1753         FILE_TEST_SETUP_TYPE_DIRECTORY, 0,
1754         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1755         FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
1756         2, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL,
1757         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1758       },
1759       {
1760         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1761         FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode,
1762         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1763         FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
1764         2, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, NULL,
1765         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1766       },
1767       {
1768         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1769         FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0,
1770         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1771         TRUE, 0, 0,
1772         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1773         FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, "source-target",
1774       },
1775       {
1776         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1777         FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
1778         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1779         TRUE, 0, 0,
1780         /* the third file is `source-target`, the original target of the old
1781          * source file */
1782         3, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1783         FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target",
1784       },
1785
1786       /* replace_make_backup == TRUE, replace_flags == REPLACE_DESTINATION,
1787        * replace_etag == NULL, setup_source_type is a regular file, with a
1788        * backup file of every type created to check it’s either replaced or the
1789        * operation fails */
1790       {
1791         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1792         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1793         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
1794         TRUE, 0, 0,
1795         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1796         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1797       },
1798       {
1799         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1800         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1801         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, FALSE,
1802         TRUE, 0, 0,
1803         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1804         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1805       },
1806       {
1807         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1808         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1809         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
1810         TRUE, 0, 0,
1811         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1812         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1813       },
1814       {
1815         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1816         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1817         FILE_TEST_SETUP_TYPE_DIRECTORY, 0, FALSE,
1818         FALSE, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP,
1819         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1820         FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL,
1821       },
1822       {
1823         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1824         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1825         FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, FALSE,
1826         TRUE, 0, 0,
1827         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1828         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1829       },
1830       {
1831         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1832         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1833         FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, FALSE,
1834         TRUE, 0, 0,
1835         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1836         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1837       },
1838       {
1839         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1840         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1841         FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, FALSE,
1842         TRUE, 0, 0,
1843         /* the third file is `source~-target`, the original target of the old
1844          * backup symlink */
1845         3, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1846         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1847       },
1848
1849       /* several different setups with replace_flags == PRIVATE */
1850       {
1851         FALSE, G_FILE_CREATE_PRIVATE, NULL,
1852         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1853         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
1854         TRUE, 0, 0,
1855         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_private_mode, new_contents,
1856         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1857       },
1858       {
1859         FALSE, G_FILE_CREATE_PRIVATE, NULL,
1860         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1861         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
1862         TRUE, 0, 0,
1863         /* the file isn’t being replaced, so it should keep its existing permissions */
1864         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1865         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1866       },
1867       {
1868         FALSE, G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1869         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1870         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
1871         TRUE, 0, 0,
1872         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_private_mode, new_contents,
1873         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1874       },
1875       {
1876         FALSE, G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1877         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1878         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
1879         TRUE, 0, 0,
1880         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_private_mode, new_contents,
1881         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1882       },
1883
1884       /* make the initial source file unreadable, so the replace operation
1885        * should fail
1886        *
1887        * Permissions are ignored if we have CAP_DAC_OVERRIDE or equivalent,
1888        * and in particular if we're root. In this scenario,we need to skip it */
1889       {
1890         FALSE, G_FILE_CREATE_NONE, NULL,
1891         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, 0  /* most restrictive permissions */,
1892         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, TRUE,
1893         FALSE, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
1894         1, FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, 0, NULL,
1895         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1896       },
1897     };
1898   gsize i;
1899
1900   g_test_summary ("Test various situations for g_file_replace()");
1901
1902   /* Reset the umask after querying it above. There’s no way to query it without
1903    * changing it. */
1904   umask (current_umask);
1905   g_test_message ("Current umask: %u", current_umask);
1906
1907   for (i = 0; i < G_N_ELEMENTS (tests); i++)
1908     {
1909       gchar *tmpdir_path = NULL;
1910       GFile *tmpdir = NULL, *source_file = NULL, *backup_file = NULL;
1911       GFileOutputStream *output_stream = NULL;
1912       GFileIOStream *io_stream = NULL;
1913       GFileEnumerator *enumerator = NULL;
1914       GFileInfo *info = NULL;
1915       guint n_files;
1916       GError *local_error = NULL;
1917
1918       /* Create a fresh, empty working directory. */
1919       tmpdir_path = g_dir_make_tmp ("g_file_replace_XXXXXX", &local_error);
1920       g_assert_no_error (local_error);
1921       tmpdir = g_file_new_for_path (tmpdir_path);
1922
1923       g_test_message ("Test %" G_GSIZE_FORMAT ", using temporary directory %s", i, tmpdir_path);
1924
1925       if (tests[i].skip_if_cap_dac_override && check_cap_dac_override (tmpdir_path))
1926         {
1927           g_test_message ("Skipping test as process has CAP_DAC_OVERRIDE capability and the test checks permissions");
1928
1929           g_file_delete (tmpdir, NULL, &local_error);
1930           g_assert_no_error (local_error);
1931           g_clear_object (&tmpdir);
1932           g_free (tmpdir_path);
1933
1934           continue;
1935         }
1936
1937       g_free (tmpdir_path);
1938
1939       /* Set up the test directory. */
1940       source_file = create_test_file (tmpdir, "source", tests[i].setup_source_type, tests[i].setup_source_mode);
1941       backup_file = create_test_file (tmpdir, "source~", tests[i].setup_backup_type, tests[i].setup_backup_mode);
1942
1943       /* Replace the source file. Check the error state only after finishing
1944        * writing, as the replace operation is split across g_file_replace() and
1945        * g_output_stream_close(). */
1946       if (read_write)
1947         io_stream = g_file_replace_readwrite (source_file,
1948                                               tests[i].replace_etag,
1949                                               tests[i].replace_make_backup,
1950                                               tests[i].replace_flags,
1951                                               NULL,
1952                                               &local_error);
1953       else
1954         output_stream = g_file_replace (source_file,
1955                                         tests[i].replace_etag,
1956                                         tests[i].replace_make_backup,
1957                                         tests[i].replace_flags,
1958                                         NULL,
1959                                         &local_error);
1960
1961       if (tests[i].expected_success)
1962         {
1963           g_assert_no_error (local_error);
1964           if (read_write)
1965             g_assert_nonnull (io_stream);
1966           else
1967             g_assert_nonnull (output_stream);
1968         }
1969
1970       /* Write new content to it. */
1971       if (io_stream != NULL)
1972         {
1973           GOutputStream *io_output_stream = g_io_stream_get_output_stream (G_IO_STREAM (io_stream));
1974           gsize n_written;
1975
1976           g_output_stream_write_all (G_OUTPUT_STREAM (io_output_stream), new_contents, strlen (new_contents),
1977                                      &n_written, NULL, &local_error);
1978
1979           if (tests[i].expected_success)
1980             {
1981               g_assert_no_error (local_error);
1982               g_assert_cmpint (n_written, ==, strlen (new_contents));
1983             }
1984
1985           g_io_stream_close (G_IO_STREAM (io_stream), NULL, (local_error == NULL) ? &local_error : NULL);
1986
1987           if (tests[i].expected_success)
1988             g_assert_no_error (local_error);
1989         }
1990       else if (output_stream != NULL)
1991         {
1992           gsize n_written;
1993
1994           g_output_stream_write_all (G_OUTPUT_STREAM (output_stream), new_contents, strlen (new_contents),
1995                                      &n_written, NULL, &local_error);
1996
1997           if (tests[i].expected_success)
1998             {
1999               g_assert_no_error (local_error);
2000               g_assert_cmpint (n_written, ==, strlen (new_contents));
2001             }
2002
2003           g_output_stream_close (G_OUTPUT_STREAM (output_stream), NULL, (local_error == NULL) ? &local_error : NULL);
2004
2005           if (tests[i].expected_success)
2006             g_assert_no_error (local_error);
2007         }
2008
2009       if (tests[i].expected_success)
2010         g_assert_no_error (local_error);
2011       else
2012         g_assert_error (local_error, tests[i].expected_error_domain, tests[i].expected_error_code);
2013
2014       g_clear_error (&local_error);
2015       g_clear_object (&io_stream);
2016       g_clear_object (&output_stream);
2017
2018       /* Verify the final state of the directory. */
2019       enumerator = g_file_enumerate_children (tmpdir,
2020                                               G_FILE_ATTRIBUTE_STANDARD_NAME,
2021                                               G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error);
2022       g_assert_no_error (local_error);
2023
2024       n_files = 0;
2025       do
2026         {
2027           g_file_enumerator_iterate (enumerator, &info, NULL, NULL, &local_error);
2028           g_assert_no_error (local_error);
2029
2030           if (info != NULL)
2031             n_files++;
2032         }
2033       while (info != NULL);
2034
2035       g_clear_object (&enumerator);
2036
2037       g_assert_cmpuint (n_files, ==, tests[i].expected_n_files);
2038
2039       check_test_file (source_file, tmpdir, "source", tests[i].expected_source_type, tests[i].expected_source_mode, tests[i].expected_source_contents);
2040       check_test_file (backup_file, tmpdir, "source~", tests[i].expected_backup_type, tests[i].expected_backup_mode, tests[i].expected_backup_contents);
2041
2042       /* Tidy up. Ignore failure apart from when deleting the directory, which
2043        * should be empty. */
2044       g_file_delete (source_file, NULL, NULL);
2045       g_file_delete (backup_file, NULL, NULL);
2046
2047       /* Other files which are occasionally generated by the tests. */
2048         {
2049           GFile *backup_target_file = g_file_get_child (tmpdir, "source~-target");
2050           g_file_delete (backup_target_file, NULL, NULL);
2051           g_clear_object (&backup_target_file);
2052         }
2053         {
2054           GFile *backup_target_file = g_file_get_child (tmpdir, "source-target");
2055           g_file_delete (backup_target_file, NULL, NULL);
2056           g_clear_object (&backup_target_file);
2057         }
2058
2059       g_file_delete (tmpdir, NULL, &local_error);
2060       g_assert_no_error (local_error);
2061
2062       g_clear_object (&backup_file);
2063       g_clear_object (&source_file);
2064       g_clear_object (&tmpdir);
2065     }
2066 #else  /* if !__linux__ */
2067   g_test_skip ("File replacement tests can only be run on Linux");
2068 #endif
2069 }
2070
2071 static void
2072 on_new_tmp_done (GObject      *object,
2073                  GAsyncResult *result,
2074                  gpointer      user_data)
2075 {
2076   GFile *file;
2077   GFile *parent;
2078   GFileInfo *info;
2079   GFileIOStream *iostream;
2080   GError *error = NULL;
2081   GMainLoop *loop = user_data;
2082   gchar *basename;
2083   GFile *tmpdir = NULL;
2084
2085   g_assert_null (object);
2086
2087   file = g_file_new_tmp_finish (result, &iostream, &error);
2088   g_assert_no_error (error);
2089
2090   g_assert_true (g_file_query_exists (file, NULL));
2091
2092   basename = g_file_get_basename (file);
2093   g_assert_true (g_str_has_prefix (basename, "g_file_new_tmp_async_"));
2094
2095   info = g_file_io_stream_query_info (iostream, G_FILE_ATTRIBUTE_STANDARD_TYPE,
2096                                       NULL, &error);
2097   g_assert_no_error (error);
2098
2099   g_assert_cmpuint (g_file_info_get_file_type (info), ==, G_FILE_TYPE_REGULAR);
2100   g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
2101   g_assert_no_error (error);
2102
2103   parent = g_file_get_parent (file);
2104   tmpdir = g_file_new_for_path (g_get_tmp_dir ());
2105
2106   g_assert_true (g_file_equal (tmpdir, parent));
2107
2108   g_main_loop_quit (loop);
2109
2110   g_object_unref (file);
2111   g_object_unref (parent);
2112   g_object_unref (iostream);
2113   g_object_unref (info);
2114   g_free (basename);
2115   g_object_unref (tmpdir);
2116 }
2117
2118 static void
2119 on_new_tmp_error (GObject      *object,
2120                   GAsyncResult *result,
2121                   gpointer      user_data)
2122 {
2123   GFileIOStream *iostream = (GFileIOStream*) &on_new_tmp_error;
2124   AsyncErrorData *error_data = user_data;
2125
2126   g_assert_null (object);
2127
2128   g_assert_null (g_file_new_tmp_finish (result, &iostream, error_data->error));
2129   g_assert_nonnull (error_data->error);
2130   g_assert_null (iostream);
2131
2132   g_main_loop_quit (error_data->loop);
2133 }
2134
2135 static void
2136 test_async_new_tmp (void)
2137 {
2138   GMainLoop *loop;
2139   GError *error = NULL;
2140   GCancellable *cancellable;
2141   AsyncErrorData error_data = { .error = &error };
2142
2143   loop = g_main_loop_new (NULL, TRUE);
2144   error_data.loop = loop;
2145
2146   g_file_new_tmp_async ("g_file_new_tmp_async_XXXXXX",
2147                         G_PRIORITY_DEFAULT, NULL,
2148                         on_new_tmp_done, loop);
2149   g_main_loop_run (loop);
2150
2151   g_file_new_tmp_async ("g_file_new_tmp_async_invalid_template",
2152                         G_PRIORITY_DEFAULT, NULL,
2153                         on_new_tmp_error, &error_data);
2154   g_main_loop_run (loop);
2155   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
2156   g_clear_error (&error);
2157
2158   cancellable = g_cancellable_new ();
2159   g_file_new_tmp_async ("g_file_new_tmp_async_cancelled_XXXXXX",
2160                         G_PRIORITY_DEFAULT, cancellable,
2161                         on_new_tmp_error, &error_data);
2162   g_cancellable_cancel (cancellable);
2163   g_main_loop_run (loop);
2164   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
2165   g_clear_object (&cancellable);
2166   g_clear_error (&error);
2167
2168   g_main_loop_unref (loop);
2169 }
2170
2171 static void
2172 on_new_tmp_dir_done (GObject      *object,
2173                      GAsyncResult *result,
2174                      gpointer      user_data)
2175 {
2176   GFile *file;
2177   GFile *parent;
2178   GFileInfo *info;
2179   GError *error = NULL;
2180   GMainLoop *loop = user_data;
2181   gchar *basename;
2182   GFile *tmpdir = NULL;
2183
2184   g_assert_null (object);
2185
2186   file = g_file_new_tmp_dir_finish (result, &error);
2187   g_assert_no_error (error);
2188
2189   g_assert_true (g_file_query_exists (file, NULL));
2190
2191   basename = g_file_get_basename (file);
2192   g_assert_true (g_str_has_prefix (basename, "g_file_new_tmp_dir_async_"));
2193
2194   info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE,
2195                             G_FILE_QUERY_INFO_NONE, NULL, &error);
2196   g_assert_no_error (error);
2197
2198   g_assert_cmpuint (g_file_info_get_file_type (info), ==, G_FILE_TYPE_DIRECTORY);
2199
2200   parent = g_file_get_parent (file);
2201   tmpdir = g_file_new_for_path (g_get_tmp_dir ());
2202
2203   g_assert_true (g_file_equal (tmpdir, parent));
2204
2205   g_main_loop_quit (loop);
2206
2207   g_object_unref (file);
2208   g_object_unref (parent);
2209   g_object_unref (info);
2210   g_free (basename);
2211   g_object_unref (tmpdir);
2212 }
2213
2214 static void
2215 on_new_tmp_dir_error (GObject      *object,
2216                       GAsyncResult *result,
2217                       gpointer      user_data)
2218 {
2219   AsyncErrorData *error_data = user_data;
2220
2221   g_assert_null (object);
2222
2223   g_assert_null (g_file_new_tmp_dir_finish (result, error_data->error));
2224   g_assert_nonnull (error_data->error);
2225
2226   g_main_loop_quit (error_data->loop);
2227 }
2228
2229 static void
2230 test_async_new_tmp_dir (void)
2231 {
2232   GMainLoop *loop;
2233   GError *error = NULL;
2234   GCancellable *cancellable;
2235   AsyncErrorData error_data = { .error = &error };
2236
2237   loop = g_main_loop_new (NULL, TRUE);
2238   error_data.loop = loop;
2239
2240   g_file_new_tmp_dir_async ("g_file_new_tmp_dir_async_XXXXXX",
2241                             G_PRIORITY_DEFAULT, NULL,
2242                             on_new_tmp_dir_done, loop);
2243   g_main_loop_run (loop);
2244
2245   g_file_new_tmp_dir_async ("g_file_new_tmp_dir_async",
2246                             G_PRIORITY_DEFAULT, NULL,
2247                             on_new_tmp_dir_error, &error_data);
2248   g_main_loop_run (loop);
2249   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
2250   g_clear_error (&error);
2251
2252   cancellable = g_cancellable_new ();
2253   g_file_new_tmp_dir_async ("g_file_new_tmp_dir_async_cancelled_XXXXXX",
2254                             G_PRIORITY_DEFAULT, cancellable,
2255                             on_new_tmp_dir_error, &error_data);
2256   g_cancellable_cancel (cancellable);
2257   g_main_loop_run (loop);
2258   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
2259   g_clear_object (&cancellable);
2260   g_clear_error (&error);
2261
2262   g_main_loop_unref (loop);
2263 }
2264
2265 static void
2266 on_file_deleted (GObject      *object,
2267                  GAsyncResult *result,
2268                  gpointer      user_data)
2269 {
2270   GError *local_error = NULL;
2271   GMainLoop *loop = user_data;
2272
2273   (void) g_file_delete_finish ((GFile*)object, result, &local_error);
2274   g_assert_no_error (local_error);
2275
2276   g_main_loop_quit (loop);
2277 }
2278
2279 static void
2280 test_async_delete (void)
2281 {
2282   GFile *file;
2283   GFileIOStream *iostream;
2284   GError *local_error = NULL;
2285   GError **error = &local_error;
2286   GMainLoop *loop;
2287
2288   file = g_file_new_tmp ("g_file_delete_XXXXXX",
2289                          &iostream, error);
2290   g_assert_no_error (local_error);
2291   g_object_unref (iostream);
2292
2293   g_assert_true (g_file_query_exists (file, NULL));
2294
2295   loop = g_main_loop_new (NULL, TRUE);
2296
2297   g_file_delete_async (file, G_PRIORITY_DEFAULT, NULL, on_file_deleted, loop);
2298
2299   g_main_loop_run (loop);
2300
2301   g_assert_false (g_file_query_exists (file, NULL));
2302
2303   g_main_loop_unref (loop);
2304   g_object_unref (file);
2305 }
2306
2307 static void
2308 on_symlink_done (GObject      *object,
2309                  GAsyncResult *result,
2310                  gpointer      user_data)
2311 {
2312   GFile *file = (GFile *) object;
2313   GError *error = NULL;
2314   GMainLoop *loop = user_data;
2315
2316   g_assert_true (g_file_make_symbolic_link_finish (file, result, &error));
2317   g_assert_no_error (error);
2318
2319   g_main_loop_quit (loop);
2320 }
2321
2322 static void
2323 on_symlink_error (GObject      *object,
2324                   GAsyncResult *result,
2325                   gpointer      user_data)
2326 {
2327   GFile *file = (GFile *) object;
2328   GError *error = NULL;
2329   AsyncErrorData *data = user_data;
2330
2331   g_assert_false (g_file_make_symbolic_link_finish (file, result, &error));
2332   g_assert_nonnull (error);
2333   g_propagate_error (data->error, g_steal_pointer (&error));
2334
2335   g_main_loop_quit (data->loop);
2336 }
2337
2338 static void
2339 test_async_make_symlink (void)
2340 {
2341   GFile *link;
2342   GFile *parent_dir;
2343   GFile *target;
2344   GFileInfo *link_info;
2345   GFileIOStream *iostream;
2346   GError *error = NULL;
2347   GCancellable *cancellable;
2348   GMainLoop *loop;
2349   AsyncErrorData error_data = {0};
2350   gchar *tmpdir_path;
2351   gchar *target_path;
2352
2353   target = g_file_new_tmp ("g_file_symlink_target_XXXXXX", &iostream, &error);
2354   g_assert_no_error (error);
2355
2356   g_io_stream_close ((GIOStream *) iostream, NULL, &error);
2357   g_assert_no_error (error);
2358   g_object_unref (iostream);
2359
2360   g_assert_true (g_file_query_exists (target, NULL));
2361
2362   loop = g_main_loop_new (NULL, TRUE);
2363   error_data.loop = loop;
2364   error_data.error = &error;
2365
2366   tmpdir_path = g_dir_make_tmp ("g_file_symlink_XXXXXX", &error);
2367   g_assert_no_error (error);
2368
2369   parent_dir = g_file_new_for_path (tmpdir_path);
2370   g_assert_true (g_file_query_exists (parent_dir, NULL));
2371
2372   link = g_file_get_child (parent_dir, "symlink");
2373   g_assert_false (g_file_query_exists (link, NULL));
2374
2375   g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
2376                          "*assertion*symlink_value*failed*");
2377   g_file_make_symbolic_link_async (link, NULL,
2378                                    G_PRIORITY_DEFAULT, NULL,
2379                                    on_symlink_done, loop);
2380   g_test_assert_expected_messages ();
2381
2382   g_file_make_symbolic_link_async (link, "",
2383                                    G_PRIORITY_DEFAULT, NULL,
2384                                    on_symlink_error, &error_data);
2385   g_main_loop_run (loop);
2386   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
2387   g_clear_error (&error);
2388
2389   target_path = g_file_get_path (target);
2390   g_file_make_symbolic_link_async (link, target_path,
2391                                    G_PRIORITY_DEFAULT, NULL,
2392                                    on_symlink_done, loop);
2393   g_main_loop_run (loop);
2394
2395   g_assert_true (g_file_query_exists (link, NULL));
2396   link_info = g_file_query_info (link,
2397                                  G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
2398                                  G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
2399                                  G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2400                                  NULL,
2401                                  &error);
2402   g_assert_no_error (error);
2403
2404   g_assert_true (g_file_info_get_is_symlink (link_info));
2405   g_assert_cmpstr (target_path, ==, g_file_info_get_symlink_target (link_info));
2406
2407   /* Try creating it again, it fails */
2408   g_file_make_symbolic_link_async (link, target_path,
2409                                    G_PRIORITY_DEFAULT, NULL,
2410                                    on_symlink_error, &error_data);
2411   g_main_loop_run (loop);
2412   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS);
2413   g_clear_error (&error);
2414
2415   cancellable = g_cancellable_new ();
2416   g_file_make_symbolic_link_async (link, target_path,
2417                                    G_PRIORITY_DEFAULT, cancellable,
2418                                    on_symlink_error, &error_data);
2419   g_cancellable_cancel (cancellable);
2420   g_main_loop_run (loop);
2421   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
2422   g_clear_error (&error);
2423   g_clear_object (&cancellable);
2424
2425   g_main_loop_unref (loop);
2426   g_object_unref (target);
2427   g_object_unref (parent_dir);
2428   g_object_unref (link);
2429   g_object_unref (link_info);
2430   g_free (tmpdir_path);
2431   g_free (target_path);
2432 }
2433
2434 static void
2435 test_copy_preserve_mode (void)
2436 {
2437 #ifdef G_OS_UNIX
2438   mode_t current_umask = umask (0);
2439   const struct
2440     {
2441       guint32 source_mode;
2442       guint32 expected_destination_mode;
2443       gboolean create_destination_before_copy;
2444       GFileCopyFlags copy_flags;
2445     }
2446   vectors[] =
2447     {
2448       /* Overwriting the destination file should copy the permissions from the
2449        * source file, even if %G_FILE_COPY_ALL_METADATA is set: */
2450       { 0600, 0600, TRUE, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA },
2451       { 0600, 0600, TRUE, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS },
2452       /* The same behaviour should hold if the destination file is not being
2453        * overwritten because it doesn’t already exist: */
2454       { 0600, 0600, FALSE, G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA },
2455       { 0600, 0600, FALSE, G_FILE_COPY_NOFOLLOW_SYMLINKS },
2456       /* Anything with %G_FILE_COPY_TARGET_DEFAULT_PERMS should use the current
2457        * umask for the destination file: */
2458       { 0600, 0666 & ~current_umask, TRUE, G_FILE_COPY_TARGET_DEFAULT_PERMS | G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA },
2459       { 0600, 0666 & ~current_umask, TRUE, G_FILE_COPY_TARGET_DEFAULT_PERMS | G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS },
2460       { 0600, 0666 & ~current_umask, FALSE, G_FILE_COPY_TARGET_DEFAULT_PERMS | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA },
2461       { 0600, 0666 & ~current_umask, FALSE, G_FILE_COPY_TARGET_DEFAULT_PERMS | G_FILE_COPY_NOFOLLOW_SYMLINKS },
2462     };
2463   gsize i;
2464
2465   /* Reset the umask after querying it above. There’s no way to query it without
2466    * changing it. */
2467   umask (current_umask);
2468   g_test_message ("Current umask: %u", current_umask);
2469
2470   for (i = 0; i < G_N_ELEMENTS (vectors); i++)
2471     {
2472       GFile *tmpfile;
2473       GFile *dest_tmpfile;
2474       GFileInfo *dest_info;
2475       GFileIOStream *iostream;
2476       GError *local_error = NULL;
2477       guint32 romode = vectors[i].source_mode;
2478       guint32 dest_mode;
2479
2480       g_test_message ("Vector %" G_GSIZE_FORMAT, i);
2481
2482       tmpfile = g_file_new_tmp ("tmp-copy-preserve-modeXXXXXX",
2483                                 &iostream, &local_error);
2484       g_assert_no_error (local_error);
2485       g_io_stream_close ((GIOStream*)iostream, NULL, &local_error);
2486       g_assert_no_error (local_error);
2487       g_clear_object (&iostream);
2488
2489       g_file_set_attribute (tmpfile, G_FILE_ATTRIBUTE_UNIX_MODE, G_FILE_ATTRIBUTE_TYPE_UINT32,
2490                             &romode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2491                             NULL, &local_error);
2492       g_assert_no_error (local_error);
2493
2494       dest_tmpfile = g_file_new_tmp ("tmp-copy-preserve-modeXXXXXX",
2495                                      &iostream, &local_error);
2496       g_assert_no_error (local_error);
2497       g_io_stream_close ((GIOStream*)iostream, NULL, &local_error);
2498       g_assert_no_error (local_error);
2499       g_clear_object (&iostream);
2500
2501       if (!vectors[i].create_destination_before_copy)
2502         {
2503           g_file_delete (dest_tmpfile, NULL, &local_error);
2504           g_assert_no_error (local_error);
2505         }
2506
2507       g_file_copy (tmpfile, dest_tmpfile, vectors[i].copy_flags,
2508                    NULL, NULL, NULL, &local_error);
2509       g_assert_no_error (local_error);
2510
2511       dest_info = g_file_query_info (dest_tmpfile, G_FILE_ATTRIBUTE_UNIX_MODE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2512                                      NULL, &local_error);
2513       g_assert_no_error (local_error);
2514
2515       dest_mode = g_file_info_get_attribute_uint32 (dest_info, G_FILE_ATTRIBUTE_UNIX_MODE);
2516
2517       g_assert_cmpint (dest_mode & ~S_IFMT, ==, vectors[i].expected_destination_mode);
2518       g_assert_cmpint (dest_mode & S_IFMT, ==, S_IFREG);
2519
2520       (void) g_file_delete (tmpfile, NULL, NULL);
2521       (void) g_file_delete (dest_tmpfile, NULL, NULL);
2522
2523       g_clear_object (&tmpfile);
2524       g_clear_object (&dest_tmpfile);
2525       g_clear_object (&dest_info);
2526     }
2527 #else  /* if !G_OS_UNIX */
2528   g_test_skip ("File permissions tests can only be run on Unix")
2529 #endif
2530 }
2531
2532 typedef struct
2533 {
2534   goffset current_num_bytes;
2535   goffset total_num_bytes;
2536 } CopyProgressData;
2537
2538 static void
2539 file_copy_progress_cb (goffset  current_num_bytes,
2540                        goffset  total_num_bytes,
2541                        gpointer user_data)
2542 {
2543   CopyProgressData *prev_data = user_data;
2544
2545   g_assert_cmpuint (total_num_bytes, ==, prev_data->total_num_bytes);
2546   g_assert_cmpuint (current_num_bytes, >=, prev_data->current_num_bytes);
2547
2548   /* Update it for the next callback. */
2549   prev_data->current_num_bytes = current_num_bytes;
2550 }
2551
2552 static void
2553 test_copy_progress (void)
2554 {
2555   GFile *src_tmpfile = NULL;
2556   GFile *dest_tmpfile = NULL;
2557   GFileIOStream *iostream;
2558   GOutputStream *ostream;
2559   GError *local_error = NULL;
2560   const guint8 buffer[] = { 1, 2, 3, 4, 5 };
2561   CopyProgressData progress_data;
2562
2563   src_tmpfile = g_file_new_tmp ("tmp-copy-progressXXXXXX",
2564                                 &iostream, &local_error);
2565   g_assert_no_error (local_error);
2566
2567   /* Write some content to the file for testing. */
2568   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
2569   g_output_stream_write (ostream, buffer, sizeof (buffer), NULL, &local_error);
2570   g_assert_no_error (local_error);
2571
2572   g_io_stream_close ((GIOStream *) iostream, NULL, &local_error);
2573   g_assert_no_error (local_error);
2574   g_clear_object (&iostream);
2575
2576   /* Grab a unique destination filename. */
2577   dest_tmpfile = g_file_new_tmp ("tmp-copy-progressXXXXXX",
2578                                  &iostream, &local_error);
2579   g_assert_no_error (local_error);
2580   g_io_stream_close ((GIOStream *) iostream, NULL, &local_error);
2581   g_assert_no_error (local_error);
2582   g_clear_object (&iostream);
2583
2584   /* Set the progress data to an initial offset of zero. The callback will
2585    * assert that progress is non-decreasing and reaches the total length of
2586    * the file. */
2587   progress_data.current_num_bytes = 0;
2588   progress_data.total_num_bytes = sizeof (buffer);
2589
2590   /* Copy the file with progress reporting. */
2591   g_file_copy (src_tmpfile, dest_tmpfile, G_FILE_COPY_OVERWRITE,
2592                NULL, file_copy_progress_cb, &progress_data, &local_error);
2593   g_assert_no_error (local_error);
2594
2595   g_assert_cmpuint (progress_data.current_num_bytes, ==, progress_data.total_num_bytes);
2596   g_assert_cmpuint (progress_data.total_num_bytes, ==, sizeof (buffer));
2597
2598   /* Clean up. */
2599   (void) g_file_delete (src_tmpfile, NULL, NULL);
2600   (void) g_file_delete (dest_tmpfile, NULL, NULL);
2601
2602   g_clear_object (&src_tmpfile);
2603   g_clear_object (&dest_tmpfile);
2604 }
2605
2606 static void
2607 test_measure (void)
2608 {
2609   GFile *file;
2610   guint64 num_bytes;
2611   guint64 num_dirs;
2612   guint64 num_files;
2613   GError *error = NULL;
2614   gboolean ok;
2615   gchar *path;
2616
2617   path = g_test_build_filename (G_TEST_DIST, "desktop-files", NULL);
2618   file = g_file_new_for_path (path);
2619
2620   ok = g_file_measure_disk_usage (file,
2621                                   G_FILE_MEASURE_APPARENT_SIZE,
2622                                   NULL,
2623                                   NULL,
2624                                   NULL,
2625                                   &num_bytes,
2626                                   &num_dirs,
2627                                   &num_files,
2628                                   &error);
2629   g_assert_true (ok);
2630   g_assert_no_error (error);
2631
2632   g_assert_cmpuint (num_bytes, ==, 74469);
2633   g_assert_cmpuint (num_dirs, ==, 6);
2634   g_assert_cmpuint (num_files, ==, 32);
2635
2636   g_object_unref (file);
2637   g_free (path);
2638 }
2639
2640 typedef struct {
2641   guint64 expected_bytes;
2642   guint64 expected_dirs;
2643   guint64 expected_files;
2644   gint progress_count;
2645   guint64 progress_bytes;
2646   guint64 progress_dirs;
2647   guint64 progress_files;
2648 } MeasureData;
2649
2650 static void
2651 measure_progress (gboolean reporting,
2652                   guint64  current_size,
2653                   guint64  num_dirs,
2654                   guint64  num_files,
2655                   gpointer user_data)
2656 {
2657   MeasureData *data = user_data;
2658
2659   data->progress_count += 1;
2660
2661   g_assert_cmpuint (current_size, >=, data->progress_bytes);
2662   g_assert_cmpuint (num_dirs, >=, data->progress_dirs);
2663   g_assert_cmpuint (num_files, >=, data->progress_files);
2664
2665   data->progress_bytes = current_size;
2666   data->progress_dirs = num_dirs;
2667   data->progress_files = num_files;
2668 }
2669
2670 static void
2671 measure_done (GObject      *source,
2672               GAsyncResult *res,
2673               gpointer      user_data)
2674 {
2675   MeasureData *data = user_data;
2676   guint64 num_bytes, num_dirs, num_files;
2677   GError *error = NULL;
2678   gboolean ok;
2679
2680   ok = g_file_measure_disk_usage_finish (G_FILE (source), res, &num_bytes, &num_dirs, &num_files, &error);
2681   g_assert_true (ok);
2682   g_assert_no_error (error);
2683
2684   g_assert_cmpuint (data->expected_bytes, ==, num_bytes);
2685   g_assert_cmpuint (data->expected_dirs, ==, num_dirs);
2686   g_assert_cmpuint (data->expected_files, ==, num_files);
2687
2688   g_assert_cmpuint (data->progress_count, >, 0);
2689   g_assert_cmpuint (num_bytes, >=, data->progress_bytes);
2690   g_assert_cmpuint (num_dirs, >=, data->progress_dirs);
2691   g_assert_cmpuint (num_files, >=, data->progress_files);
2692
2693   g_free (data);
2694   g_object_unref (source);
2695 }
2696
2697 static void
2698 test_measure_async (void)
2699 {
2700   gchar *path;
2701   GFile *file;
2702   MeasureData *data;
2703
2704   data = g_new (MeasureData, 1);
2705
2706   data->progress_count = 0;
2707   data->progress_bytes = 0;
2708   data->progress_files = 0;
2709   data->progress_dirs = 0;
2710
2711   path = g_test_build_filename (G_TEST_DIST, "desktop-files", NULL);
2712   file = g_file_new_for_path (path);
2713   g_free (path);
2714
2715   data->expected_bytes = 74469;
2716   data->expected_dirs = 6;
2717   data->expected_files = 32;
2718
2719   g_file_measure_disk_usage_async (file,
2720                                    G_FILE_MEASURE_APPARENT_SIZE,
2721                                    0, NULL,
2722                                    measure_progress, data,
2723                                    measure_done, data);
2724 }
2725
2726 static void
2727 test_load_bytes (void)
2728 {
2729   gchar filename[] = "g_file_load_bytes_XXXXXX";
2730   GError *error = NULL;
2731   GBytes *bytes;
2732   GFile *file;
2733   int len;
2734   int fd;
2735   int ret;
2736
2737   fd = g_mkstemp (filename);
2738   g_assert_cmpint (fd, !=, -1);
2739   len = strlen ("test_load_bytes");
2740   ret = write (fd, "test_load_bytes", len);
2741   g_assert_cmpint (ret, ==, len);
2742   close (fd);
2743
2744   file = g_file_new_for_path (filename);
2745   bytes = g_file_load_bytes (file, NULL, NULL, &error);
2746   g_assert_no_error (error);
2747   g_assert_nonnull (bytes);
2748   g_assert_cmpint (len, ==, g_bytes_get_size (bytes));
2749   g_assert_cmpstr ("test_load_bytes", ==, (gchar *)g_bytes_get_data (bytes, NULL));
2750
2751   g_file_delete (file, NULL, NULL);
2752
2753   g_bytes_unref (bytes);
2754   g_object_unref (file);
2755 }
2756
2757 typedef struct
2758 {
2759   GMainLoop *main_loop;
2760   GFile *file;
2761   GBytes *bytes;
2762 } LoadBytesAsyncData;
2763
2764 static void
2765 test_load_bytes_cb (GObject      *object,
2766                     GAsyncResult *result,
2767                     gpointer      user_data)
2768 {
2769   GFile *file = G_FILE (object);
2770   LoadBytesAsyncData *data = user_data;
2771   GError *error = NULL;
2772
2773   data->bytes = g_file_load_bytes_finish (file, result, NULL, &error);
2774   g_assert_no_error (error);
2775   g_assert_nonnull (data->bytes);
2776
2777   g_main_loop_quit (data->main_loop);
2778 }
2779
2780 static void
2781 test_load_bytes_async (void)
2782 {
2783   LoadBytesAsyncData data = { 0 };
2784   gchar filename[] = "g_file_load_bytes_XXXXXX";
2785   int len;
2786   int fd;
2787   int ret;
2788
2789   fd = g_mkstemp (filename);
2790   g_assert_cmpint (fd, !=, -1);
2791   len = strlen ("test_load_bytes_async");
2792   ret = write (fd, "test_load_bytes_async", len);
2793   g_assert_cmpint (ret, ==, len);
2794   close (fd);
2795
2796   data.main_loop = g_main_loop_new (NULL, FALSE);
2797   data.file = g_file_new_for_path (filename);
2798
2799   g_file_load_bytes_async (data.file, NULL, test_load_bytes_cb, &data);
2800   g_main_loop_run (data.main_loop);
2801
2802   g_assert_cmpint (len, ==, g_bytes_get_size (data.bytes));
2803   g_assert_cmpstr ("test_load_bytes_async", ==, (gchar *)g_bytes_get_data (data.bytes, NULL));
2804
2805   g_file_delete (data.file, NULL, NULL);
2806   g_object_unref (data.file);
2807   g_bytes_unref (data.bytes);
2808   g_main_loop_unref (data.main_loop);
2809 }
2810
2811 static void
2812 test_writev_helper (GOutputVector *vectors,
2813                     gsize          n_vectors,
2814                     gboolean       use_bytes_written,
2815                     const guint8  *expected_contents,
2816                     gsize          expected_length)
2817 {
2818   GFile *file;
2819   GFileIOStream *iostream = NULL;
2820   GOutputStream *ostream;
2821   GError *error = NULL;
2822   gsize bytes_written = 0;
2823   gboolean res;
2824   guint8 *contents;
2825   gsize length;
2826
2827   file = g_file_new_tmp ("g_file_writev_XXXXXX",
2828                          &iostream, NULL);
2829   g_assert_nonnull (file);
2830   g_assert_nonnull (iostream);
2831
2832   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
2833
2834   res = g_output_stream_writev_all (ostream, vectors, n_vectors, use_bytes_written ? &bytes_written : NULL, NULL, &error);
2835   g_assert_no_error (error);
2836   g_assert_true (res);
2837   if (use_bytes_written)
2838     g_assert_cmpuint (bytes_written, ==, expected_length);
2839
2840   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
2841   g_assert_no_error (error);
2842   g_assert_true (res);
2843   g_object_unref (iostream);
2844
2845   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
2846   g_assert_no_error (error);
2847   g_assert_true (res);
2848
2849   g_assert_cmpmem (contents, length, expected_contents, expected_length);
2850
2851   g_free (contents);
2852
2853   g_file_delete (file, NULL, NULL);
2854   g_object_unref (file);
2855 }
2856
2857 /* Test that writev() on local file output streams works on a non-empty vector */
2858 static void
2859 test_writev (void)
2860 {
2861   GOutputVector vectors[3];
2862   const guint8 buffer[] = {1, 2, 3, 4, 5,
2863                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
2864                            1, 2, 3};
2865
2866   vectors[0].buffer = buffer;
2867   vectors[0].size = 5;
2868
2869   vectors[1].buffer = buffer + 5;
2870   vectors[1].size = 12;
2871
2872   vectors[2].buffer = buffer + 5 + 12;
2873   vectors[2].size = 3;
2874
2875   test_writev_helper (vectors, G_N_ELEMENTS (vectors), TRUE, buffer, sizeof buffer);
2876 }
2877
2878 /* Test that writev() on local file output streams works on a non-empty vector without returning bytes_written */
2879 static void
2880 test_writev_no_bytes_written (void)
2881 {
2882   GOutputVector vectors[3];
2883   const guint8 buffer[] = {1, 2, 3, 4, 5,
2884                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
2885                            1, 2, 3};
2886
2887   vectors[0].buffer = buffer;
2888   vectors[0].size = 5;
2889
2890   vectors[1].buffer = buffer + 5;
2891   vectors[1].size = 12;
2892
2893   vectors[2].buffer = buffer + 5 + 12;
2894   vectors[2].size = 3;
2895
2896   test_writev_helper (vectors, G_N_ELEMENTS (vectors), FALSE, buffer, sizeof buffer);
2897 }
2898
2899 /* Test that writev() on local file output streams works on 0 vectors */
2900 static void
2901 test_writev_no_vectors (void)
2902 {
2903   test_writev_helper (NULL, 0, TRUE, NULL, 0);
2904 }
2905
2906 /* Test that writev() on local file output streams works on empty vectors */
2907 static void
2908 test_writev_empty_vectors (void)
2909 {
2910   GOutputVector vectors[3];
2911
2912   vectors[0].buffer = NULL;
2913   vectors[0].size = 0;
2914   vectors[1].buffer = NULL;
2915   vectors[1].size = 0;
2916   vectors[2].buffer = NULL;
2917   vectors[2].size = 0;
2918
2919   test_writev_helper (vectors, G_N_ELEMENTS (vectors), TRUE, NULL, 0);
2920 }
2921
2922 /* Test that writev() fails if the sum of sizes in the vector is too big */
2923 static void
2924 test_writev_too_big_vectors (void)
2925 {
2926   GFile *file;
2927   GFileIOStream *iostream = NULL;
2928   GOutputStream *ostream;
2929   GError *error = NULL;
2930   gsize bytes_written = 0;
2931   gboolean res;
2932   guint8 *contents;
2933   gsize length;
2934   GOutputVector vectors[3];
2935
2936   vectors[0].buffer = (void*) 1;
2937   vectors[0].size = G_MAXSIZE / 2;
2938
2939   vectors[1].buffer = (void*) 1;
2940   vectors[1].size = G_MAXSIZE / 2;
2941
2942   vectors[2].buffer = (void*) 1;
2943   vectors[2].size = G_MAXSIZE / 2;
2944
2945   file = g_file_new_tmp ("g_file_writev_XXXXXX",
2946                          &iostream, NULL);
2947   g_assert_nonnull (file);
2948   g_assert_nonnull (iostream);
2949
2950   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
2951
2952   res = g_output_stream_writev_all (ostream, vectors, G_N_ELEMENTS (vectors), &bytes_written, NULL, &error);
2953   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
2954   g_assert_cmpuint (bytes_written, ==, 0);
2955   g_assert_false (res);
2956   g_clear_error (&error);
2957
2958   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
2959   g_assert_no_error (error);
2960   g_assert_true (res);
2961   g_object_unref (iostream);
2962
2963   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
2964   g_assert_no_error (error);
2965   g_assert_true (res);
2966
2967   g_assert_cmpmem (contents, length, NULL, 0);
2968
2969   g_free (contents);
2970
2971   g_file_delete (file, NULL, NULL);
2972   g_object_unref (file);
2973 }
2974
2975 typedef struct
2976 {
2977   gsize bytes_written;
2978   GOutputVector *vectors;
2979   gsize n_vectors;
2980   GError *error;
2981   gboolean done;
2982 } WritevAsyncData;
2983
2984 static void
2985 test_writev_async_cb (GObject      *object,
2986                       GAsyncResult *result,
2987                       gpointer      user_data)
2988 {
2989   GOutputStream *ostream = G_OUTPUT_STREAM (object);
2990   WritevAsyncData *data = user_data;
2991   GError *error = NULL;
2992   gsize bytes_written;
2993   gboolean res;
2994
2995   res = g_output_stream_writev_finish (ostream, result, &bytes_written, &error);
2996   g_assert_true (res);
2997   g_assert_no_error (error);
2998   data->bytes_written += bytes_written;
2999
3000   /* skip vectors that have been written in full */
3001   while (data->n_vectors > 0 && bytes_written >= data->vectors[0].size)
3002     {
3003       bytes_written -= data->vectors[0].size;
3004       ++data->vectors;
3005       --data->n_vectors;
3006     }
3007   /* skip partially written vector data */
3008   if (bytes_written > 0 && data->n_vectors > 0)
3009     {
3010       data->vectors[0].size -= bytes_written;
3011       data->vectors[0].buffer = ((guint8 *) data->vectors[0].buffer) + bytes_written;
3012     }
3013
3014   if (data->n_vectors > 0)
3015     g_output_stream_writev_async (ostream, data->vectors, data->n_vectors, 0, NULL, test_writev_async_cb, &data);
3016 }
3017
3018 /* Test that writev_async() on local file output streams works on a non-empty vector */
3019 static void
3020 test_writev_async (void)
3021 {
3022   WritevAsyncData data = { 0 };
3023   GFile *file;
3024   GFileIOStream *iostream = NULL;
3025   GOutputVector vectors[3];
3026   const guint8 buffer[] = {1, 2, 3, 4, 5,
3027                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
3028                            1, 2, 3};
3029   GOutputStream *ostream;
3030   GError *error = NULL;
3031   gboolean res;
3032   guint8 *contents;
3033   gsize length;
3034
3035   vectors[0].buffer = buffer;
3036   vectors[0].size = 5;
3037
3038   vectors[1].buffer = buffer + 5;
3039   vectors[1].size = 12;
3040
3041   vectors[2].buffer = buffer + 5  + 12;
3042   vectors[2].size = 3;
3043
3044   file = g_file_new_tmp ("g_file_writev_XXXXXX",
3045                          &iostream, NULL);
3046   g_assert_nonnull (file);
3047   g_assert_nonnull (iostream);
3048
3049   data.vectors = vectors;
3050   data.n_vectors = G_N_ELEMENTS (vectors);
3051
3052   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
3053
3054   g_output_stream_writev_async (ostream, data.vectors, data.n_vectors, 0, NULL, test_writev_async_cb, &data);
3055
3056   while (data.n_vectors > 0)
3057     g_main_context_iteration (NULL, TRUE);
3058
3059   g_assert_cmpuint (data.bytes_written, ==, sizeof buffer);
3060
3061   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
3062   g_assert_no_error (error);
3063   g_assert_true (res);
3064   g_object_unref (iostream);
3065
3066   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
3067   g_assert_no_error (error);
3068   g_assert_true (res);
3069
3070   g_assert_cmpmem (contents, length, buffer, sizeof buffer);
3071
3072   g_free (contents);
3073
3074   g_file_delete (file, NULL, NULL);
3075   g_object_unref (file);
3076 }
3077
3078 static void
3079 test_writev_all_cb (GObject      *object,
3080                     GAsyncResult *result,
3081                     gpointer      user_data)
3082 {
3083   GOutputStream *ostream = G_OUTPUT_STREAM (object);
3084   WritevAsyncData *data = user_data;
3085
3086   g_output_stream_writev_all_finish (ostream, result, &data->bytes_written, &data->error);
3087   data->done = TRUE;
3088 }
3089
3090 /* Test that writev_async_all() on local file output streams works on a non-empty vector */
3091 static void
3092 test_writev_async_all (void)
3093 {
3094   WritevAsyncData data = { 0 };
3095   GFile *file;
3096   GFileIOStream *iostream = NULL;
3097   GOutputStream *ostream;
3098   GOutputVector vectors[3];
3099   const guint8 buffer[] = {1, 2, 3, 4, 5,
3100                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
3101                            1, 2, 3};
3102   GError *error = NULL;
3103   gboolean res;
3104   guint8 *contents;
3105   gsize length;
3106
3107   vectors[0].buffer = buffer;
3108   vectors[0].size = 5;
3109
3110   vectors[1].buffer = buffer + 5;
3111   vectors[1].size = 12;
3112
3113   vectors[2].buffer = buffer + 5  + 12;
3114   vectors[2].size = 3;
3115
3116   file = g_file_new_tmp ("g_file_writev_XXXXXX",
3117                          &iostream, NULL);
3118   g_assert_nonnull (file);
3119   g_assert_nonnull (iostream);
3120
3121   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
3122
3123   g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data);
3124
3125   while (!data.done)
3126     g_main_context_iteration (NULL, TRUE);
3127
3128   g_assert_cmpuint (data.bytes_written, ==, sizeof buffer);
3129   g_assert_no_error (data.error);
3130
3131   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
3132   g_assert_no_error (error);
3133   g_assert_true (res);
3134   g_object_unref (iostream);
3135
3136   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
3137   g_assert_no_error (error);
3138   g_assert_true (res);
3139
3140   g_assert_cmpmem (contents, length, buffer, sizeof buffer);
3141
3142   g_free (contents);
3143
3144   g_file_delete (file, NULL, NULL);
3145   g_object_unref (file);
3146 }
3147
3148 /* Test that writev_async_all() on local file output streams handles cancellation correctly */
3149 static void
3150 test_writev_async_all_cancellation (void)
3151 {
3152   WritevAsyncData data = { 0 };
3153   GFile *file;
3154   GFileIOStream *iostream = NULL;
3155   GOutputVector vectors[3];
3156   const guint8 buffer[] = {1, 2, 3, 4, 5,
3157                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
3158                            1, 2, 3};
3159   GOutputStream *ostream;
3160   GError *error = NULL;
3161   gboolean res;
3162   guint8 *contents;
3163   gsize length;
3164   GCancellable *cancellable;
3165
3166   vectors[0].buffer = buffer;
3167   vectors[0].size = 5;
3168
3169   vectors[1].buffer = buffer + 5;
3170   vectors[1].size = 12;
3171
3172   vectors[2].buffer = buffer + 5  + 12;
3173   vectors[2].size = 3;
3174
3175   file = g_file_new_tmp ("g_file_writev_XXXXXX",
3176                          &iostream, NULL);
3177   g_assert_nonnull (file);
3178   g_assert_nonnull (iostream);
3179
3180   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
3181
3182   cancellable = g_cancellable_new ();
3183   g_cancellable_cancel (cancellable);
3184
3185   g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, cancellable, test_writev_all_cb, &data);
3186
3187   while (!data.done)
3188     g_main_context_iteration (NULL, TRUE);
3189
3190   g_assert_cmpuint (data.bytes_written, ==, 0);
3191   g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
3192   g_clear_error (&data.error);
3193
3194   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
3195   g_assert_no_error (error);
3196   g_assert_true (res);
3197   g_object_unref (iostream);
3198
3199   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
3200   g_assert_no_error (error);
3201   g_assert_true (res);
3202   g_assert_cmpuint (length, ==, 0);
3203
3204   g_free (contents);
3205
3206   g_file_delete (file, NULL, NULL);
3207   g_object_unref (file);
3208   g_object_unref (cancellable);
3209 }
3210
3211 /* Test that writev_async_all() with empty vectors is handled correctly */
3212 static void
3213 test_writev_async_all_empty_vectors (void)
3214 {
3215   WritevAsyncData data = { 0 };
3216   GFile *file;
3217   GFileIOStream *iostream = NULL;
3218   GOutputVector vectors[3];
3219   GOutputStream *ostream;
3220   GError *error = NULL;
3221   gboolean res;
3222   guint8 *contents;
3223   gsize length;
3224
3225   vectors[0].buffer = NULL;
3226   vectors[0].size = 0;
3227
3228   vectors[1].buffer = NULL;
3229   vectors[1].size = 0;
3230
3231   vectors[2].buffer = NULL;
3232   vectors[2].size = 0;
3233
3234   file = g_file_new_tmp ("g_file_writev_XXXXXX",
3235                          &iostream, NULL);
3236   g_assert_nonnull (file);
3237   g_assert_nonnull (iostream);
3238
3239   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
3240
3241   g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data);
3242
3243   while (!data.done)
3244     g_main_context_iteration (NULL, TRUE);
3245
3246   g_assert_cmpuint (data.bytes_written, ==, 0);
3247   g_assert_no_error (data.error);
3248   g_clear_error (&data.error);
3249
3250   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
3251   g_assert_no_error (error);
3252   g_assert_true (res);
3253   g_object_unref (iostream);
3254
3255   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
3256   g_assert_no_error (error);
3257   g_assert_true (res);
3258   g_assert_cmpuint (length, ==, 0);
3259
3260   g_free (contents);
3261
3262   g_file_delete (file, NULL, NULL);
3263   g_object_unref (file);
3264 }
3265
3266 /* Test that writev_async_all() with no vectors is handled correctly */
3267 static void
3268 test_writev_async_all_no_vectors (void)
3269 {
3270   WritevAsyncData data = { 0 };
3271   GFile *file;
3272   GFileIOStream *iostream = NULL;
3273   GOutputStream *ostream;
3274   GError *error = NULL;
3275   gboolean res;
3276   guint8 *contents;
3277   gsize length;
3278
3279   file = g_file_new_tmp ("g_file_writev_XXXXXX",
3280                          &iostream, NULL);
3281   g_assert_nonnull (file);
3282   g_assert_nonnull (iostream);
3283
3284   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
3285
3286   g_output_stream_writev_all_async (ostream, NULL, 0, 0, NULL, test_writev_all_cb, &data);
3287
3288   while (!data.done)
3289     g_main_context_iteration (NULL, TRUE);
3290
3291   g_assert_cmpuint (data.bytes_written, ==, 0);
3292   g_assert_no_error (data.error);
3293   g_clear_error (&data.error);
3294
3295   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
3296   g_assert_no_error (error);
3297   g_assert_true (res);
3298   g_object_unref (iostream);
3299
3300   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
3301   g_assert_no_error (error);
3302   g_assert_true (res);
3303   g_assert_cmpuint (length, ==, 0);
3304
3305   g_free (contents);
3306
3307   g_file_delete (file, NULL, NULL);
3308   g_object_unref (file);
3309 }
3310
3311 /* Test that writev_async_all() with too big vectors is handled correctly */
3312 static void
3313 test_writev_async_all_too_big_vectors (void)
3314 {
3315   WritevAsyncData data = { 0 };
3316   GFile *file;
3317   GFileIOStream *iostream = NULL;
3318   GOutputVector vectors[3];
3319   GOutputStream *ostream;
3320   GError *error = NULL;
3321   gboolean res;
3322   guint8 *contents;
3323   gsize length;
3324
3325   vectors[0].buffer = (void*) 1;
3326   vectors[0].size = G_MAXSIZE / 2;
3327
3328   vectors[1].buffer = (void*) 1;
3329   vectors[1].size = G_MAXSIZE / 2;
3330
3331   vectors[2].buffer = (void*) 1;
3332   vectors[2].size = G_MAXSIZE / 2;
3333
3334   file = g_file_new_tmp ("g_file_writev_XXXXXX",
3335                          &iostream, NULL);
3336   g_assert_nonnull (file);
3337   g_assert_nonnull (iostream);
3338
3339   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
3340
3341   g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data);
3342
3343   while (!data.done)
3344     g_main_context_iteration (NULL, TRUE);
3345
3346   g_assert_cmpuint (data.bytes_written, ==, 0);
3347   g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
3348   g_clear_error (&data.error);
3349
3350   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
3351   g_assert_no_error (error);
3352   g_assert_true (res);
3353   g_object_unref (iostream);
3354
3355   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
3356   g_assert_no_error (error);
3357   g_assert_true (res);
3358   g_assert_cmpuint (length, ==, 0);
3359
3360   g_free (contents);
3361
3362   g_file_delete (file, NULL, NULL);
3363   g_object_unref (file);
3364 }
3365
3366 static void
3367 test_build_attribute_list_for_copy (void)
3368 {
3369   GFile *tmpfile;
3370   GFileIOStream *iostream;
3371   GError *error = NULL;
3372   const GFileCopyFlags test_flags[] =
3373     {
3374       G_FILE_COPY_NONE,
3375       G_FILE_COPY_TARGET_DEFAULT_PERMS,
3376       G_FILE_COPY_ALL_METADATA,
3377       G_FILE_COPY_ALL_METADATA | G_FILE_COPY_TARGET_DEFAULT_PERMS,
3378     };
3379   gsize i;
3380   char *attrs;
3381   gchar *attrs_with_commas;
3382
3383   tmpfile = g_file_new_tmp ("tmp-build-attribute-list-for-copyXXXXXX",
3384                             &iostream, &error);
3385   g_assert_no_error (error);
3386   g_io_stream_close ((GIOStream*)iostream, NULL, &error);
3387   g_assert_no_error (error);
3388   g_clear_object (&iostream);
3389
3390   for (i = 0; i < G_N_ELEMENTS (test_flags); i++)
3391     {
3392       GFileCopyFlags flags = test_flags[i];
3393
3394       attrs = g_file_build_attribute_list_for_copy (tmpfile, flags, NULL, &error);
3395       g_test_message ("Attributes for copy: %s", attrs);
3396       g_assert_no_error (error);
3397       g_assert_nonnull (attrs);
3398       attrs_with_commas = g_strconcat (",", attrs, ",", NULL);
3399       g_free (attrs);
3400
3401       /* See g_local_file_class_init for reference. */
3402       if (flags & G_FILE_COPY_TARGET_DEFAULT_PERMS)
3403         g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_MODE ","));
3404       else
3405         g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_MODE ","));
3406 #ifdef G_OS_UNIX
3407       if (flags & G_FILE_COPY_ALL_METADATA)
3408         {
3409           g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_UID ","));
3410           g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_GID ","));
3411         }
3412       else
3413         {
3414           g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_UID ","));
3415           g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_GID ","));
3416         }
3417 #endif
3418 #ifdef HAVE_UTIMES
3419       g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_MODIFIED ","));
3420       g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC ","));
3421       if (flags & G_FILE_COPY_ALL_METADATA)
3422         {
3423           g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS ","));
3424           g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS_USEC ","));
3425         }
3426       else
3427         {
3428           g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS ","));
3429           g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS_USEC ","));
3430         }
3431 #endif
3432 #ifdef HAVE_UTIMENSAT
3433       g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_MODIFIED ","));
3434       g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC ","));
3435       if (flags & G_FILE_COPY_ALL_METADATA)
3436         {
3437           g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS ","));
3438           g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC ","));
3439         }
3440       else
3441         {
3442           g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS ","));
3443           g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC ","));
3444         }
3445 #endif
3446       g_free (attrs_with_commas);
3447     }
3448
3449   (void) g_file_delete (tmpfile, NULL, NULL);
3450   g_clear_object (&tmpfile);
3451 }
3452
3453 typedef struct
3454 {
3455   GError *error;
3456   gboolean done;
3457   gboolean res;
3458 } MoveAsyncData;
3459
3460 static void
3461 test_move_async_cb (GObject      *object,
3462                     GAsyncResult *result,
3463                     gpointer      user_data)
3464 {
3465   GFile *file = G_FILE (object);
3466   MoveAsyncData *data = user_data;
3467   GError *error = NULL;
3468
3469   data->res = g_file_move_finish (file, result, &error);
3470   data->error = error;
3471   data->done = TRUE;
3472 }
3473
3474 typedef struct
3475 {
3476   goffset total_num_bytes;
3477 } MoveAsyncProgressData;
3478
3479 static void
3480 test_move_async_progress_cb (goffset  current_num_bytes,
3481                              goffset  total_num_bytes,
3482                              gpointer user_data)
3483 {
3484   MoveAsyncProgressData *data = user_data;
3485   data->total_num_bytes = total_num_bytes;
3486 }
3487
3488 /* Test that move_async() moves the file correctly */
3489 static void
3490 test_move_async (void)
3491 {
3492   MoveAsyncData data = { 0 };
3493   MoveAsyncProgressData progress_data = { 0 };
3494   GFile *source;
3495   GFileIOStream *iostream;
3496   GOutputStream *ostream;
3497   GFile *destination;
3498   gchar *destination_path;
3499   GError *error = NULL;
3500   gboolean res;
3501   const guint8 buffer[] = {1, 2, 3, 4, 5};
3502
3503   source = g_file_new_tmp ("g_file_move_XXXXXX", &iostream, NULL);
3504
3505   destination_path = g_build_path (G_DIR_SEPARATOR_S, g_get_tmp_dir (), "g_file_move_target", NULL);
3506   destination = g_file_new_for_path (destination_path);
3507
3508   g_assert_nonnull (source);
3509   g_assert_nonnull (iostream);
3510
3511   res = g_file_query_exists (source, NULL);
3512   g_assert_true (res);
3513   res = g_file_query_exists (destination, NULL);
3514   g_assert_false (res);
3515
3516   // Write a known amount of bytes to the file, so we can test the progress callback against it
3517   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
3518   g_output_stream_write (ostream, buffer, sizeof (buffer), NULL, &error);
3519   g_assert_no_error (error);
3520
3521   g_file_move_async (source,
3522                      destination,
3523                      G_FILE_COPY_NONE,
3524                      0,
3525                      NULL,
3526                      test_move_async_progress_cb,
3527                      &progress_data,
3528                      test_move_async_cb,
3529                      &data);
3530
3531   while (!data.done)
3532     g_main_context_iteration (NULL, TRUE);
3533
3534   g_assert_no_error (data.error);
3535   g_assert_true (data.res);
3536   g_assert_cmpuint (progress_data.total_num_bytes, ==, sizeof (buffer));
3537
3538   res = g_file_query_exists (source, NULL);
3539   g_assert_false (res);
3540   res = g_file_query_exists (destination, NULL);
3541   g_assert_true (res);
3542
3543   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
3544   g_assert_no_error (error);
3545   g_assert_true (res);
3546   g_object_unref (iostream);
3547
3548   res = g_file_delete (destination, NULL, &error);
3549   g_assert_no_error (error);
3550   g_assert_true (res);
3551
3552   g_object_unref (source);
3553   g_object_unref (destination);
3554
3555   g_free (destination_path);
3556 }
3557
3558 static GAppInfo *
3559 create_command_line_app_info (const char *name,
3560                               const char *command_line,
3561                               const char *default_for_type)
3562 {
3563   GAppInfo *info;
3564   GError *error = NULL;
3565
3566   info = g_app_info_create_from_commandline (command_line,
3567                                              name,
3568                                              G_APP_INFO_CREATE_NONE,
3569                                              &error);
3570   g_assert_no_error (error);
3571
3572   g_app_info_set_as_default_for_type (info, default_for_type, &error);
3573   g_assert_no_error (error);
3574
3575   return g_steal_pointer (&info);
3576 }
3577
3578 static void
3579 test_query_default_handler_uri (void)
3580 {
3581   GError *error = NULL;
3582   GAppInfo *info;
3583   GAppInfo *default_info;
3584   GFile *file;
3585   GFile *invalid_file;
3586
3587 #if defined(G_OS_WIN32) || defined(__APPLE__)
3588   g_test_skip ("Default URI handlers are not currently supported on Windows or macOS");
3589   return;
3590 #endif
3591
3592   info = create_command_line_app_info ("Gio File Handler", "true",
3593                                        "x-scheme-handler/gio-file");
3594   g_assert_true (G_IS_APP_INFO (info));
3595
3596   file = g_file_new_for_uri ("gio-file://hello-gio!");
3597   default_info = g_file_query_default_handler (file, NULL, &error);
3598   g_assert_no_error (error);
3599   g_assert_true (g_app_info_equal (default_info, info));
3600
3601   invalid_file = g_file_new_for_uri ("gio-file-INVALID://goodbye-gio!");
3602   g_assert_null (g_file_query_default_handler (invalid_file, NULL, &error));
3603   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
3604   g_clear_error (&error);
3605
3606   g_app_info_remove_supports_type (info, "x-scheme-handler/gio-file", &error);
3607   g_assert_no_error (error);
3608   g_app_info_reset_type_associations ("x-scheme-handler/gio-file");
3609
3610   g_object_unref (default_info);
3611   g_object_unref (info);
3612   g_object_unref (file);
3613   g_object_unref (invalid_file);
3614 }
3615
3616 static void
3617 test_query_zero_length_content_type (void)
3618 {
3619   GFile *empty_file;
3620   GFileInfo *file_info;
3621   GError *error = NULL;
3622   GFileIOStream *iostream;
3623
3624   g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=755795");
3625   /* Historically, GLib used to explicitly consider zero-size files as text/plain,
3626    * so they opened in a text editor. In 2.76, we changed that to application/x-zerosize,
3627    * because that’s what xdgmime uses:
3628    * - https://gitlab.gnome.org/GNOME/glib/-/blob/2.74.0/gio/glocalfileinfo.c#L1360-1369
3629    * - https://bugzilla.gnome.org/show_bug.cgi?id=755795
3630    * - https://gitlab.gnome.org/GNOME/glib/-/issues/2777
3631    */
3632   g_test_summary ("empty files should always be considered application/x-zerosize");
3633
3634   empty_file = g_file_new_tmp ("empty-file-XXXXXX", &iostream, &error);
3635   g_assert_no_error (error);
3636
3637   g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
3638   g_assert_no_error (error);
3639   g_clear_object (&iostream);
3640
3641   file_info =
3642     g_file_query_info (empty_file,
3643                        G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
3644                        G_FILE_QUERY_INFO_NONE,
3645                        NULL, &error);
3646   g_assert_no_error (error);
3647
3648 #ifndef __APPLE__
3649   g_assert_cmpstr (g_file_info_get_content_type (file_info), ==, "application/x-zerosize");
3650 #else
3651   g_assert_cmpstr (g_file_info_get_content_type (file_info), ==, "public.text");
3652 #endif
3653
3654   g_clear_object (&file_info);
3655   g_clear_object (&empty_file);
3656 }
3657
3658 static void
3659 test_query_default_handler_file (void)
3660 {
3661   GError *error = NULL;
3662   GAppInfo *info;
3663   GAppInfo *default_info;
3664   GFile *text_file;
3665   GFile *binary_file;
3666   GFile *invalid_file;
3667   GFileIOStream *iostream;
3668   GOutputStream *output_stream;
3669   const char buffer[] = "Text file!\n";
3670   const guint8 binary_buffer[] = "\xde\xad\xbe\xff";
3671
3672 #if defined(G_OS_WIN32) || defined(__APPLE__)
3673   g_test_skip ("Default URI handlers are not currently supported on Windows or macOS");
3674   return;
3675 #endif
3676
3677   text_file = g_file_new_tmp ("query-default-handler-XXXXXX", &iostream, &error);
3678   g_assert_no_error (error);
3679
3680   output_stream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
3681   g_output_stream_write_all (output_stream, buffer, G_N_ELEMENTS (buffer) - 1,
3682                              NULL, NULL, &error);
3683   g_assert_no_error (error);
3684
3685   g_output_stream_flush (output_stream, NULL, &error);
3686   g_assert_no_error (error);
3687
3688   g_output_stream_close (output_stream, NULL, &error);
3689   g_assert_no_error (error);
3690   g_clear_object (&iostream);
3691
3692   info = create_command_line_app_info ("Text handler", "true", "text/plain");
3693   g_assert_true (G_IS_APP_INFO (info));
3694
3695   default_info = g_file_query_default_handler (text_file, NULL, &error);
3696   g_assert_no_error (error);
3697   g_assert_true (g_app_info_equal (default_info, info));
3698
3699   invalid_file = g_file_new_for_path ("/hopefully/this-does-not-exists");
3700   g_assert_null (g_file_query_default_handler (invalid_file, NULL, &error));
3701   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
3702   g_clear_error (&error);
3703
3704   binary_file = g_file_new_tmp ("query-default-handler-bin-XXXXXX", &iostream, &error);
3705   g_assert_no_error (error);
3706
3707   output_stream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
3708   g_output_stream_write_all (output_stream, binary_buffer,
3709                              G_N_ELEMENTS (binary_buffer),
3710                              NULL, NULL, &error);
3711   g_assert_no_error (error);
3712
3713   g_output_stream_flush (output_stream, NULL, &error);
3714   g_assert_no_error (error);
3715
3716   g_output_stream_close (output_stream, NULL, &error);
3717   g_assert_no_error (error);
3718   g_clear_object (&iostream);
3719
3720   g_assert_null (g_file_query_default_handler (binary_file, NULL, &error));
3721   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
3722   g_clear_error (&error);
3723
3724   g_app_info_remove_supports_type (info, "text/plain", &error);
3725   g_assert_no_error (error);
3726   g_app_info_reset_type_associations ("text/plain");
3727
3728   g_object_unref (default_info);
3729   g_object_unref (info);
3730   g_object_unref (text_file);
3731   g_object_unref (binary_file);
3732   g_object_unref (invalid_file);
3733 }
3734
3735 typedef struct {
3736   GMainLoop *loop;
3737   GAppInfo *info;
3738   GError *error;
3739 } QueryDefaultHandlerData;
3740
3741 static void
3742 on_query_default (GObject      *source,
3743                   GAsyncResult *result,
3744                   gpointer      user_data)
3745 {
3746   QueryDefaultHandlerData *data = user_data;
3747
3748   data->info = g_file_query_default_handler_finish (G_FILE (source), result,
3749                                                     &data->error);
3750   g_main_loop_quit (data->loop);
3751 }
3752
3753 static void
3754 test_query_default_handler_file_async (void)
3755 {
3756   QueryDefaultHandlerData data = {0};
3757   GCancellable *cancellable;
3758   GAppInfo *info;
3759   GFile *text_file;
3760   GFile *binary_file;
3761   GFile *invalid_file;
3762   GFileIOStream *iostream;
3763   GOutputStream *output_stream;
3764   const char buffer[] = "Text file!\n";
3765   const guint8 binary_buffer[] = "\xde\xad\xbe\xff";
3766   GError *error = NULL;
3767
3768 #if defined(G_OS_WIN32) || defined(__APPLE__)
3769   g_test_skip ("Default URI handlers are not currently supported on Windows or macOS");
3770   return;
3771 #endif
3772
3773   data.loop = g_main_loop_new (NULL, FALSE);
3774
3775   text_file = g_file_new_tmp ("query-default-handler-XXXXXX", &iostream, &error);
3776   g_assert_no_error (error);
3777
3778   output_stream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
3779   g_output_stream_write_all (output_stream, buffer, G_N_ELEMENTS (buffer) - 1,
3780                              NULL, NULL, &error);
3781   g_assert_no_error (error);
3782
3783   g_output_stream_close (output_stream, NULL, &error);
3784   g_assert_no_error (error);
3785   g_clear_object (&iostream);
3786
3787   info = create_command_line_app_info ("Text handler", "true", "text/plain");
3788   g_assert_true (G_IS_APP_INFO (info));
3789
3790   g_file_query_default_handler_async (text_file, G_PRIORITY_DEFAULT,
3791                                       NULL, on_query_default,
3792                                       &data);
3793   g_main_loop_run (data.loop);
3794   g_assert_no_error (data.error);
3795   g_assert_true (g_app_info_equal (data.info, info));
3796   g_clear_object (&data.info);
3797
3798   invalid_file = g_file_new_for_path ("/hopefully/this/.file/does-not-exists");
3799   g_file_query_default_handler_async (invalid_file, G_PRIORITY_DEFAULT,
3800                                       NULL, on_query_default,
3801                                       &data);
3802   g_main_loop_run (data.loop);
3803   g_assert_null (data.info);
3804   g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
3805   g_clear_error (&data.error);
3806
3807   cancellable = g_cancellable_new ();
3808   g_file_query_default_handler_async (text_file, G_PRIORITY_DEFAULT,
3809                                       cancellable, on_query_default,
3810                                       &data);
3811   g_cancellable_cancel (cancellable);
3812   g_main_loop_run (data.loop);
3813   g_assert_null (data.info);
3814   g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
3815   g_clear_error (&data.error);
3816
3817   binary_file = g_file_new_tmp ("query-default-handler-bin-XXXXXX", &iostream, &error);
3818   g_assert_no_error (error);
3819
3820   output_stream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
3821   g_output_stream_write_all (output_stream, binary_buffer,
3822                              G_N_ELEMENTS (binary_buffer),
3823                              NULL, NULL, &error);
3824   g_assert_no_error (error);
3825
3826   g_output_stream_close (output_stream, NULL, &error);
3827   g_assert_no_error (error);
3828   g_clear_object (&iostream);
3829
3830   g_file_query_default_handler_async (binary_file, G_PRIORITY_DEFAULT,
3831                                       NULL, on_query_default,
3832                                       &data);
3833   g_main_loop_run (data.loop);
3834   g_assert_null (data.info);
3835   g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
3836   g_clear_error (&data.error);
3837
3838   g_app_info_remove_supports_type (info, "text/plain", &error);
3839   g_assert_no_error (error);
3840   g_app_info_reset_type_associations ("text/plain");
3841
3842   g_main_loop_unref (data.loop);
3843   g_object_unref (info);
3844   g_object_unref (text_file);
3845   g_object_unref (binary_file);
3846   g_object_unref (invalid_file);
3847 }
3848
3849 static void
3850 test_query_default_handler_uri_async (void)
3851 {
3852   QueryDefaultHandlerData data = {0};
3853   GCancellable *cancellable;
3854   GAppInfo *info;
3855   GFile *file;
3856   GFile *invalid_file;
3857
3858 #if defined(G_OS_WIN32) || defined(__APPLE__)
3859   g_test_skip ("Default URI handlers are not currently supported on Windows or macOS");
3860   return;
3861 #endif
3862
3863   info = create_command_line_app_info ("Gio File Handler", "true",
3864                                        "x-scheme-handler/gio-file");
3865   g_assert_true (G_IS_APP_INFO (info));
3866
3867   data.loop = g_main_loop_new (NULL, FALSE);
3868
3869   file = g_file_new_for_uri ("gio-file://hello-gio!");
3870   g_file_query_default_handler_async (file, G_PRIORITY_DEFAULT,
3871                                       NULL, on_query_default,
3872                                       &data);
3873   g_main_loop_run (data.loop);
3874   g_assert_no_error (data.error);
3875   g_assert_true (g_app_info_equal (data.info, info));
3876   g_clear_object (&data.info);
3877
3878   invalid_file = g_file_new_for_uri ("gio-file-INVALID://goodbye-gio!");
3879   g_file_query_default_handler_async (invalid_file, G_PRIORITY_DEFAULT,
3880                                       NULL, on_query_default,
3881                                       &data);
3882   g_main_loop_run (data.loop);
3883   g_assert_null (data.info);
3884   g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
3885   g_clear_error (&data.error);
3886
3887   cancellable = g_cancellable_new ();
3888   g_file_query_default_handler_async (file, G_PRIORITY_DEFAULT,
3889                                       cancellable, on_query_default,
3890                                       &data);
3891   g_cancellable_cancel (cancellable);
3892   g_main_loop_run (data.loop);
3893   g_assert_null (data.info);
3894   g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
3895   g_clear_error (&data.error);
3896
3897   g_app_info_remove_supports_type (info, "x-scheme-handler/gio-file", &data.error);
3898   g_assert_no_error (data.error);
3899   g_app_info_reset_type_associations ("x-scheme-handler/gio-file");
3900
3901   g_main_loop_unref (data.loop);
3902   g_object_unref (info);
3903   g_object_unref (file);
3904   g_object_unref (invalid_file);
3905 }
3906
3907 static void
3908 test_enumerator_cancellation (void)
3909 {
3910   GCancellable *cancellable;
3911   GFileEnumerator *enumerator;
3912   GFileInfo *info;
3913   GFile *dir;
3914   GError *error = NULL;
3915
3916   dir = g_file_new_for_path (g_get_tmp_dir ());
3917   g_assert_nonnull (dir);
3918
3919   enumerator = g_file_enumerate_children (dir,
3920                                           G_FILE_ATTRIBUTE_STANDARD_NAME,
3921                                           G_FILE_QUERY_INFO_NONE,
3922                                           NULL,
3923                                           &error);
3924   g_assert_nonnull (enumerator);
3925
3926   cancellable = g_cancellable_new ();
3927   g_cancellable_cancel (cancellable);
3928   info = g_file_enumerator_next_file (enumerator, cancellable, &error);
3929   g_assert_null (info);
3930   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
3931
3932   g_error_free (error);
3933   g_object_unref (cancellable);
3934   g_object_unref (enumerator);
3935   g_object_unref (dir);
3936 }
3937
3938 static void
3939 test_from_uri_ignores_fragment (void)
3940 {
3941   GFile *file;
3942   gchar *path;
3943   file = g_file_new_for_uri ("file:///tmp/foo#bar");
3944   path = g_file_get_path (file);
3945 #ifdef G_OS_WIN32
3946   g_assert_cmpstr (path, ==, "\\tmp\\foo");
3947 #else
3948   g_assert_cmpstr (path, ==, "/tmp/foo");
3949 #endif
3950   g_free (path);
3951   g_object_unref (file);
3952 }
3953
3954 static void
3955 test_from_uri_ignores_query_string (void)
3956 {
3957   GFile *file;
3958   gchar *path;
3959   file = g_file_new_for_uri ("file:///tmp/foo?bar");
3960   path = g_file_get_path (file);
3961 #ifdef G_OS_WIN32
3962   g_assert_cmpstr (path, ==, "\\tmp\\foo");
3963 #else
3964   g_assert_cmpstr (path, ==, "/tmp/foo");
3965 #endif
3966   g_free (path);
3967   g_object_unref (file);
3968 }
3969
3970 int
3971 main (int argc, char *argv[])
3972 {
3973   setlocale (LC_ALL, "");
3974
3975   g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
3976
3977   g_test_add_func ("/file/basic", test_basic);
3978   g_test_add_func ("/file/build-filename", test_build_filename);
3979   g_test_add_func ("/file/build-filenamev", test_build_filenamev);
3980   g_test_add_func ("/file/parent", test_parent);
3981   g_test_add_func ("/file/child", test_child);
3982   g_test_add_func ("/file/empty-path", test_empty_path);
3983   g_test_add_func ("/file/type", test_type);
3984   g_test_add_func ("/file/parse-name", test_parse_name);
3985   g_test_add_data_func ("/file/async-create-delete/0", GINT_TO_POINTER (0), test_create_delete);
3986   g_test_add_data_func ("/file/async-create-delete/1", GINT_TO_POINTER (1), test_create_delete);
3987   g_test_add_data_func ("/file/async-create-delete/10", GINT_TO_POINTER (10), test_create_delete);
3988   g_test_add_data_func ("/file/async-create-delete/25", GINT_TO_POINTER (25), test_create_delete);
3989   g_test_add_data_func ("/file/async-create-delete/4096", GINT_TO_POINTER (4096), test_create_delete);
3990   g_test_add_func ("/file/replace-load", test_replace_load);
3991   g_test_add_func ("/file/replace-cancel", test_replace_cancel);
3992   g_test_add_func ("/file/replace-symlink", test_replace_symlink);
3993   g_test_add_func ("/file/replace-symlink/using-etag", test_replace_symlink_using_etag);
3994   g_test_add_data_func ("/file/replace/write-only", GUINT_TO_POINTER (FALSE), test_replace);
3995   g_test_add_data_func ("/file/replace/read-write", GUINT_TO_POINTER (TRUE), test_replace);
3996   g_test_add_func ("/file/async-new-tmp", test_async_new_tmp);
3997   g_test_add_func ("/file/async-new-tmp-dir", test_async_new_tmp_dir);
3998   g_test_add_func ("/file/async-delete", test_async_delete);
3999   g_test_add_func ("/file/async-make-symlink", test_async_make_symlink);
4000   g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode);
4001   g_test_add_func ("/file/copy/progress", test_copy_progress);
4002   g_test_add_func ("/file/measure", test_measure);
4003   g_test_add_func ("/file/measure-async", test_measure_async);
4004   g_test_add_func ("/file/load-bytes", test_load_bytes);
4005   g_test_add_func ("/file/load-bytes-async", test_load_bytes_async);
4006   g_test_add_func ("/file/writev", test_writev);
4007   g_test_add_func ("/file/writev/no-bytes-written", test_writev_no_bytes_written);
4008   g_test_add_func ("/file/writev/no-vectors", test_writev_no_vectors);
4009   g_test_add_func ("/file/writev/empty-vectors", test_writev_empty_vectors);
4010   g_test_add_func ("/file/writev/too-big-vectors", test_writev_too_big_vectors);
4011   g_test_add_func ("/file/writev/async", test_writev_async);
4012   g_test_add_func ("/file/writev/async_all", test_writev_async_all);
4013   g_test_add_func ("/file/writev/async_all-empty-vectors", test_writev_async_all_empty_vectors);
4014   g_test_add_func ("/file/writev/async_all-no-vectors", test_writev_async_all_no_vectors);
4015   g_test_add_func ("/file/writev/async_all-to-big-vectors", test_writev_async_all_too_big_vectors);
4016   g_test_add_func ("/file/writev/async_all-cancellation", test_writev_async_all_cancellation);
4017   g_test_add_func ("/file/build-attribute-list-for-copy", test_build_attribute_list_for_copy);
4018   g_test_add_func ("/file/move_async", test_move_async);
4019   g_test_add_func ("/file/query-zero-length-content-type", test_query_zero_length_content_type);
4020   g_test_add_func ("/file/query-default-handler-file", test_query_default_handler_file);
4021   g_test_add_func ("/file/query-default-handler-file-async", test_query_default_handler_file_async);
4022   g_test_add_func ("/file/query-default-handler-uri", test_query_default_handler_uri);
4023   g_test_add_func ("/file/query-default-handler-uri-async", test_query_default_handler_uri_async);
4024   g_test_add_func ("/file/enumerator-cancellation", test_enumerator_cancellation);
4025   g_test_add_func ("/file/from-uri/ignores-query-string", test_from_uri_ignores_query_string);
4026   g_test_add_func ("/file/from-uri/ignores-fragment", test_from_uri_ignores_fragment);
4027
4028   return g_test_run ();
4029 }
4030