diff --git a/docs/reference/gio/gio-sections-common.txt b/docs/reference/gio/gio-sections-common.txt index 4173f7dcf..912c25507 100644 --- a/docs/reference/gio/gio-sections-common.txt +++ b/docs/reference/gio/gio-sections-common.txt @@ -199,6 +199,7 @@ g_file_replace_contents g_file_replace_contents_async g_file_replace_contents_bytes_async g_file_replace_contents_finish +g_file_build_attribute_list_for_copy g_file_copy_attributes g_file_create_readwrite g_file_create_readwrite_async diff --git a/gio/gfile.c b/gio/gfile.c index 533efa7df..6e3b524f7 100644 --- a/gio/gfile.c +++ b/gio/gfile.c @@ -2695,14 +2695,36 @@ should_copy (GFileAttributeInfo *info, return info->flags & G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE; } -static gboolean -build_attribute_list_for_copy (GFile *file, - GFileCopyFlags flags, - char **out_attributes, - GCancellable *cancellable, - GError **error) +/** + * g_file_build_attribute_list_for_copy: + * @file: a #GFile to copy attributes to + * @flags: a set of #GFileCopyFlags + * @cancellable: (nullable): optional #GCancellable object, + * %NULL to ignore + * @error: a #GError, %NULL to ignore + * + * Prepares the file attribute query string for copying to @file. + * + * This function prepares an attribute query string to be + * passed to g_file_query_info() to get a list of attributes + * normally copied with the file (see g_file_copy_attributes() + * for the detailed description). This function is used by the + * implementation of g_file_copy_attributes() and is useful + * when one needs to query and set the attributes in two + * stages (e.g., for recursive move of a directory). + * + * Returns: an attribute query string for g_file_query_info(), + * or %NULL if an error occurs. + * + * Since: 2.68 + */ +char * +g_file_build_attribute_list_for_copy (GFile *file, + GFileCopyFlags flags, + GCancellable *cancellable, + GError **error) { - gboolean ret = FALSE; + char *ret = NULL; GFileAttributeInfoList *attributes = NULL, *namespaces = NULL; GString *s = NULL; gboolean first; @@ -2710,6 +2732,10 @@ build_attribute_list_for_copy (GFile *file, gboolean copy_all_attributes; gboolean skip_perms; + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + copy_all_attributes = flags & G_FILE_COPY_ALL_METADATA; skip_perms = (flags & G_FILE_COPY_TARGET_DEFAULT_PERMS) != 0; @@ -2763,8 +2789,7 @@ build_attribute_list_for_copy (GFile *file, } } - ret = TRUE; - *out_attributes = g_string_free (s, FALSE); + ret = g_string_free (s, FALSE); s = NULL; out: if (s) @@ -2810,8 +2835,9 @@ g_file_copy_attributes (GFile *source, GFileInfo *info; gboolean source_nofollow_symlinks; - if (!build_attribute_list_for_copy (destination, flags, &attrs_to_read, - cancellable, error)) + attrs_to_read = g_file_build_attribute_list_for_copy (destination, flags, + cancellable, error); + if (!attrs_to_read) return FALSE; source_nofollow_symlinks = flags & G_FILE_COPY_NOFOLLOW_SYMLINKS; @@ -3157,6 +3183,7 @@ file_copy_fallback (GFile *source, char *attrs_to_read; gboolean do_set_attributes = FALSE; GFileCreateFlags create_flags; + GError *tmp_error = NULL; /* need to know the file type */ info = g_file_query_info (source, @@ -3198,47 +3225,43 @@ file_copy_fallback (GFile *source, goto out; in = G_INPUT_STREAM (file_in); - if (!build_attribute_list_for_copy (destination, flags, &attrs_to_read, - cancellable, error)) + attrs_to_read = g_file_build_attribute_list_for_copy (destination, flags, + cancellable, error); + if (!attrs_to_read) goto out; - if (attrs_to_read != NULL) + /* Ok, ditch the previous lightweight info (on Unix we just + * called lstat()); at this point we gather all the information + * we need about the source from the opened file descriptor. + */ + g_object_unref (info); + + info = g_file_input_stream_query_info (file_in, attrs_to_read, + cancellable, &tmp_error); + if (!info) { - GError *tmp_error = NULL; - - /* Ok, ditch the previous lightweight info (on Unix we just - * called lstat()); at this point we gather all the information - * we need about the source from the opened file descriptor. + /* Not all gvfs backends implement query_info_on_read(), we + * can just fall back to the pathname again. + * https://bugzilla.gnome.org/706254 */ - g_object_unref (info); - - info = g_file_input_stream_query_info (file_in, attrs_to_read, - cancellable, &tmp_error); - if (!info) + if (g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) { - /* Not all gvfs backends implement query_info_on_read(), we - * can just fall back to the pathname again. - * https://bugzilla.gnome.org/706254 - */ - if (g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) - { - g_clear_error (&tmp_error); - info = g_file_query_info (source, attrs_to_read, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable, error); - } - else - { - g_free (attrs_to_read); - g_propagate_error (error, tmp_error); - goto out; - } + g_clear_error (&tmp_error); + info = g_file_query_info (source, attrs_to_read, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, error); + } + else + { + g_free (attrs_to_read); + g_propagate_error (error, tmp_error); + goto out; } - g_free (attrs_to_read); - if (!info) - goto out; - - do_set_attributes = TRUE; } + g_free (attrs_to_read); + if (!info) + goto out; + + do_set_attributes = TRUE; /* In the local file path, we pass down the source info which * includes things like unix::mode, to ensure that the target file diff --git a/gio/gfile.h b/gio/gfile.h index 8b6d08385..4cff1a372 100644 --- a/gio/gfile.h +++ b/gio/gfile.h @@ -1094,6 +1094,12 @@ gboolean g_file_eject_mountable_with_operation_finish (GFile GAsyncResult *result, GError **error); +GLIB_AVAILABLE_IN_2_68 +char * g_file_build_attribute_list_for_copy (GFile *file, + GFileCopyFlags flags, + GCancellable *cancellable, + GError **error); + GLIB_AVAILABLE_IN_ALL gboolean g_file_copy_attributes (GFile *source, GFile *destination, diff --git a/gio/tests/file.c b/gio/tests/file.c index c3877af4b..e951e1fcd 100644 --- a/gio/tests/file.c +++ b/gio/tests/file.c @@ -1780,6 +1780,79 @@ test_writev_async_all_too_big_vectors (void) g_object_unref (file); } +static void +test_build_attribute_list_for_copy (void) +{ + GFile *tmpfile; + GFileIOStream *iostream; + GError *error = NULL; + const GFileCopyFlags test_flags[] = + { + G_FILE_COPY_NONE, + G_FILE_COPY_TARGET_DEFAULT_PERMS, + G_FILE_COPY_ALL_METADATA, + G_FILE_COPY_ALL_METADATA | G_FILE_COPY_TARGET_DEFAULT_PERMS, + }; + gsize i; + char *attrs; + gchar *attrs_with_commas; + + tmpfile = g_file_new_tmp ("tmp-build-attribute-list-for-copyXXXXXX", + &iostream, &error); + g_assert_no_error (error); + g_io_stream_close ((GIOStream*)iostream, NULL, &error); + g_assert_no_error (error); + g_clear_object (&iostream); + + for (i = 0; i < G_N_ELEMENTS (test_flags); i++) + { + GFileCopyFlags flags = test_flags[i]; + + attrs = g_file_build_attribute_list_for_copy (tmpfile, flags, NULL, &error); + g_test_message ("Attributes for copy: %s", attrs); + g_assert_no_error (error); + g_assert_nonnull (attrs); + attrs_with_commas = g_strconcat (",", attrs, ",", NULL); + g_free (attrs); + + /* See g_local_file_class_init for reference. */ + if (flags & G_FILE_COPY_TARGET_DEFAULT_PERMS) + g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_MODE ",")); + else + g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_MODE ",")); +#ifdef G_OS_UNIX + if (flags & G_FILE_COPY_ALL_METADATA) + { + g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_UID ",")); + g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_GID ",")); + } + else + { + g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_UID ",")); + g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_GID ",")); + } +#endif +#ifdef HAVE_UTIMES + 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_USEC ",")); + 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_USEC ",")); + } + 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_USEC ",")); + } +#endif + g_free (attrs_with_commas); + } + + (void) g_file_delete (tmpfile, NULL, NULL); + g_clear_object (&tmpfile); +} + int main (int argc, char *argv[]) { @@ -1817,6 +1890,7 @@ main (int argc, char *argv[]) g_test_add_func ("/file/writev/async_all-no-vectors", test_writev_async_all_no_vectors); g_test_add_func ("/file/writev/async_all-to-big-vectors", test_writev_async_all_too_big_vectors); 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); return g_test_run (); }