Improve the g_file_make_symbolic_link docs
[platform/upstream/glib.git] / gio / gfile.c
index ceaf802..d104ab4 100644 (file)
  */
 
 #include "config.h"
+#ifdef HAVE_SPLICE
+#define _GNU_SOURCE
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#endif
 #include <string.h>
 #include <sys/types.h>
 #ifdef HAVE_PWD_H
@@ -31,9 +38,9 @@
 #include "gfile.h"
 #include "gvfs.h"
 #include "gioscheduler.h"
-#include "glocalfile.h"
 #include "gsimpleasyncresult.h"
 #include "gfileattribute-priv.h"
+#include "gfiledescriptorbased.h"
 #include "gpollfilemonitor.h"
 #include "gappinfo.h"
 #include "gfileinputstream.h"
  * </para>
  **/
 
-static void g_file_base_init (gpointer g_class);
-static void g_file_class_init (gpointer g_class,
-                              gpointer class_data);
-
 static void               g_file_real_query_info_async            (GFile                  *file,
                                                                   const char             *attributes,
                                                                   GFileQueryInfoFlags     flags,
@@ -266,43 +269,12 @@ static gboolean           g_file_real_copy_finish                 (GFile
                                                                   GAsyncResult           *res,
                                                                   GError                **error);
 
-GType
-g_file_get_type (void)
-{
-  static volatile gsize g_define_type_id__volatile = 0;
-
-  if (g_once_init_enter (&g_define_type_id__volatile))
-    {
-      const GTypeInfo file_info =
-      {
-        sizeof (GFileIface), /* class_size */
-       g_file_base_init,   /* base_init */
-       NULL,           /* base_finalize */
-       g_file_class_init,
-       NULL,           /* class_finalize */
-       NULL,           /* class_data */
-       0,
-       0,              /* n_preallocs */
-       NULL
-      };
-      GType g_define_type_id =
-       g_type_register_static (G_TYPE_INTERFACE, I_("GFile"),
-                               &file_info, 0);
-
-      g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
-
-      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
-    }
-
-  return g_define_type_id__volatile;
-}
+typedef GFileIface GFileInterface;
+G_DEFINE_INTERFACE (GFile, g_file, G_TYPE_OBJECT)
 
 static void
-g_file_class_init (gpointer g_class,
-                  gpointer class_data)
+g_file_default_init (GFileIface *iface)
 {
-  GFileIface *iface = g_class;
-
   iface->enumerate_children_async = g_file_real_enumerate_children_async;
   iface->enumerate_children_finish = g_file_real_enumerate_children_finish;
   iface->set_display_name_async = g_file_real_set_display_name_async;
@@ -334,11 +306,6 @@ g_file_class_init (gpointer g_class,
   iface->copy_finish = g_file_real_copy_finish;
 }
 
-static void
-g_file_base_init (gpointer g_class)
-{
-}
-
 
 /**
  * g_file_is_native:
@@ -2161,7 +2128,7 @@ g_file_replace_finish (GFile         *file,
       if (g_simple_async_result_propagate_error (simple, error))
        return NULL;
     }
-  
+
   iface = G_FILE_GET_IFACE (file);
   return (* iface->replace_finish) (file, res, error);
 }
@@ -2669,7 +2636,6 @@ g_file_copy_attributes (GFile           *source,
   return res;
 }
 
-/* Closes the streams */
 static gboolean
 copy_stream_with_progress (GInputStream           *in,
                           GOutputStream          *out,
@@ -2755,25 +2721,134 @@ copy_stream_with_progress (GInputStream           *in,
        progress_callback (current_size, total_size, progress_callback_data);
     }
 
-  if (!res)
-    error = NULL; /* Ignore further errors */
-
   /* Make sure we send full copied size */
   if (progress_callback)
     progress_callback (current_size, total_size, progress_callback_data);
-  
-  /* Don't care about errors in source here */
-  g_input_stream_close (in, cancellable, NULL);
 
-  /* But write errors on close are bad! */
-  if (!g_output_stream_close (out, cancellable, error))
-    res = FALSE;
+  return res;
+}
+
+#ifdef HAVE_SPLICE
+
+static gboolean
+do_splice (int     fd_in,
+          loff_t *off_in,
+           int     fd_out,
+          loff_t *off_out,
+           size_t  len,
+           long   *bytes_transferd,
+           GError **error)
+{
+  long result;
+
+retry:
+  result = splice (fd_in, off_in, fd_out, off_out, len, SPLICE_F_MORE);
+
+  if (result == -1)
+    {
+      int errsv = errno;
+
+      if (errsv == EINTR)
+        goto retry;
+      else if (errsv == ENOSYS || errsv == EINVAL)
+        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                             _("Splice not supported"));
+      else
+        g_set_error (error, G_IO_ERROR,
+                     g_io_error_from_errno (errsv),
+                     _("Error splicing file: %s"),
+                     g_strerror (errsv));
+
+      return FALSE;
+    }
+
+  *bytes_transferd = result;
+  return TRUE;
+}
+
+static gboolean
+splice_stream_with_progress (GInputStream           *in,
+                             GOutputStream          *out,
+                             GCancellable           *cancellable,
+                             GFileProgressCallback   progress_callback,
+                             gpointer                progress_callback_data,
+                             GError                **error)
+{
+  int buffer[2];
+  gboolean res;
+  goffset total_size;
+  loff_t offset_in;
+  loff_t offset_out;
+  int fd_in, fd_out;
+
+  fd_in = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (in));
+  fd_out = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (out));
+
+  if (pipe (buffer) != 0)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                           "Pipe creation failed");
+      return FALSE;
+    }
+
+  total_size = -1;
+  /* avoid performance impact of querying total size when it's not needed */
+  if (progress_callback)
+    {
+      struct stat sbuf;
+
+      if (fstat (fd_in, &sbuf) == 0)
+        total_size = sbuf.st_size;
+    }
+
+  if (total_size == -1)
+    total_size = 0;
+
+  offset_in = offset_out = 0;
+  res = FALSE;
+  while (TRUE)
+    {
+      long n_read;
+      long n_written;
+
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+        break;
+
+      if (!do_splice (fd_in, &offset_in, buffer[1], NULL, 1024*64, &n_read, error))
+        break;
+
+      if (n_read == 0)
+        {
+          res = TRUE;
+          break;
+        }
+
+      while (n_read > 0)
+        {
+          if (g_cancellable_set_error_if_cancelled (cancellable, error))
+            goto out;
+
+          if (!do_splice (buffer[0], NULL, fd_out, &offset_out, n_read, &n_written, error))
+            goto out;
+
+          n_read -= n_written;
+        }
+
+      if (progress_callback)
+        progress_callback (offset_in, total_size, progress_callback_data);
+    }
+
+  /* Make sure we send full copied size */
+  if (progress_callback)
+    progress_callback (offset_in, total_size, progress_callback_data);
+
+ out:
+  close (buffer[0]);
+  close (buffer[1]);
 
