[gvfs] gphoto2: Implement pull support



commit 86162bbe4b09f517b551ff1c9207a119e91ab733
Author: Ross Lagerwall <rosslagerwall gmail com>
Date:   Fri Nov 8 06:49:58 2013 +0200

    gphoto2: Implement pull support
    
    Make use of gp_file_new_from_fd() to copy the file from the device
    straight to the local file without copying it into memory first.  This
    fixes memory usage issues with copying large videos off devices.
    
    A positive side effect is that the progress bar now behaves correctly
    when copying a large file.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=642814

 daemon/gvfsbackendgphoto2.c |  177 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 177 insertions(+), 0 deletions(-)
---
diff --git a/daemon/gvfsbackendgphoto2.c b/daemon/gvfsbackendgphoto2.c
index 64de43c..bd759fc 100644
--- a/daemon/gvfsbackendgphoto2.c
+++ b/daemon/gvfsbackendgphoto2.c
@@ -34,6 +34,7 @@
 #include <glib/gstdio.h>
 #include <glib/gi18n.h>
 #include <gio/gio.h>
+#include <gio/gfiledescriptorbased.h>
 #include <gphoto2.h>
 #ifdef HAVE_GUDEV
   #include <gudev/gudev.h>
@@ -294,6 +295,15 @@ typedef struct {
 
 /* ------------------------------------------------------------------------------------------------- */
 
+typedef struct {
+  goffset size;
+  float target;
+  GFileProgressCallback progress_callback;
+  gpointer progress_callback_data;
+} PullContext;
+
+/* ------------------------------------------------------------------------------------------------- */
+
 static void
 DEBUG (const gchar *message, ...)
 {
@@ -3476,6 +3486,172 @@ do_move (GVfsBackend *backend,
   g_free (dst_name);
 }
 
+static unsigned int
+ctx_progress_start_func (GPContext *context,
+                         float target,
+                         const char *str,
+                         void *data)
+{
+  PullContext *pc = data;
+  pc->target = target;
+  return 0;
+}
+
+static void
+ctx_progress_update_func (GPContext *context,
+                          unsigned int id,
+                          float current,
+                          void *data)
+{
+  PullContext *pc = data;
+  if (pc->progress_callback)
+    pc->progress_callback ((current / pc->target) * pc->size, pc->size,
+                           pc->progress_callback_data);
+}
+
+static void
+ctx_progress_stop_func (GPContext *context,
+                        unsigned int id,
+                        void *data)
+{
+  PullContext *pc = data;
+  if (pc->progress_callback)
+    pc->progress_callback (pc->size, pc->size, pc->progress_callback_data);
+}
+
+static void
+do_pull (GVfsBackend *backend,
+         GVfsJobPull *job,
+         const char *source,
+         const char *local_path,
+         GFileCopyFlags flags,
+         gboolean remove_source,
+         GFileProgressCallback progress_callback,
+         gpointer progress_callback_data)
+{
+  GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
+  GFileInfo *info = g_file_info_new ();
+  GError *error = NULL;
+  PullContext pc;
+  CameraFile *file;
+  GFile *dest;
+  GFileDescriptorBased *fdstream;
+  char *dir, *name;
+  int rc;
+
+  ensure_not_dirty (gphoto2_backend);
+
+  split_filename_with_ignore_prefix (gphoto2_backend, source, &dir, &name);
+
+  if (remove_source && !gphoto2_backend->can_delete)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+                        G_IO_ERROR_NOT_SUPPORTED,
+                        _("Not supported"));
+      goto out;
+    }
+
+  /* Fallback to the default implementation unless we have a regular file */
+  if (!file_get_info (gphoto2_backend, dir, name, info, &error, FALSE) ||
+      g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+                        G_IO_ERROR_NOT_SUPPORTED,
+                        _("Not supported"));
+      goto out;
+    }
+
+  dest = g_file_new_for_path (local_path);
+  if (flags & G_FILE_COPY_OVERWRITE)
+    {
+      fdstream = G_FILE_DESCRIPTOR_BASED (
+                     g_file_replace (dest,
+                                     NULL,
+                                     flags & G_FILE_COPY_BACKUP ? TRUE : FALSE,
+                                     G_FILE_CREATE_REPLACE_DESTINATION,
+                                     G_VFS_JOB (job)->cancellable, &error));
+    }
+  else
+    {
+      fdstream = G_FILE_DESCRIPTOR_BASED (
+                     g_file_create (dest,
+                                    G_FILE_CREATE_NONE,
+                                    G_VFS_JOB (job)->cancellable, &error));
+    }
+  g_object_unref (dest);
+
+  if (!fdstream)
+    {
+      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+      goto out;
+    }
+
+  rc = gp_file_new_from_fd (&file, g_file_descriptor_based_get_fd (fdstream));
+  if (rc != 0)
+    {
+      error = get_error_from_gphoto2 (_("Error creating file object"), rc);
+      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+      g_object_unref (fdstream);
+      goto out;
+    }
+
+  pc.size = g_file_info_get_size (info);
+  pc.progress_callback = progress_callback;
+  pc.progress_callback_data = progress_callback_data;
+
+  gp_context_set_progress_funcs (gphoto2_backend->context,
+                                 ctx_progress_start_func,
+                                 ctx_progress_update_func,
+                                 ctx_progress_stop_func,
+                                 &pc);
+
+  rc = gp_camera_file_get (gphoto2_backend->camera,
+                           dir,
+                           name,
+                           GP_FILE_TYPE_NORMAL,
+                           file,
+                           gphoto2_backend->context);
+
+  gp_context_set_progress_funcs (gphoto2_backend->context, NULL, NULL, NULL, NULL);
+
+  /* gp_camera_file_get() closes the fd so we just unref here */
+  g_object_unref (fdstream);
+  gp_file_unref (file);
+
+  if (rc != 0)
+    {
+      error = get_error_from_gphoto2 (_("Error getting file"), rc);
+      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+      goto out;
+    }
+
+  if (remove_source)
+    {
+      rc = gp_camera_file_delete (gphoto2_backend->camera,
+                                  dir,
+                                  name,
+                                  gphoto2_backend->context);
+      if (rc != 0)
+        {
+          error = get_error_from_gphoto2 (_("Error deleting file"), rc);
+          g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+          goto out;
+        }
+
+      caches_invalidate_file (gphoto2_backend, dir, name);
+      caches_invalidate_free_space (gphoto2_backend);
+      monitors_emit_deleted (gphoto2_backend, dir, name);
+    }
+
+  g_vfs_job_succeeded (G_VFS_JOB (job));
+
+out:
+  g_object_unref (info);
+  g_free (name);
+  g_free (dir);
+  g_clear_error (&error);
+}
+
 /* ------------------------------------------------------------------------------------------------- */
 
 static void
@@ -3607,6 +3783,7 @@ g_vfs_backend_gphoto2_class_init (GVfsBackendGphoto2Class *klass)
   backend_class->seek_on_write = do_seek_on_write;
   backend_class->truncate = do_truncate;
   backend_class->move = do_move;
+  backend_class->pull = do_pull;
   backend_class->create_dir_monitor = do_create_dir_monitor;
   backend_class->create_file_monitor = do_create_file_monitor;
 


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