From: Christian Kellner Date: Sun, 7 Feb 2010 16:23:38 +0000 (+0100) Subject: GFile: Support for splice(2) in copy_fallback X-Git-Tag: 2.23.4~32 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=bb4f63d6390fe5efd183f259e5bd891f89de9e24;p=platform%2Fupstream%2Fglib.git GFile: Support for splice(2) in copy_fallback The (linux specific) system call splice can be used to transfer data between file descriptors whitout copying them into user space. See bug #604086 for additional details. --- diff --git a/configure.in b/configure.in index 37416b4..258f8f6 100644 --- a/configure.in +++ b/configure.in @@ -969,6 +969,7 @@ AC_CHECK_FUNCS(chown lchmod lchown fchmod fchown link statvfs statfs utimes getg AC_CHECK_FUNCS(getmntent_r setmntent endmntent hasmntopt getmntinfo) # Check for high-resolution sleep functions AC_CHECK_FUNCS(nanosleep nsleep) +AC_CHECK_FUNCS(splice) AC_CHECK_HEADERS(crt_externs.h) AC_CHECK_FUNCS(_NSGetEnviron) diff --git a/gio/gfile.c b/gio/gfile.c index 5bd8915..9bad3f7 100644 --- a/gio/gfile.c +++ b/gio/gfile.c @@ -23,6 +23,13 @@ */ #include "config.h" +#ifdef HAVE_SPLICE +#define _GNU_SOURCE +#include +#include +#include +#include +#endif #include #include #ifdef HAVE_PWD_H @@ -33,6 +40,7 @@ #include "gioscheduler.h" #include "gsimpleasyncresult.h" #include "gfileattribute-priv.h" +#include "gfiledescriptorbased.h" #include "gpollfilemonitor.h" #include "gappinfo.h" #include "gfileinputstream.h" @@ -2120,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); } @@ -2628,7 +2636,6 @@ g_file_copy_attributes (GFile *source, return res; } -/* Closes the streams */ static gboolean copy_stream_with_progress (GInputStream *in, GOutputStream *out, @@ -2714,25 +2721,133 @@ 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)) + 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, @@ -2747,6 +2862,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, @@ -2814,13 +2933,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);