[libdazzle] file-transfer: add api for file transfers
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libdazzle] file-transfer: add api for file transfers
- Date: Sun, 3 Dec 2017 10:39:29 +0000 (UTC)
commit 0ce0ddefadfd19eb4cb692991bb4edeb33824ea2
Author: Christian Hergert <chergert redhat com>
Date: Sun Dec 3 02:36:56 2017 -0800
file-transfer: add api for file transfers
We still need some more work around this, but this is the real
basics of the API until we can finish it off.
src/dazzle.h | 2 +
src/dzl-enums.c.in | 40 +++
src/dzl-enums.h.in | 24 ++
src/files/dzl-file-transfer.c | 706 +++++++++++++++++++++++++++++++++++++++++
src/files/dzl-file-transfer.h | 79 +++++
src/files/meson.build | 7 +
src/meson.build | 12 +
tests/meson.build | 7 +
tests/test-file-transfer.c | 82 +++++
9 files changed, 959 insertions(+), 0 deletions(-)
---
diff --git a/src/dazzle.h b/src/dazzle.h
index bfc8333..a0769c0 100644
--- a/src/dazzle.h
+++ b/src/dazzle.h
@@ -33,6 +33,7 @@ G_BEGIN_DECLS
#define DAZZLE_INSIDE
#include "dzl-version.h"
+#include "dzl-enums.h"
#include "actions/dzl-action-group.h"
#include "actions/dzl-child-property-action.h"
#include "actions/dzl-properties-group.h"
@@ -47,6 +48,7 @@ G_BEGIN_DECLS
#include "cache/dzl-task-cache.h"
#include "files/dzl-directory-model.h"
#include "files/dzl-directory-reaper.h"
+#include "files/dzl-file-transfer.h"
#include "files/dzl-recursive-file-monitor.h"
#include "graphing/dzl-cpu-graph.h"
#include "graphing/dzl-cpu-model.h"
diff --git a/src/dzl-enums.c.in b/src/dzl-enums.c.in
new file mode 100644
index 0000000..e5fda56
--- /dev/null
+++ b/src/dzl-enums.c.in
@@ -0,0 +1,40 @@
+/*** BEGIN file-header ***/
+
+#include "config.h"
+
+#include "dzl-enums.h"
+
+#include "files/dzl-file-transfer.h"
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+@enum_name@_get_type (void)
+{
+ static GType etype = 0;
+ if (G_UNLIKELY(etype == 0)) {
+ static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+ etype = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
+ }
+ return etype;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+
+/*** END file-tail ***/
diff --git a/src/dzl-enums.h.in b/src/dzl-enums.h.in
new file mode 100644
index 0000000..6714857
--- /dev/null
+++ b/src/dzl-enums.h.in
@@ -0,0 +1,24 @@
+/*** BEGIN file-header ***/
+#ifndef __DZL_ENUMS_H__
+#define __DZL_ENUMS_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void);
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* __DZL_ENUMS_H__ */
+/*** END file-tail ***/
diff --git a/src/files/dzl-file-transfer.c b/src/files/dzl-file-transfer.c
new file mode 100644
index 0000000..fb5c1df
--- /dev/null
+++ b/src/files/dzl-file-transfer.c
@@ -0,0 +1,706 @@
+/* dzl-file-transfer.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "dzl-file-transfer"
+
+#include "dzl-debug.h"
+#include "dzl-enums.h"
+
+#include "files/dzl-directory-reaper.h"
+#include "files/dzl-file-transfer.h"
+#include "util/dzl-macros.h"
+
+#define QUERY_ATTRS (G_FILE_ATTRIBUTE_STANDARD_NAME"," \
+ G_FILE_ATTRIBUTE_STANDARD_TYPE"," \
+ G_FILE_ATTRIBUTE_STANDARD_SIZE)
+#define QUERY_FLAGS (G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
+
+typedef struct
+{
+ GPtrArray *opers;
+
+ DzlFileTransferStat stat_buf;
+
+ DzlFileTransferFlags flags;
+
+ gint64 last_num_bytes;
+
+ guint executed : 1;
+} DzlFileTransferPrivate;
+
+typedef struct
+{
+ /* Unowned pointers */
+ DzlFileTransfer *self;
+ GCancellable *cancellable;
+
+ /* Owned pointers */
+ GFile *src;
+ GFile *dst;
+ GError *error;
+
+ DzlFileTransferFlags flags;
+} Oper;
+
+typedef void (*FileWalkCallback) (GFile *file,
+ GFileInfo *child_info,
+ gpointer user_data);
+
+enum {
+ PROP_0,
+ PROP_FLAGS,
+ PROP_PROGRESS,
+ N_PROPS
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (DzlFileTransfer, dzl_file_transfer, G_TYPE_OBJECT)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+oper_free (gpointer data)
+{
+ Oper *oper = data;
+
+ oper->self = NULL;
+ oper->cancellable = NULL;
+
+ g_clear_object (&oper->src);
+ g_clear_object (&oper->dst);
+ g_clear_error (&oper->error);
+
+ g_slice_free (Oper, oper);
+}
+
+static void
+dzl_file_transfer_finalize (GObject *object)
+{
+ DzlFileTransfer *self = (DzlFileTransfer *)object;
+ DzlFileTransferPrivate *priv = dzl_file_transfer_get_instance_private (self);
+
+ g_clear_pointer (&priv->opers, g_ptr_array_unref);
+
+ G_OBJECT_CLASS (dzl_file_transfer_parent_class)->finalize (object);
+}
+
+static void
+dzl_file_transfer_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ DzlFileTransfer *self = DZL_FILE_TRANSFER (object);
+
+ switch (prop_id)
+ {
+ case PROP_FLAGS:
+ g_value_set_flags (value, dzl_file_transfer_get_flags (self));
+ break;
+
+ case PROP_PROGRESS:
+ g_value_set_double (value, dzl_file_transfer_get_progress (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+dzl_file_transfer_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ DzlFileTransfer *self = DZL_FILE_TRANSFER (object);
+
+ switch (prop_id)
+ {
+ case PROP_FLAGS:
+ dzl_file_transfer_set_flags (self, g_value_get_flags (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+dzl_file_transfer_class_init (DzlFileTransferClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = dzl_file_transfer_finalize;
+ object_class->get_property = dzl_file_transfer_get_property;
+ object_class->set_property = dzl_file_transfer_set_property;
+
+ properties [PROP_FLAGS] =
+ g_param_spec_flags ("flags",
+ "Flags",
+ "The transfer flags for the operation",
+ DZL_TYPE_FILE_TRANSFER_FLAGS,
+ DZL_FILE_TRANSFER_FLAGS_NONE,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_PROGRESS] =
+ g_param_spec_double ("progress",
+ "Progress",
+ "The transfer progress, from 0 to 1",
+ 0.0, 1.0, 0.0,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+dzl_file_transfer_init (DzlFileTransfer *self)
+{
+ DzlFileTransferPrivate *priv = dzl_file_transfer_get_instance_private (self);
+
+ priv->opers = g_ptr_array_new_with_free_func (oper_free);
+}
+
+DzlFileTransfer *
+dzl_file_transfer_new (void)
+{
+ return g_object_new (DZL_TYPE_FILE_TRANSFER, NULL);
+}
+
+void
+dzl_file_transfer_add (DzlFileTransfer *self,
+ GFile *src,
+ GFile *dst)
+{
+ DzlFileTransferPrivate *priv = dzl_file_transfer_get_instance_private (self);
+ Oper *oper;
+
+ DZL_ENTRY;
+
+ g_return_if_fail (DZL_IS_FILE_TRANSFER (self));
+ g_return_if_fail (G_IS_FILE (src));
+ g_return_if_fail (G_IS_FILE (dst));
+
+ if (priv->executed)
+ {
+ g_warning ("Cannot add files to transfer after executing");
+ DZL_EXIT;
+ }
+
+ oper = g_slice_new0 (Oper);
+ oper->src = g_object_ref (src);
+ oper->dst = g_object_ref (dst);
+ oper->self = self;
+
+ g_assert (priv->opers != NULL);
+
+ g_ptr_array_add (priv->opers, oper);
+
+ DZL_EXIT;
+}
+
+DzlFileTransferFlags
+dzl_file_transfer_get_flags (DzlFileTransfer *self)
+{
+ DzlFileTransferPrivate *priv = dzl_file_transfer_get_instance_private (self);
+
+ g_return_val_if_fail (DZL_IS_FILE_TRANSFER (self), 0);
+
+ return priv->flags;
+}
+
+void
+dzl_file_transfer_set_flags (DzlFileTransfer *self,
+ DzlFileTransferFlags flags)
+{
+ DzlFileTransferPrivate *priv = dzl_file_transfer_get_instance_private (self);
+
+ g_return_if_fail (DZL_IS_FILE_TRANSFER (self));
+
+ if (priv->executed)
+ {
+ g_warning ("Cannot set flags after executing transfer");
+ return;
+ }
+
+ if (priv->flags != flags)
+ {
+ priv->flags = flags;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_FLAGS]);
+ }
+}
+
+gdouble
+dzl_file_transfer_get_progress (DzlFileTransfer *self)
+{
+ DzlFileTransferPrivate *priv = dzl_file_transfer_get_instance_private (self);
+
+ g_return_val_if_fail (DZL_IS_FILE_TRANSFER (self), 0.0);
+
+ if (priv->stat_buf.n_bytes_total != 0)
+ return (gdouble)priv->stat_buf.n_bytes / (gdouble)priv->stat_buf.n_bytes_total;
+
+ return 0.0;
+}
+
+static void
+file_walk_full (GFile *parent,
+ GFileInfo *info,
+ GCancellable *cancellable,
+ FileWalkCallback callback,
+ gpointer user_data)
+{
+ g_assert (!parent || G_IS_FILE (parent));
+ g_assert (G_IS_FILE_INFO (info));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_assert (callback != NULL);
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ callback (parent, info, user_data);
+
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
+ {
+ g_autoptr(GFileEnumerator) enumerator = NULL;
+ g_autoptr(GFile) child = NULL;
+ const gchar *name = g_file_info_get_name (info);
+
+ if (name == NULL)
+ return;
+
+ child = g_file_get_child (parent, name);
+ enumerator = g_file_enumerate_children (child, QUERY_ATTRS, QUERY_FLAGS, cancellable, NULL);
+
+ if (enumerator != NULL)
+ {
+ gpointer infoptr;
+
+ while (NULL != (infoptr = g_file_enumerator_next_file (enumerator, cancellable, NULL)))
+ {
+ g_autoptr(GFileInfo) grandchild_info = infoptr;
+ file_walk_full (child, grandchild_info, cancellable, callback, user_data);
+ }
+
+ g_file_enumerator_close (enumerator, cancellable, NULL);
+ }
+ }
+}
+
+static void
+file_walk (GFile *root,
+ GCancellable *cancellable,
+ FileWalkCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GFile) parent = NULL;
+ g_autoptr(GFileInfo) info = NULL;
+
+ g_assert (G_IS_FILE (root));
+ g_assert (callback != NULL);
+
+ parent = g_file_get_parent (root);
+ if (g_file_equal (root, parent))
+ g_clear_object (&parent);
+
+ info = g_file_query_info (root, QUERY_ATTRS, QUERY_FLAGS, cancellable, NULL);
+ if (info != NULL)
+ file_walk_full (parent, info, cancellable, callback, user_data);
+}
+
+static void
+handle_preflight_cb (GFile *file,
+ GFileInfo *child_info,
+ gpointer user_data)
+{
+ DzlFileTransferStat *stat_buf = user_data;
+ GFileType file_type;
+
+ g_assert (G_IS_FILE (file));
+ g_assert (G_IS_FILE_INFO (child_info));
+ g_assert (stat_buf != NULL);
+
+ file_type = g_file_info_get_file_type (child_info);
+
+ if (file_type == G_FILE_TYPE_DIRECTORY)
+ {
+ stat_buf->n_dirs_total++;
+ }
+ else if (file_type == G_FILE_TYPE_REGULAR)
+ {
+ stat_buf->n_files_total++;
+ stat_buf->n_bytes_total += g_file_info_get_size (child_info);
+ }
+}
+
+static void
+handle_preflight (DzlFileTransfer *self,
+ GPtrArray *opers,
+ GCancellable *cancellable)
+{
+ DzlFileTransferPrivate *priv = dzl_file_transfer_get_instance_private (self);
+
+ g_assert (DZL_IS_FILE_TRANSFER (self));
+ g_assert (opers != NULL);
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ for (guint i = 0; i < opers->len; i++)
+ {
+ Oper *oper = g_ptr_array_index (opers, i);
+
+ g_assert (oper != NULL);
+ g_assert (DZL_IS_FILE_TRANSFER (oper->self));
+ g_assert (G_IS_FILE (oper->src));
+ g_assert (G_IS_FILE (oper->dst));
+
+ file_walk (oper->src, cancellable, handle_preflight_cb, &priv->stat_buf);
+
+ if (oper->error != NULL)
+ break;
+ }
+}
+
+static void
+dzl_file_transfer_progress_cb (goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data)
+{
+ DzlFileTransfer *self = user_data;
+ DzlFileTransferPrivate *priv = dzl_file_transfer_get_instance_private (self);
+
+ priv->stat_buf.n_bytes += (current_num_bytes - priv->last_num_bytes);
+}
+
+static void
+handle_copy_cb (GFile *file,
+ GFileInfo *child_info,
+ gpointer user_data)
+{
+ DzlFileTransferPrivate *priv;
+ g_autoptr(GFile) src = NULL;
+ g_autoptr(GFile) dst = NULL;
+ const gchar *name;
+ Oper *oper = user_data;
+ GFileType file_type;
+
+ g_assert (DZL_IS_FILE_TRANSFER (oper->self));
+ g_assert (G_IS_FILE (oper->src));
+ g_assert (G_IS_FILE (oper->dst));
+ g_assert (G_IS_FILE (file));
+ g_assert (G_IS_FILE_INFO (child_info));
+
+ if (oper->error != NULL)
+ return;
+
+ if (g_cancellable_is_cancelled (oper->cancellable))
+ return;
+
+ priv = dzl_file_transfer_get_instance_private (oper->self);
+
+ file_type = g_file_info_get_file_type (child_info);
+ name = g_file_info_get_name (child_info);
+
+ if (name == NULL)
+ return;
+
+ src = g_file_get_child (file, name);
+
+ if (!g_file_equal (oper->src, src))
+ {
+ g_autofree gchar *relative = NULL;
+
+ relative = g_file_get_relative_path (oper->src, src);
+ dst = g_file_get_child (oper->dst, relative);
+ }
+ else
+ {
+ dst = g_object_ref (oper->dst);
+ }
+
+ priv->last_num_bytes = 0;
+
+ switch (file_type)
+ {
+ case G_FILE_TYPE_DIRECTORY:
+ g_file_make_directory_with_parents (dst, oper->cancellable, &oper->error);
+ break;
+
+ case G_FILE_TYPE_REGULAR:
+ case G_FILE_TYPE_SPECIAL:
+ case G_FILE_TYPE_SHORTCUT:
+ case G_FILE_TYPE_SYMBOLIC_LINK:
+ /* Try to use g_file_move() when we can */
+ if ((oper->flags & DZL_FILE_TRANSFER_FLAGS_MOVE) != 0)
+ g_file_move (src, dst,
+ G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA,
+ oper->cancellable,
+ dzl_file_transfer_progress_cb,
+ oper->self,
+ &oper->error);
+ else
+ g_file_copy (src, dst,
+ G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA,
+ oper->cancellable,
+ dzl_file_transfer_progress_cb,
+ oper->self,
+ &oper->error);
+ break;
+
+ case G_FILE_TYPE_UNKNOWN:
+ case G_FILE_TYPE_MOUNTABLE:
+ default:
+ break;
+ }
+}
+
+static void
+handle_copy (DzlFileTransfer *self,
+ GPtrArray *opers,
+ GCancellable *cancellable)
+{
+ g_assert (DZL_IS_FILE_TRANSFER (self));
+ g_assert (opers != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ for (guint i = 0; i < opers->len; i++)
+ {
+ Oper *oper = g_ptr_array_index (opers, i);
+
+ g_assert (oper != NULL);
+ g_assert (G_IS_FILE (oper->src));
+ g_assert (G_IS_FILE (oper->dst));
+
+ oper->self = self;
+ oper->cancellable = cancellable;
+
+ if (oper->error == NULL)
+ {
+ file_walk (oper->src, cancellable, handle_copy_cb, oper);
+
+ if (oper->error != NULL)
+ break;
+ }
+ }
+}
+
+static void
+handle_removal (DzlFileTransfer *self,
+ GPtrArray *opers,
+ GCancellable *cancellable)
+{
+ g_autoptr(DzlDirectoryReaper) reaper = NULL;
+
+ g_assert (DZL_IS_FILE_TRANSFER (self));
+ g_assert (opers != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ reaper = dzl_directory_reaper_new ();
+
+ for (guint i = 0; i < opers->len; i++)
+ {
+ Oper *oper = g_ptr_array_index (opers, i);
+
+ g_assert (oper != NULL);
+ g_assert (G_IS_FILE (oper->src));
+ g_assert (G_IS_FILE (oper->dst));
+
+ /* Don't delete anything if there was a failure */
+ if (oper->error != NULL)
+ return;
+
+ if (g_file_query_file_type (oper->src, 0, NULL) == G_FILE_TYPE_DIRECTORY)
+ dzl_directory_reaper_add_directory (reaper, oper->src, 0);
+
+ dzl_directory_reaper_add_file (reaper, oper->src, 0);
+ }
+
+ dzl_directory_reaper_execute (reaper, cancellable, NULL);
+}
+
+static void
+dzl_file_transfer_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ DzlFileTransfer *self = source_object;
+ DzlFileTransferPrivate *priv = dzl_file_transfer_get_instance_private (self);
+ GPtrArray *opers = task_data;
+
+ DZL_ENTRY;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (DZL_IS_FILE_TRANSFER (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_assert (opers != NULL);
+
+ /* TODO: Start GSource for notifies */
+
+ for (guint i = 0; i < opers->len; i++)
+ {
+ Oper *oper = g_ptr_array_index (opers, i);
+
+ oper->self = self;
+ oper->cancellable = cancellable;
+ oper->flags = priv->flags;
+ }
+
+ handle_preflight (self, opers, cancellable);
+ handle_copy (self, opers, cancellable);
+ if ((priv->flags & DZL_FILE_TRANSFER_FLAGS_MOVE) != 0)
+ handle_removal (self, opers, cancellable);
+
+ for (guint i = 0; i < opers->len; i++)
+ {
+ Oper *oper = g_ptr_array_index (opers, i);
+
+ if (oper->error != NULL)
+ {
+ g_task_return_error (task, g_steal_pointer (&oper->error));
+ DZL_EXIT;
+ }
+ }
+
+ g_task_return_boolean (task, TRUE);
+
+ /* TODO: Stop GSource for notifies */
+
+ DZL_EXIT;
+}
+
+gboolean
+dzl_file_transfer_execute (DzlFileTransfer *self,
+ gint io_priority,
+ GCancellable *cancellable,
+ GError **error)
+{
+ DzlFileTransferPrivate *priv = dzl_file_transfer_get_instance_private (self);
+ g_autoptr(GTask) task = NULL;
+ gboolean ret;
+
+ DZL_ENTRY;
+
+ g_return_val_if_fail (DZL_IS_FILE_TRANSFER (self), FALSE);
+ g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+
+ task = g_task_new (self, cancellable, NULL, NULL);
+ g_task_set_source_tag (task, dzl_file_transfer_execute);
+
+ if (priv->executed)
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_INVAL,
+ "Transfer can only be executed once.");
+ DZL_RETURN (FALSE);
+ }
+
+ g_task_set_check_cancellable (task, TRUE);
+ g_task_set_return_on_cancel (task, TRUE);
+ g_task_set_priority (task, io_priority);
+ g_task_set_task_data (task, g_steal_pointer (&priv->opers), (GDestroyNotify)g_ptr_array_unref);
+ g_task_run_in_thread_sync (task, dzl_file_transfer_worker);
+
+ ret = g_task_propagate_boolean (task, error);
+
+ DZL_RETURN (ret);
+}
+
+void
+dzl_file_transfer_execute_async (DzlFileTransfer *self,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ DzlFileTransferPrivate *priv = dzl_file_transfer_get_instance_private (self);
+ g_autoptr(GTask) task = NULL;
+
+ DZL_ENTRY;
+
+ g_return_if_fail (DZL_IS_FILE_TRANSFER (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, NULL, NULL);
+ g_task_set_source_tag (task, dzl_file_transfer_execute);
+
+ if (priv->executed)
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_INVAL,
+ "Transfer can only be executed once.");
+ DZL_EXIT;
+ }
+
+ priv->executed = TRUE;
+
+ g_task_set_check_cancellable (task, TRUE);
+ g_task_set_return_on_cancel (task, TRUE);
+ g_task_set_priority (task, io_priority);
+ g_task_set_task_data (task, g_steal_pointer (&priv->opers), (GDestroyNotify)g_ptr_array_unref);
+ g_task_run_in_thread (task, dzl_file_transfer_worker);
+
+ DZL_EXIT;
+}
+
+gboolean
+dzl_file_transfer_execute_finish (DzlFileTransfer *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ gboolean ret;
+
+ DZL_ENTRY;
+
+ g_return_val_if_fail (DZL_IS_FILE_TRANSFER (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+ g_return_val_if_fail (g_task_is_valid (G_TASK (result), self), FALSE);
+
+ ret = g_task_propagate_boolean (G_TASK (result), error);
+
+ DZL_RETURN (ret);
+}
+
+/**
+ * dzl_file_transfer_stat:
+ * @self: a #DzlFileTransfer
+ * @stat_buf: (out): a #DzlFileTransferStat
+ *
+ * Gets statistics about the transfer progress.
+ *
+ * Since: 3.28
+ */
+void
+dzl_file_transfer_stat (DzlFileTransfer *self,
+ DzlFileTransferStat *stat_buf)
+{
+ DzlFileTransferPrivate *priv = dzl_file_transfer_get_instance_private (self);
+
+ g_return_if_fail (DZL_IS_FILE_TRANSFER (self));
+ g_return_if_fail (stat_buf != NULL);
+
+ *stat_buf = priv->stat_buf;
+}
diff --git a/src/files/dzl-file-transfer.h b/src/files/dzl-file-transfer.h
new file mode 100644
index 0000000..ad18080
--- /dev/null
+++ b/src/files/dzl-file-transfer.h
@@ -0,0 +1,79 @@
+/* dzl-file-transfer.h
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define DZL_TYPE_FILE_TRANSFER (dzl_file_transfer_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (DzlFileTransfer, dzl_file_transfer, DZL, FILE_TRANSFER, GObject)
+
+struct _DzlFileTransferClass
+{
+ GObjectClass parent_class;
+
+ /*< private >*/
+ gpointer _padding[12];
+};
+
+typedef enum
+{
+ DZL_FILE_TRANSFER_FLAGS_NONE = 0,
+ DZL_FILE_TRANSFER_FLAGS_MOVE = 1 << 0,
+} DzlFileTransferFlags;
+
+typedef struct
+{
+ gint64 n_files_total;
+ gint64 n_files;
+ gint64 n_dirs_total;
+ gint64 n_dirs;
+ gint64 n_bytes_total;
+ gint64 n_bytes;
+
+ /*< private >*/
+ gint64 _padding[10];
+} DzlFileTransferStat;
+
+DzlFileTransfer *dzl_file_transfer_new (void);
+DzlFileTransferFlags dzl_file_transfer_get_flags (DzlFileTransfer *self);
+void dzl_file_transfer_set_flags (DzlFileTransfer *self,
+ DzlFileTransferFlags flags);
+gdouble dzl_file_transfer_get_progress (DzlFileTransfer *self);
+void dzl_file_transfer_stat (DzlFileTransfer *self,
+ DzlFileTransferStat *stat_buf);
+void dzl_file_transfer_add (DzlFileTransfer *self,
+ GFile *src,
+ GFile *dest);
+void dzl_file_transfer_execute_async (DzlFileTransfer *self,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean dzl_file_transfer_execute_finish (DzlFileTransfer *self,
+ GAsyncResult *result,
+ GError **error);
+gboolean dzl_file_transfer_execute (DzlFileTransfer *self,
+ gint io_priority,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
diff --git a/src/files/meson.build b/src/files/meson.build
index b574b15..49d7b0d 100644
--- a/src/files/meson.build
+++ b/src/files/meson.build
@@ -1,16 +1,23 @@
files_headers = [
'dzl-directory-model.h',
'dzl-directory-reaper.h',
+ 'dzl-file-transfer.h',
'dzl-recursive-file-monitor.h',
]
files_sources = [
'dzl-directory-model.c',
'dzl-directory-reaper.c',
+ 'dzl-file-transfer.c',
'dzl-recursive-file-monitor.c',
]
+files_enums_headers = [
+ 'dzl-file-transfer.h',
+]
+
libdazzle_public_headers += files(files_headers)
libdazzle_public_sources += files(files_sources)
+dzl_enum_headers += files(files_enums_headers)
install_headers(files_headers, subdir: join_paths(libdazzle_header_subdir, 'files'))
diff --git a/src/meson.build b/src/meson.build
index 6a4dd5a..c7732fc 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -9,6 +9,8 @@ libdazzle_resources = gnome.compile_resources(
c_name: 'dzl',
)
+dzl_enum_headers = []
+
version_data = configuration_data()
version_data.set('DZL_MAJOR_VERSION', dazzle_version_major)
version_data.set('DZL_MINOR_VERSION', dazzle_version_minor)
@@ -64,6 +66,16 @@ subdir('tree')
subdir('util')
subdir('widgets')
+dzl_enums = gnome.mkenums('dzl-enums',
+ h_template: 'dzl-enums.h.in',
+ c_template: 'dzl-enums.c.in',
+ sources: dzl_enum_headers,
+ install_header: true,
+ install_dir: libdazzle_header_dir,
+)
+libdazzle_public_sources += [dzl_enums[0]]
+libdazzle_generated_headers += [dzl_enums[1]]
+
libdazzle_sources = [
libdazzle_generated_headers,
libdazzle_public_sources,
diff --git a/tests/meson.build b/tests/meson.build
index 7097ddb..ac8dd20 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -350,6 +350,13 @@ test_recursive_monitor = executable('test-recursive-monitor', 'test-recursive-mo
)
test('test-recursive-monitor', test_recursive_monitor, env: test_env)
+test_file_transfer = executable('test-file-transfer', 'test-file-transfer.c',
+ c_args: test_cflags,
+ link_args: test_link_args,
+ dependencies: libdazzle_deps + [libdazzle_dep],
+)
+test('test-file-transfer', test_file_transfer, env: test_env)
+
test_tree = executable('test-tree', 'test-tree.c',
c_args: test_cflags,
link_args: test_link_args,
diff --git a/tests/test-file-transfer.c b/tests/test-file-transfer.c
new file mode 100644
index 0000000..c6a4c71
--- /dev/null
+++ b/tests/test-file-transfer.c
@@ -0,0 +1,82 @@
+#include <dazzle.h>
+#include <glib/gstdio.h>
+
+static void
+write_file (const gchar *path)
+{
+ g_autoptr(GFile) file = g_file_new_for_path (path);
+ g_autoptr(GOutputStream) stream = NULL;
+ g_autoptr(GError) error = NULL;
+ gsize len = 0;
+
+ stream = G_OUTPUT_STREAM (g_file_create (file, G_FILE_CREATE_NONE, NULL, &error));
+ g_assert_no_error (error);
+ g_assert (G_IS_OUTPUT_STREAM (stream));
+ g_output_stream_write_all (stream, "some-data", strlen ("some-data"), &len, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpint (len, ==, strlen ("some-data"));
+}
+
+static void
+test_basic (void)
+{
+ g_autoptr(DzlFileTransfer) xfer = NULL;
+ g_autoptr(GFile) root = g_file_new_for_path ("test-file-transfer-data");
+ g_autoptr(GFile) copy = g_file_new_for_path ("test-file-transfer-copy");
+ g_autoptr(GError) error = NULL;
+ gboolean r;
+
+ xfer = dzl_file_transfer_new ();
+
+ if (g_file_query_exists (root, NULL))
+ {
+ g_autoptr(DzlDirectoryReaper) reaper = dzl_directory_reaper_new ();
+
+ dzl_directory_reaper_add_directory (reaper, root, 0);
+ dzl_directory_reaper_add_file (reaper, root, 0);
+ dzl_directory_reaper_execute (reaper, NULL, NULL);
+
+ g_assert (!g_file_query_exists (root, NULL));
+ }
+
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/a", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/a/1", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/a/1/a", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/a/1/b", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/a/1/c", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/a/2", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/a/2/a", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/a/2/b", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/a/2/c", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/b", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/b/1", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/b/1/a", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/b/1/b", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/b/1/c", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/c", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/c/1", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/c/1/a", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/c/1/b", 0750));
+ g_assert_cmpint (0, ==, g_mkdir ("test-file-transfer-data/c/1/c", 0750));
+
+ write_file ("test-file-transfer-data/z");
+ write_file ("test-file-transfer-data/a/z");
+ write_file ("test-file-transfer-data/b/1/c/z");
+ write_file ("test-file-transfer-data/c/1/c/z");
+
+ dzl_file_transfer_set_flags (xfer, DZL_FILE_TRANSFER_FLAGS_MOVE);
+ dzl_file_transfer_add (xfer, root, copy);
+ r = dzl_file_transfer_execute (xfer, G_PRIORITY_DEFAULT, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpint (r, ==, TRUE);
+}
+
+gint
+main (gint argc,
+ gchar *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+ g_test_add_func ("/Dazzle/FileTransfer/basic", test_basic);
+ return g_test_run ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]