-  g_object_unref (in);
-  g_object_unref (out);
-      
   return res;
 }
+#endif
 
 static gboolean
 file_copy_fallback (GFile                  *source,
@@ -2788,6 +2863,10 @@ file_copy_fallback (GFile                  *source,
   GOutputStream *out;
   GFileInfo *info;
   const char *target;
+  gboolean result;
+#ifdef HAVE_SPLICE
+  gboolean fallback = TRUE;
+#endif
 
   /* need to know the file type */
   info = g_file_query_info (source,
@@ -2855,13 +2934,45 @@ file_copy_fallback (GFile                  *source,
       return FALSE;
     }
 
-  if (!copy_stream_with_progress (in, out, source, cancellable,
-                                 progress_callback, progress_callback_data,
-                                 error))
+#ifdef HAVE_SPLICE
+  if (G_IS_FILE_DESCRIPTOR_BASED (in) && G_IS_FILE_DESCRIPTOR_BASED (out))
+    {
+      GError *splice_err = NULL;
+
+      result = splice_stream_with_progress (in, out, cancellable,
+                                            progress_callback, progress_callback_data,
+                                            &splice_err);
+
+      if (result || !g_error_matches (splice_err, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+        {
+          fallback = FALSE;
+          if (!result)
+            g_propagate_error (error, splice_err);
+        }
+      else
+        g_clear_error (&splice_err);
+    }
+
+  if (fallback)
+#endif
+    result = copy_stream_with_progress (in, out, source, cancellable,
+                                       progress_callback, progress_callback_data,
+                                       error);
+
+  /* Don't care about errors in source here */
+  g_input_stream_close (in, cancellable, NULL);
+
+  /* But write errors on close are bad! */
+  if (!g_output_stream_close (out, cancellable, result ? error : NULL))
+    result = FALSE;
+
+  g_object_unref (in);
+  g_object_unref (out);
+
+  if (result == FALSE)
     return FALSE;
 
  copied_file:
-
   /* Ignore errors here. Failure to copy metadata is not a hard error */
   g_file_copy_attributes (source, destination,
                          flags, cancellable, NULL);
@@ -3334,19 +3445,20 @@ g_file_make_directory_with_parents (GFile         *file,
 
 /**
  * g_file_make_symbolic_link:
- * @file: input #GFile.
- * @symlink_value: a string with the value of the new symlink.
+ * @file: a #GFile with the name of the symlink to create
+ * @symlink_value: a string with the path for the target of the new symlink
  * @cancellable: optional #GCancellable object, %NULL to ignore.
- * @error: a #GError. 
- * 
- * Creates a symbolic link.
+ * @error: a #GError.
+ *
+ * Creates a symbolic link named @file which contains the string
+ * @symlink_value.
  *
  * If @cancellable is not %NULL, then the operation can be cancelled by
  * triggering the cancellable object from another thread. If the operation
- * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. 
- * 
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
  * Returns: %TRUE on the creation of a new symlink, %FALSE otherwise.
- **/
+ */
 gboolean
 g_file_make_symbolic_link (GFile         *file,
                           const char    *symlink_value,