summaryrefslogtreecommitdiff
path: root/io.c
diff options
context:
space:
mode:
authorglass <glass@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2019-01-28 05:04:17 +0000
committerglass <glass@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2019-01-28 05:04:17 +0000
commit2210709b79222ecd2d9758f7316fdbd7c068ea84 (patch)
treeb96f0fd2231b84eb4f0ea6528c982b50414b4ae9 /io.c
parent774c60955a725e378caded5e074863ceddb27ca8 (diff)
io.c: use fcopyfile(3) in IO.copy_stream if available
fixed r66930. * io.c (nogvl_copy_stream_func): use fcopyfile(3) in IO.copy_stream if available * configure.ac: check copyfile.h and fcopyfile(3) git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66934 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'io.c')
-rw-r--r--io.c103
1 files changed, 103 insertions, 0 deletions
diff --git a/io.c b/io.c
index 35bf3b08bc..1559bedb6f 100644
--- a/io.c
+++ b/io.c
@@ -95,6 +95,10 @@
# include <sys/wait.h> /* for WNOHANG on BSD */
#endif
+#ifdef HAVE_COPYFILE_H
+# include <copyfile.h>
+#endif
+
#include "ruby/util.h"
#ifndef O_ACCMODE
@@ -10699,6 +10703,9 @@ struct copy_stream_struct {
const char *syserr;
const char *notimp;
VALUE th;
+#ifdef HAVE_FCOPYFILE
+ copyfile_state_t copyfile_state;
+#endif
};
static void *
@@ -10954,6 +10961,90 @@ nogvl_copy_file_range(struct copy_stream_struct *stp)
}
#endif
+#ifdef HAVE_FCOPYFILE
+static int
+nogvl_fcopyfile(struct copy_stream_struct *stp)
+{
+ struct stat sb;
+ off_t src_size, cur, ss = 0;
+ int ret;
+
+ if (stp->copy_length >= (off_t)0) {
+ /* copy_length can't be specified in copyfile(3) */
+ return 0;
+ }
+
+ ret = fstat(stp->src_fd, &sb);
+ if (ret < 0) {
+ stp->syserr = "fstat";
+ stp->error_no = errno;
+ return ret;
+ }
+ if (!S_ISREG(sb.st_mode))
+ return 0;
+
+ src_size = sb.st_size;
+ ret = fstat(stp->dst_fd, &sb);
+ if (ret < 0) {
+ stp->syserr = "fstat";
+ stp->error_no = errno;
+ return ret;
+ }
+ if (!S_ISREG(sb.st_mode))
+ return 0;
+ if (lseek(stp->dst_fd, 0, SEEK_CUR) > (off_t)0) /* if dst IO was already written */
+ return 0;
+
+ if (stp->src_offset > (off_t)0) {
+ off_t r;
+
+ /* get current offset */
+ errno = 0;
+ cur = lseek(stp->src_fd, 0, SEEK_CUR);
+ if (cur < (off_t)0 && errno) {
+ stp->error_no = errno;
+ return 1;
+ }
+
+ errno = 0;
+ r = lseek(stp->src_fd, stp->src_offset, SEEK_SET);
+ if (r < (off_t)0 && errno) {
+ stp->error_no = errno;
+ return 1;
+ }
+ }
+
+ stp->copyfile_state = copyfile_state_alloc(); /* this will be freed by copy_stream_finalize() */
+ ret = fcopyfile(stp->src_fd, stp->dst_fd, stp->copyfile_state, COPYFILE_DATA);
+ copyfile_state_get(stp->copyfile_state, COPYFILE_STATE_COPIED, &ss); /* get copied bytes */
+
+ if (ret == 0) { /* success */
+ stp->total = ss;
+ if (stp->src_offset > (off_t)0) {
+ off_t r;
+ errno = 0;
+ /* reset offset */
+ r = lseek(stp->src_fd, cur, SEEK_SET);
+ if (r < (off_t)0 && errno) {
+ stp->error_no = errno;
+ return 1;
+ }
+ }
+ } else {
+ switch (errno) {
+ case ENOTSUP:
+ case EPERM:
+ case EINVAL:
+ return 0;
+ }
+ stp->syserr = "fcopyfile";
+ stp->error_no = errno;
+ return (int)ret;
+ }
+ return 1;
+}
+#endif
+
#ifdef HAVE_SENDFILE
# ifdef __linux__
@@ -11263,6 +11354,12 @@ nogvl_copy_stream_func(void *arg)
goto finish; /* error or success */
#endif
+#ifdef HAVE_FCOPYFILE
+ ret = nogvl_fcopyfile(stp);
+ if (ret != 0)
+ goto finish; /* error or success */
+#endif
+
#ifdef USE_SENDFILE
ret = nogvl_copy_stream_sendfile(stp);
if (ret != 0)
@@ -11471,6 +11568,12 @@ copy_stream_finalize(VALUE arg)
{
struct copy_stream_struct *stp = (struct copy_stream_struct *)arg;
+#ifdef HAVE_FCOPYFILE
+ if (stp->copyfile_state) {
+ copyfile_state_free(stp->copyfile_state);
+ }
+#endif
+
if (stp->close_src) {
rb_io_close_m(stp->src);
}