[glib: 1/2] gio: Expose g_file_build_attribute_list_for_copy




commit 094eca7076d8db4d7e2d5344101b7d8e6bd9de36
Author: Maxim Mikityanskiy <maxtram95 gmail com>
Date:   Mon Sep 7 19:20:01 2020 +0300

    gio: Expose g_file_build_attribute_list_for_copy
    
    Expose a function that prepares an attribute query string to be passed
    to g_file_query_info() to get a list of attributes normally copied with
    the file. This function is used by the implementation of
    g_file_copy_attributes, and it's useful if one needs to split
    g_file_copy_attributes into two stages, for example, when nautilus does
    a recursive move of a directory. When files are moved from the source
    directory, its modification time changes. To preserve the mtime on the
    destination directory, it has to be queried before moving files and set
    after doing it, hence these two stages.
    
    Signed-off-by: Maxim Mikityanskiy <maxtram95 gmail com>

 docs/reference/gio/gio-sections-common.txt |   1 +
 gio/gfile.c                                | 113 +++++++++++++++++------------
 gio/gfile.h                                |   6 ++
 gio/tests/file.c                           |  74 +++++++++++++++++++
 4 files changed, 149 insertions(+), 45 deletions(-)
---
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)
-    {
-      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.
+   */
+  g_object_unref (info);
 
-      /* 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.
+  info = g_file_input_stream_query_info (file_in, attrs_to_read,
+                                         cancellable, &tmp_error);
+  if (!info)
+    {
+      /* 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 ();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]