diff options
Diffstat (limited to 'gio/tests/file.c')
-rw-r--r-- | gio/tests/file.c | 977 |
1 files changed, 844 insertions, 133 deletions
diff --git a/gio/tests/file.c b/gio/tests/file.c index a849e83cf..754c6c326 100644 --- a/gio/tests/file.c +++ b/gio/tests/file.c @@ -1,3 +1,5 @@ +#include "config.h" + #include <locale.h> #include <string.h> #include <stdio.h> @@ -8,6 +10,12 @@ #include <sys/stat.h> #endif +typedef struct +{ + GMainLoop *loop; + GError **error; +} AsyncErrorData; + static void test_basic_for_file (GFile *file, const gchar *suffix) @@ -444,15 +452,13 @@ created_cb (GObject *source, data); } -static gboolean +static void stop_timeout (gpointer user_data) { CreateDeleteData *data = user_data; data->timed_out = TRUE; g_main_context_wakeup (data->context); - - return G_SOURCE_REMOVE; } /* @@ -510,7 +516,7 @@ test_create_delete (gconstpointer d) /* Use the global default main context */ data->context = NULL; - data->timeout = g_timeout_add_seconds (10, stop_timeout, data); + data->timeout = g_timeout_add_seconds_once (10, stop_timeout, data); g_file_create_async (data->file, 0, 0, NULL, created_cb, data); @@ -852,6 +858,18 @@ test_replace_symlink (void) g_test_message ("Using temporary directory %s", tmpdir_path); g_free (tmpdir_path); + source_file = g_file_get_child (tmpdir, "source"); + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*symlink_value*failed*"); + g_assert_false (g_file_make_symbolic_link (source_file, NULL, NULL, &local_error)); + g_assert_no_error (local_error); + g_test_assert_expected_messages (); + + g_assert_false (g_file_make_symbolic_link (source_file, "", NULL, &local_error)); + g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_clear_object (&source_file); + g_clear_error (&local_error); + /* Create symlink `source` which points to `target`. */ source_file = g_file_get_child (tmpdir, "source"); target_file = g_file_get_child (tmpdir, "target"); @@ -1290,6 +1308,65 @@ check_test_file (GFile *test_file, #endif /* __linux__ */ +#ifdef __linux__ +/* + * check_cap_dac_override: + * @tmpdir: A temporary directory in which we can create and delete files + * + * Check whether the current process can bypass DAC permissions. + * + * Traditionally, "privileged" processes (those with effective uid 0) + * could do this (and bypass many other checks), and "unprivileged" + * processes could not. + * + * In Linux, the special powers of euid 0 are divided into many + * capabilities: see `capabilities(7)`. The one we are interested in + * here is `CAP_DAC_OVERRIDE`. + * + * We do this generically instead of actually looking at the capability + * bits, so that the right thing will happen on non-Linux Unix + * implementations, in particular if they have something equivalent to + * but not identical to Linux permissions. + * + * Returns: %TRUE if we have Linux `CAP_DAC_OVERRIDE` or equivalent + * privileges + */ +static gboolean +check_cap_dac_override (const char *tmpdir) +{ + gchar *dac_denies_write; + gchar *inside; + gboolean have_cap; + + dac_denies_write = g_build_filename (tmpdir, "dac-denies-write", NULL); + inside = g_build_filename (dac_denies_write, "inside", NULL); + + g_assert_no_errno (mkdir (dac_denies_write, S_IRWXU)); + g_assert_no_errno (chmod (dac_denies_write, 0)); + + if (mkdir (inside, S_IRWXU) == 0) + { + g_test_message ("Looks like we have CAP_DAC_OVERRIDE or equivalent"); + g_assert_no_errno (rmdir (inside)); + have_cap = TRUE; + } + else + { + int saved_errno = errno; + + g_test_message ("We do not have CAP_DAC_OVERRIDE or equivalent"); + g_assert_cmpint (saved_errno, ==, EACCES); + have_cap = FALSE; + } + + g_assert_no_errno (chmod (dac_denies_write, S_IRWXU)); + g_assert_no_errno (rmdir (dac_denies_write)); + g_free (dac_denies_write); + g_free (inside); + return have_cap; +} +#endif + /* A big test for g_file_replace() and g_file_replace_readwrite(). The * @test_data is a boolean: %TRUE to test g_file_replace_readwrite(), %FALSE to * test g_file_replace(). The test setup and checks are identical for both @@ -1330,6 +1407,7 @@ test_replace (gconstpointer test_data) guint setup_source_mode; FileTestSetupType setup_backup_type; guint setup_backup_mode; + gboolean skip_if_cap_dac_override; /* Expected results. */ gboolean expected_success; @@ -1353,7 +1431,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, - FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, + FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE, TRUE, 0, 0, 1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL, @@ -1361,7 +1439,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1369,7 +1447,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1377,7 +1455,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY, 2, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1385,7 +1463,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE, 2, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1393,7 +1471,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, 3, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target", FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1401,7 +1479,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, 3, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target", FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1412,7 +1490,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_NONE, "incorrect etag", FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, FALSE, G_IO_ERROR, G_IO_ERROR_WRONG_ETAG, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1424,7 +1502,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, - FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, + FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE, TRUE, 0, 0, 1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL, @@ -1432,7 +1510,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, NULL, @@ -1440,7 +1518,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, @@ -1448,7 +1526,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY, 2, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1456,7 +1534,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE, 2, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1464,7 +1542,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, /* The final situation here is a bit odd; the backup file is a bit * pointless as the original source file was a dangling symlink. @@ -1480,7 +1558,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, /* FIXME: The permissions for the backup file are just the default umask, * but should probably be the same as the permissions for the source @@ -1496,7 +1574,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, + FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, @@ -1504,7 +1582,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, @@ -1512,7 +1590,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, @@ -1520,7 +1598,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_DIRECTORY, 0, + FILE_TEST_SETUP_TYPE_DIRECTORY, 0, FALSE, FALSE, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL, @@ -1528,7 +1606,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, + FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, @@ -1536,7 +1614,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, + FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, @@ -1544,7 +1622,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, + FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, FALSE, TRUE, 0, 0, /* the third file is `source~-target`, the original target of the old * backup symlink */ @@ -1558,7 +1636,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, - FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, + FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE, TRUE, 0, 0, 1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL, @@ -1566,7 +1644,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1574,7 +1652,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1582,7 +1660,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY, 2, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1590,7 +1668,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE, 2, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1598,7 +1676,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1606,7 +1684,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, /* the third file is `source-target`, the original target of the old * source file */ @@ -1620,7 +1698,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_REPLACE_DESTINATION, "incorrect etag", FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, FALSE, G_IO_ERROR, G_IO_ERROR_WRONG_ETAG, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1633,7 +1711,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, - FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, + FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE, TRUE, 0, 0, 1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL, @@ -1641,7 +1719,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, NULL, @@ -1649,7 +1727,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, @@ -1657,7 +1735,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY, 2, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1665,7 +1743,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE, 2, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents, @@ -1673,7 +1751,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, "source-target", @@ -1681,7 +1759,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, /* the third file is `source-target`, the original target of the old * source file */ @@ -1696,7 +1774,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, + FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, @@ -1704,7 +1782,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, @@ -1712,7 +1790,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, + FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, @@ -1720,7 +1798,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_DIRECTORY, 0, + FILE_TEST_SETUP_TYPE_DIRECTORY, 0, FALSE, FALSE, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL, @@ -1728,7 +1806,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, + FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, @@ -1736,7 +1814,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, + FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, FALSE, TRUE, 0, 0, 2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents, @@ -1744,7 +1822,7 @@ test_replace (gconstpointer test_data) { TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, + FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, FALSE, TRUE, 0, 0, /* the third file is `source~-target`, the original target of the old * backup symlink */ @@ -1756,7 +1834,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_PRIVATE, NULL, FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, - FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, + FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE, TRUE, 0, 0, 1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_private_mode, new_contents, FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL, @@ -1764,7 +1842,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_PRIVATE, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, + FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE, TRUE, 0, 0, /* the file isn’t being replaced, so it should keep its existing permissions */ 1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents, @@ -1773,7 +1851,7 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, - FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, + FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE, TRUE, 0, 0, 1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_private_mode, new_contents, FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL, @@ -1781,18 +1859,21 @@ test_replace (gconstpointer test_data) { FALSE, G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION, NULL, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, - FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, + FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE, TRUE, 0, 0, 1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_private_mode, new_contents, FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL, }, /* make the initial source file unreadable, so the replace operation - * should fail */ + * should fail + * + * Permissions are ignored if we have CAP_DAC_OVERRIDE or equivalent, + * and in particular if we're root. In this scenario,we need to skip it */ { FALSE, G_FILE_CREATE_NONE, NULL, FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, 0 /* most restrictive permissions */, - FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, + FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, TRUE, FALSE, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, 1, FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, 0, NULL, FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL, @@ -1824,6 +1905,19 @@ test_replace (gconstpointer test_data) tmpdir = g_file_new_for_path (tmpdir_path); g_test_message ("Test %" G_GSIZE_FORMAT ", using temporary directory %s", i, tmpdir_path); + + if (tests[i].skip_if_cap_dac_override && check_cap_dac_override (tmpdir_path)) + { + g_test_message ("Skipping test as process has CAP_DAC_OVERRIDE capability and the test checks permissions"); + + g_file_delete (tmpdir, NULL, &local_error); + g_assert_no_error (local_error); + g_clear_object (&tmpdir); + g_free (tmpdir_path); + + continue; + } + g_free (tmpdir_path); /* Set up the test directory. */ @@ -1959,6 +2053,200 @@ test_replace (gconstpointer test_data) } static void +on_new_tmp_done (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GFile *file; + GFile *parent; + GFileInfo *info; + GFileIOStream *iostream; + GError *error = NULL; + GMainLoop *loop = user_data; + gchar *basename; + GFile *tmpdir = NULL; + + g_assert_null (object); + + file = g_file_new_tmp_finish (result, &iostream, &error); + g_assert_no_error (error); + + g_assert_true (g_file_query_exists (file, NULL)); + + basename = g_file_get_basename (file); + g_assert_true (g_str_has_prefix (basename, "g_file_new_tmp_async_")); + + info = g_file_io_stream_query_info (iostream, G_FILE_ATTRIBUTE_STANDARD_TYPE, + NULL, &error); + g_assert_no_error (error); + + g_assert_cmpuint (g_file_info_get_file_type (info), ==, G_FILE_TYPE_REGULAR); + g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + + parent = g_file_get_parent (file); + tmpdir = g_file_new_for_path (g_get_tmp_dir ()); + + g_assert_true (g_file_equal (tmpdir, parent)); + + g_main_loop_quit (loop); + + g_object_unref (file); + g_object_unref (parent); + g_object_unref (iostream); + g_object_unref (info); + g_free (basename); + g_object_unref (tmpdir); +} + +static void +on_new_tmp_error (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GFileIOStream *iostream = (GFileIOStream*) &on_new_tmp_error; + AsyncErrorData *error_data = user_data; + + g_assert_null (object); + + g_assert_null (g_file_new_tmp_finish (result, &iostream, error_data->error)); + g_assert_nonnull (error_data->error); + g_assert_null (iostream); + + g_main_loop_quit (error_data->loop); +} + +static void +test_async_new_tmp (void) +{ + GMainLoop *loop; + GError *error = NULL; + GCancellable *cancellable; + AsyncErrorData error_data = { .error = &error }; + + loop = g_main_loop_new (NULL, TRUE); + error_data.loop = loop; + + g_file_new_tmp_async ("g_file_new_tmp_async_XXXXXX", + G_PRIORITY_DEFAULT, NULL, + on_new_tmp_done, loop); + g_main_loop_run (loop); + + g_file_new_tmp_async ("g_file_new_tmp_async_invalid_template", + G_PRIORITY_DEFAULT, NULL, + on_new_tmp_error, &error_data); + g_main_loop_run (loop); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_clear_error (&error); + + cancellable = g_cancellable_new (); + g_file_new_tmp_async ("g_file_new_tmp_async_cancelled_XXXXXX", + G_PRIORITY_DEFAULT, cancellable, + on_new_tmp_error, &error_data); + g_cancellable_cancel (cancellable); + g_main_loop_run (loop); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_clear_object (&cancellable); + g_clear_error (&error); + + g_main_loop_unref (loop); +} + +static void +on_new_tmp_dir_done (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GFile *file; + GFile *parent; + GFileInfo *info; + GError *error = NULL; + GMainLoop *loop = user_data; + gchar *basename; + GFile *tmpdir = NULL; + + g_assert_null (object); + + file = g_file_new_tmp_dir_finish (result, &error); + g_assert_no_error (error); + + g_assert_true (g_file_query_exists (file, NULL)); + + basename = g_file_get_basename (file); + g_assert_true (g_str_has_prefix (basename, "g_file_new_tmp_dir_async_")); + + info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE, + G_FILE_QUERY_INFO_NONE, NULL, &error); + g_assert_no_error (error); + + g_assert_cmpuint (g_file_info_get_file_type (info), ==, G_FILE_TYPE_DIRECTORY); + + parent = g_file_get_parent (file); + tmpdir = g_file_new_for_path (g_get_tmp_dir ()); + + g_assert_true (g_file_equal (tmpdir, parent)); + + g_main_loop_quit (loop); + + g_object_unref (file); + g_object_unref (parent); + g_object_unref (info); + g_free (basename); + g_object_unref (tmpdir); +} + +static void +on_new_tmp_dir_error (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + AsyncErrorData *error_data = user_data; + + g_assert_null (object); + + g_assert_null (g_file_new_tmp_dir_finish (result, error_data->error)); + g_assert_nonnull (error_data->error); + + g_main_loop_quit (error_data->loop); +} + +static void +test_async_new_tmp_dir (void) +{ + GMainLoop *loop; + GError *error = NULL; + GCancellable *cancellable; + AsyncErrorData error_data = { .error = &error }; + + loop = g_main_loop_new (NULL, TRUE); + error_data.loop = loop; + + g_file_new_tmp_dir_async ("g_file_new_tmp_dir_async_XXXXXX", + G_PRIORITY_DEFAULT, NULL, + on_new_tmp_dir_done, loop); + g_main_loop_run (loop); + + g_file_new_tmp_dir_async ("g_file_new_tmp_dir_async", + G_PRIORITY_DEFAULT, NULL, + on_new_tmp_dir_error, &error_data); + g_main_loop_run (loop); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_clear_error (&error); + + cancellable = g_cancellable_new (); + g_file_new_tmp_dir_async ("g_file_new_tmp_dir_async_cancelled_XXXXXX", + G_PRIORITY_DEFAULT, cancellable, + on_new_tmp_dir_error, &error_data); + g_cancellable_cancel (cancellable); + g_main_loop_run (loop); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_clear_object (&cancellable); + g_clear_error (&error); + + g_main_loop_unref (loop); +} + +static void on_file_deleted (GObject *object, GAsyncResult *result, gpointer user_data) @@ -2001,6 +2289,133 @@ test_async_delete (void) } static void +on_symlink_done (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GFile *file = (GFile *) object; + GError *error = NULL; + GMainLoop *loop = user_data; + + g_assert_true (g_file_make_symbolic_link_finish (file, result, &error)); + g_assert_no_error (error); + + g_main_loop_quit (loop); +} + +static void +on_symlink_error (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GFile *file = (GFile *) object; + GError *error = NULL; + AsyncErrorData *data = user_data; + + g_assert_false (g_file_make_symbolic_link_finish (file, result, &error)); + g_assert_nonnull (error); + g_propagate_error (data->error, g_steal_pointer (&error)); + + g_main_loop_quit (data->loop); +} + +static void +test_async_make_symlink (void) +{ + GFile *link; + GFile *parent_dir; + GFile *target; + GFileInfo *link_info; + GFileIOStream *iostream; + GError *error = NULL; + GCancellable *cancellable; + GMainLoop *loop; + AsyncErrorData error_data = {0}; + gchar *tmpdir_path; + gchar *target_path; + + target = g_file_new_tmp ("g_file_symlink_target_XXXXXX", &iostream, &error); + g_assert_no_error (error); + + g_io_stream_close ((GIOStream *) iostream, NULL, &error); + g_assert_no_error (error); + g_object_unref (iostream); + + g_assert_true (g_file_query_exists (target, NULL)); + + loop = g_main_loop_new (NULL, TRUE); + error_data.loop = loop; + error_data.error = &error; + + tmpdir_path = g_dir_make_tmp ("g_file_symlink_XXXXXX", &error); + g_assert_no_error (error); + + parent_dir = g_file_new_for_path (tmpdir_path); + g_assert_true (g_file_query_exists (parent_dir, NULL)); + + link = g_file_get_child (parent_dir, "symlink"); + g_assert_false (g_file_query_exists (link, NULL)); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*symlink_value*failed*"); + g_file_make_symbolic_link_async (link, NULL, + G_PRIORITY_DEFAULT, NULL, + on_symlink_done, loop); + g_test_assert_expected_messages (); + + g_file_make_symbolic_link_async (link, "", + G_PRIORITY_DEFAULT, NULL, + on_symlink_error, &error_data); + g_main_loop_run (loop); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_clear_error (&error); + + target_path = g_file_get_path (target); + g_file_make_symbolic_link_async (link, target_path, + G_PRIORITY_DEFAULT, NULL, + on_symlink_done, loop); + g_main_loop_run (loop); + + g_assert_true (g_file_query_exists (link, NULL)); + link_info = g_file_query_info (link, + G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK "," + G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, + &error); + g_assert_no_error (error); + + g_assert_true (g_file_info_get_is_symlink (link_info)); + g_assert_cmpstr (target_path, ==, g_file_info_get_symlink_target (link_info)); + + /* Try creating it again, it fails */ + g_file_make_symbolic_link_async (link, target_path, + G_PRIORITY_DEFAULT, NULL, + on_symlink_error, &error_data); + g_main_loop_run (loop); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS); + g_clear_error (&error); + + cancellable = g_cancellable_new (); + g_file_make_symbolic_link_async (link, target_path, + G_PRIORITY_DEFAULT, cancellable, + on_symlink_error, &error_data); + g_cancellable_cancel (cancellable); + g_main_loop_run (loop); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_clear_error (&error); + g_clear_object (&cancellable); + + g_main_loop_unref (loop); + g_object_unref (target); + g_object_unref (parent_dir); + g_object_unref (link); + g_object_unref (link_info); + g_free (tmpdir_path); + g_free (target_path); +} + +static void test_copy_preserve_mode (void) { #ifdef G_OS_UNIX @@ -2098,71 +2513,10 @@ test_copy_preserve_mode (void) #endif } -static gchar * -splice_to_string (GInputStream *stream, - GError **error) -{ - GMemoryOutputStream *buffer = NULL; - char *ret = NULL; - - buffer = (GMemoryOutputStream*)g_memory_output_stream_new (NULL, 0, g_realloc, g_free); - if (g_output_stream_splice ((GOutputStream*)buffer, stream, 0, NULL, error) < 0) - goto out; - - if (!g_output_stream_write ((GOutputStream*)buffer, "\0", 1, NULL, error)) - goto out; - - if (!g_output_stream_close ((GOutputStream*)buffer, NULL, error)) - goto out; - - ret = g_memory_output_stream_steal_data (buffer); - out: - g_clear_object (&buffer); - return ret; -} - -static gboolean -get_size_from_du (const gchar *path, guint64 *size) -{ - GSubprocess *du; - gboolean ok; - gchar *result; - gchar *endptr; - GError *error = NULL; - gchar *du_path = NULL; - - /* If we can’t find du, don’t try and run the test. */ - du_path = g_find_program_in_path ("du"); - if (du_path == NULL) - return FALSE; - g_free (du_path); - - du = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE, - &error, - "du", "--bytes", "-s", path, NULL); - g_assert_no_error (error); - - result = splice_to_string (g_subprocess_get_stdout_pipe (du), &error); - g_assert_no_error (error); - - *size = g_ascii_strtoll (result, &endptr, 10); - - g_subprocess_wait (du, NULL, &error); - g_assert_no_error (error); - - ok = g_subprocess_get_successful (du); - - g_object_unref (du); - g_free (result); - - return ok; -} - static void test_measure (void) { GFile *file; - guint64 size; guint64 num_bytes; guint64 num_dirs; guint64 num_files; @@ -2173,12 +2527,6 @@ test_measure (void) path = g_test_build_filename (G_TEST_DIST, "desktop-files", NULL); file = g_file_new_for_path (path); - if (!get_size_from_du (path, &size)) - { - g_test_message ("du not found or fail to run, skipping byte measurement"); - size = 0; - } - ok = g_file_measure_disk_usage (file, G_FILE_MEASURE_APPARENT_SIZE, NULL, @@ -2191,8 +2539,7 @@ test_measure (void) g_assert_true (ok); g_assert_no_error (error); - if (size > 0) - g_assert_cmpuint (num_bytes, ==, size); + g_assert_cmpuint (num_bytes, ==, 74478); g_assert_cmpuint (num_dirs, ==, 6); g_assert_cmpuint (num_files, ==, 32); @@ -2244,8 +2591,7 @@ measure_done (GObject *source, g_assert_true (ok); g_assert_no_error (error); - if (data->expected_bytes > 0) - g_assert_cmpuint (data->expected_bytes, ==, num_bytes); + g_assert_cmpuint (data->expected_bytes, ==, num_bytes); g_assert_cmpuint (data->expected_dirs, ==, num_dirs); g_assert_cmpuint (data->expected_files, ==, num_files); @@ -2274,15 +2620,9 @@ test_measure_async (void) path = g_test_build_filename (G_TEST_DIST, "desktop-files", NULL); file = g_file_new_for_path (path); - - if (!get_size_from_du (path, &data->expected_bytes)) - { - g_test_message ("du not found or fail to run, skipping byte measurement"); - data->expected_bytes = 0; - } - g_free (path); + data->expected_bytes = 74478; data->expected_dirs = 6; data->expected_files = 32; @@ -2999,6 +3339,20 @@ test_build_attribute_list_for_copy (void) g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS_USEC ",")); } #endif +#ifdef HAVE_UTIMENSAT + g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_MODIFIED ",")); + g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC ",")); + if (flags & G_FILE_COPY_ALL_METADATA) + { + g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS ",")); + g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC ",")); + } + else + { + g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS ",")); + g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC ",")); + } +#endif g_free (attrs_with_commas); } @@ -3111,12 +3465,361 @@ test_move_async (void) g_free (destination_path); } +static GAppInfo * +create_command_line_app_info (const char *name, + const char *command_line, + const char *default_for_type) +{ + GAppInfo *info; + GError *error = NULL; + + info = g_app_info_create_from_commandline (command_line, + name, + G_APP_INFO_CREATE_NONE, + &error); + g_assert_no_error (error); + + g_app_info_set_as_default_for_type (info, default_for_type, &error); + g_assert_no_error (error); + + return g_steal_pointer (&info); +} + +static void +test_query_default_handler_uri (void) +{ + GError *error = NULL; + GAppInfo *info; + GAppInfo *default_info; + GFile *file; + GFile *invalid_file; + +#if defined(G_OS_WIN32) || defined(__APPLE__) + g_test_skip ("Default URI handlers are not currently supported on Windows or macOS"); + return; +#endif + + info = create_command_line_app_info ("Gio File Handler", "true", + "x-scheme-handler/gio-file"); + g_assert_true (G_IS_APP_INFO (info)); + + file = g_file_new_for_uri ("gio-file://hello-gio!"); + default_info = g_file_query_default_handler (file, NULL, &error); + g_assert_no_error (error); + g_assert_true (g_app_info_equal (default_info, info)); + + invalid_file = g_file_new_for_uri ("gio-file-INVALID://goodbye-gio!"); + g_assert_null (g_file_query_default_handler (invalid_file, NULL, &error)); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); + g_clear_error (&error); + + g_app_info_remove_supports_type (info, "x-scheme-handler/gio-file", &error); + g_assert_no_error (error); + g_app_info_reset_type_associations ("x-scheme-handler/gio-file"); + + g_object_unref (default_info); + g_object_unref (info); + g_object_unref (file); + g_object_unref (invalid_file); +} + +static void +test_query_zero_length_content_type (void) +{ + GFile *empty_file; + GFileInfo *file_info; + GError *error = NULL; + GFileIOStream *iostream; + + g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=755795"); + /* Historically, GLib used to explicitly consider zero-size files as text/plain, + * so they opened in a text editor. In 2.76, we changed that to application/x-zerosize, + * because that’s what xdgmime uses: + * - https://gitlab.gnome.org/GNOME/glib/-/blob/2.74.0/gio/glocalfileinfo.c#L1360-1369 + * - https://bugzilla.gnome.org/show_bug.cgi?id=755795 + * - https://gitlab.gnome.org/GNOME/glib/-/issues/2777 + */ + g_test_summary ("empty files should always be considered application/x-zerosize"); + + empty_file = g_file_new_tmp ("empty-file-XXXXXX", &iostream, &error); + g_assert_no_error (error); + + g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_clear_object (&iostream); + + file_info = + g_file_query_info (empty_file, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + G_FILE_QUERY_INFO_NONE, + NULL, &error); + g_assert_no_error (error); + +#ifndef __APPLE__ + g_assert_cmpstr (g_file_info_get_content_type (file_info), ==, "application/x-zerosize"); +#else + g_assert_cmpstr (g_file_info_get_content_type (file_info), ==, "public.text"); +#endif + + g_clear_object (&file_info); + g_clear_object (&empty_file); +} + +static void +test_query_default_handler_file (void) +{ + GError *error = NULL; + GAppInfo *info; + GAppInfo *default_info; + GFile *text_file; + GFile *binary_file; + GFile *invalid_file; + GFileIOStream *iostream; + GOutputStream *output_stream; + const char buffer[] = "Text file!\n"; + const guint8 binary_buffer[] = "\xde\xad\xbe\xff"; + +#if defined(G_OS_WIN32) || defined(__APPLE__) + g_test_skip ("Default URI handlers are not currently supported on Windows or macOS"); + return; +#endif + + text_file = g_file_new_tmp ("query-default-handler-XXXXXX", &iostream, &error); + g_assert_no_error (error); + + output_stream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + g_output_stream_write_all (output_stream, buffer, G_N_ELEMENTS (buffer) - 1, + NULL, NULL, &error); + g_assert_no_error (error); + + g_output_stream_flush (output_stream, NULL, &error); + g_assert_no_error (error); + + g_output_stream_close (output_stream, NULL, &error); + g_assert_no_error (error); + g_clear_object (&iostream); + + info = create_command_line_app_info ("Text handler", "true", "text/plain"); + g_assert_true (G_IS_APP_INFO (info)); + + default_info = g_file_query_default_handler (text_file, NULL, &error); + g_assert_no_error (error); + g_assert_true (g_app_info_equal (default_info, info)); + + invalid_file = g_file_new_for_path ("/hopefully/this-does-not-exists"); + g_assert_null (g_file_query_default_handler (invalid_file, NULL, &error)); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_clear_error (&error); + + binary_file = g_file_new_tmp ("query-default-handler-bin-XXXXXX", &iostream, &error); + g_assert_no_error (error); + + output_stream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + g_output_stream_write_all (output_stream, binary_buffer, + G_N_ELEMENTS (binary_buffer), + NULL, NULL, &error); + g_assert_no_error (error); + + g_output_stream_flush (output_stream, NULL, &error); + g_assert_no_error (error); + + g_output_stream_close (output_stream, NULL, &error); + g_assert_no_error (error); + g_clear_object (&iostream); + + g_assert_null (g_file_query_default_handler (binary_file, NULL, &error)); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); + g_clear_error (&error); + + g_app_info_remove_supports_type (info, "text/plain", &error); + g_assert_no_error (error); + g_app_info_reset_type_associations ("text/plain"); + + g_object_unref (default_info); + g_object_unref (info); + g_object_unref (text_file); + g_object_unref (binary_file); + g_object_unref (invalid_file); +} + +typedef struct { + GMainLoop *loop; + GAppInfo *info; + GError *error; +} QueryDefaultHandlerData; + +static void +on_query_default (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + QueryDefaultHandlerData *data = user_data; + + data->info = g_file_query_default_handler_finish (G_FILE (source), result, + &data->error); + g_main_loop_quit (data->loop); +} + +static void +test_query_default_handler_file_async (void) +{ + QueryDefaultHandlerData data = {0}; + GCancellable *cancellable; + GAppInfo *info; + GFile *text_file; + GFile *binary_file; + GFile *invalid_file; + GFileIOStream *iostream; + GOutputStream *output_stream; + const char buffer[] = "Text file!\n"; + const guint8 binary_buffer[] = "\xde\xad\xbe\xff"; + GError *error = NULL; + +#if defined(G_OS_WIN32) || defined(__APPLE__) + g_test_skip ("Default URI handlers are not currently supported on Windows or macOS"); + return; +#endif + + data.loop = g_main_loop_new (NULL, FALSE); + + text_file = g_file_new_tmp ("query-default-handler-XXXXXX", &iostream, &error); + g_assert_no_error (error); + + output_stream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + g_output_stream_write_all (output_stream, buffer, G_N_ELEMENTS (buffer) - 1, + NULL, NULL, &error); + g_assert_no_error (error); + + g_output_stream_close (output_stream, NULL, &error); + g_assert_no_error (error); + g_clear_object (&iostream); + + info = create_command_line_app_info ("Text handler", "true", "text/plain"); + g_assert_true (G_IS_APP_INFO (info)); + + g_file_query_default_handler_async (text_file, G_PRIORITY_DEFAULT, + NULL, on_query_default, + &data); + g_main_loop_run (data.loop); + g_assert_no_error (data.error); + g_assert_true (g_app_info_equal (data.info, info)); + g_clear_object (&data.info); + + invalid_file = g_file_new_for_path ("/hopefully/this/.file/does-not-exists"); + g_file_query_default_handler_async (invalid_file, G_PRIORITY_DEFAULT, + NULL, on_query_default, + &data); + g_main_loop_run (data.loop); + g_assert_null (data.info); + g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_clear_error (&data.error); + + cancellable = g_cancellable_new (); + g_file_query_default_handler_async (text_file, G_PRIORITY_DEFAULT, + cancellable, on_query_default, + &data); + g_cancellable_cancel (cancellable); + g_main_loop_run (data.loop); + g_assert_null (data.info); + g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_clear_error (&data.error); + + binary_file = g_file_new_tmp ("query-default-handler-bin-XXXXXX", &iostream, &error); + g_assert_no_error (error); + + output_stream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + g_output_stream_write_all (output_stream, binary_buffer, + G_N_ELEMENTS (binary_buffer), + NULL, NULL, &error); + g_assert_no_error (error); + + g_output_stream_close (output_stream, NULL, &error); + g_assert_no_error (error); + g_clear_object (&iostream); + + g_file_query_default_handler_async (binary_file, G_PRIORITY_DEFAULT, + NULL, on_query_default, + &data); + g_main_loop_run (data.loop); + g_assert_null (data.info); + g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); + g_clear_error (&data.error); + + g_app_info_remove_supports_type (info, "text/plain", &error); + g_assert_no_error (error); + g_app_info_reset_type_associations ("text/plain"); + + g_main_loop_unref (data.loop); + g_object_unref (info); + g_object_unref (text_file); + g_object_unref (binary_file); + g_object_unref (invalid_file); +} + +static void +test_query_default_handler_uri_async (void) +{ + QueryDefaultHandlerData data = {0}; + GCancellable *cancellable; + GAppInfo *info; + GFile *file; + GFile *invalid_file; + +#if defined(G_OS_WIN32) || defined(__APPLE__) + g_test_skip ("Default URI handlers are not currently supported on Windows or macOS"); + return; +#endif + + info = create_command_line_app_info ("Gio File Handler", "true", + "x-scheme-handler/gio-file"); + g_assert_true (G_IS_APP_INFO (info)); + + data.loop = g_main_loop_new (NULL, FALSE); + + file = g_file_new_for_uri ("gio-file://hello-gio!"); + g_file_query_default_handler_async (file, G_PRIORITY_DEFAULT, + NULL, on_query_default, + &data); + g_main_loop_run (data.loop); + g_assert_no_error (data.error); + g_assert_true (g_app_info_equal (data.info, info)); + g_clear_object (&data.info); + + invalid_file = g_file_new_for_uri ("gio-file-INVALID://goodbye-gio!"); + g_file_query_default_handler_async (invalid_file, G_PRIORITY_DEFAULT, + NULL, on_query_default, + &data); + g_main_loop_run (data.loop); + g_assert_null (data.info); + g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); + g_clear_error (&data.error); + + cancellable = g_cancellable_new (); + g_file_query_default_handler_async (file, G_PRIORITY_DEFAULT, + cancellable, on_query_default, + &data); + g_cancellable_cancel (cancellable); + g_main_loop_run (data.loop); + g_assert_null (data.info); + g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_clear_error (&data.error); + + g_app_info_remove_supports_type (info, "x-scheme-handler/gio-file", &data.error); + g_assert_no_error (data.error); + g_app_info_reset_type_associations ("x-scheme-handler/gio-file"); + + g_main_loop_unref (data.loop); + g_object_unref (info); + g_object_unref (file); + g_object_unref (invalid_file); +} + int main (int argc, char *argv[]) { setlocale (LC_ALL, ""); - g_test_init (&argc, &argv, NULL); + g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); g_test_add_func ("/file/basic", test_basic); g_test_add_func ("/file/build-filename", test_build_filename); @@ -3136,7 +3839,10 @@ main (int argc, char *argv[]) g_test_add_func ("/file/replace-symlink/using-etag", test_replace_symlink_using_etag); g_test_add_data_func ("/file/replace/write-only", GUINT_TO_POINTER (FALSE), test_replace); g_test_add_data_func ("/file/replace/read-write", GUINT_TO_POINTER (TRUE), test_replace); + g_test_add_func ("/file/async-new-tmp", test_async_new_tmp); + g_test_add_func ("/file/async-new-tmp-dir", test_async_new_tmp_dir); g_test_add_func ("/file/async-delete", test_async_delete); + g_test_add_func ("/file/async-make-symlink", test_async_make_symlink); g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode); g_test_add_func ("/file/measure", test_measure); g_test_add_func ("/file/measure-async", test_measure_async); @@ -3155,6 +3861,11 @@ main (int argc, char *argv[]) g_test_add_func ("/file/writev/async_all-cancellation", test_writev_async_all_cancellation); g_test_add_func ("/file/build-attribute-list-for-copy", test_build_attribute_list_for_copy); g_test_add_func ("/file/move_async", test_move_async); + g_test_add_func ("/file/query-zero-length-content-type", test_query_zero_length_content_type); + g_test_add_func ("/file/query-default-handler-file", test_query_default_handler_file); + g_test_add_func ("/file/query-default-handler-file-async", test_query_default_handler_file_async); + g_test_add_func ("/file/query-default-handler-uri", test_query_default_handler_uri); + g_test_add_func ("/file/query-default-handler-uri-async", test_query_default_handler_uri_async); return g_test_run (); } |