*/
#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
#include "gioscheduler.h"
#include "gsimpleasyncresult.h"
#include "gfileattribute-priv.h"
+#include "gfiledescriptorbased.h"
#include "gpollfilemonitor.h"
#include "gappinfo.h"
#include "gfileinputstream.h"
if (g_simple_async_result_propagate_error (simple, error))
return NULL;
}
-
+
iface = G_FILE_GET_IFACE (file);
return (* iface->replace_finish) (file, res, error);
}
return res;
}
-/* Closes the streams */
static gboolean
copy_stream_with_progress (GInputStream *in,
GOutputStream *out,
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))
+ break;
+
+ if (!do_splice (buffer[0], NULL, fd_out, &offset_out, n_read, &n_written, error))
+ break;
+
+ 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);
+
+ close (buffer[0]);
+ close (buffer[1]);
- g_object_unref (in);
- g_object_unref (out);
-
return res;
}
+#endif
static gboolean
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,
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);