]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_io: implement 'copy_range' command
authorAnna Schumaker <Anna.Schumaker@netapp.com>
Wed, 20 Jul 2016 05:31:54 +0000 (15:31 +1000)
committerDave Chinner <david@fromorbit.com>
Wed, 20 Jul 2016 05:31:54 +0000 (15:31 +1000)
Implements a new xfs_io command, named 'copy_range', which is supposed
to be used to copy a range of data from one file to another.

Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
configure.ac
include/builddefs.in
io/Makefile
io/copy_file_range.c [new file with mode: 0644]
io/init.c
io/io.h
m4/package_libcdev.m4

index 83f90206d1cdba120a30f7530e6c85fc3cb58f92..464b3147869be0fb50b65104b3ae7e7ff692072e 100644 (file)
@@ -122,6 +122,7 @@ AC_HAVE_GETMNTINFO
 AC_HAVE_FALLOCATE
 AC_HAVE_FIEMAP
 AC_HAVE_PREADV
+AC_HAVE_COPY_FILE_RANGE
 AC_HAVE_SYNC_FILE_RANGE
 AC_HAVE_MNTENT
 AC_HAVE_FLS
index b5ce336aa990b37f068bd2aca4b61c79f4ceebb9..7153d7a76132140596055dbd29c95a67c565e14c 100644 (file)
@@ -102,6 +102,7 @@ HAVE_GETMNTINFO = @have_getmntinfo@
 HAVE_FALLOCATE = @have_fallocate@
 HAVE_FIEMAP = @have_fiemap@
 HAVE_PREADV = @have_preadv@
+HAVE_COPY_FILE_RANGE = @have_copy_file_range@
 HAVE_SYNC_FILE_RANGE = @have_sync_file_range@
 HAVE_READDIR = @have_readdir@
 HAVE_MNTENT = @have_mntent@
index 0b53f4187ba9eee4fc34cc2462fac3f89a78f4dc..62bc03b8fdd4a407a573a7f59bd0634a122181cd 100644 (file)
@@ -59,6 +59,11 @@ CFILES += inject.c resblks.c
 LCFLAGS += -DHAVE_INJECT -DHAVE_RESBLKS
 endif
 
+ifeq ($(HAVE_COPY_FILE_RANGE),yes)
+CFILES += copy_file_range.c
+LCFLAGS += -DHAVE_COPY_FILE_RANGE
+endif
+
 ifeq ($(HAVE_SYNC_FILE_RANGE),yes)
 CFILES += sync_file_range.c
 LCFLAGS += -DHAVE_SYNC_FILE_RANGE
diff --git a/io/copy_file_range.c b/io/copy_file_range.c
new file mode 100644 (file)
index 0000000..eddc634
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ *  Copyright (c) 2016 Netapp, Inc. All rights reserved.
+ *
+ *  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 2 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <sys/syscall.h>
+#include <sys/uio.h>
+#include <xfs/xfs.h>
+#include "command.h"
+#include "input.h"
+#include "init.h"
+#include "io.h"
+
+static cmdinfo_t copy_range_cmd;
+
+static void
+copy_range_help(void)
+{
+       printf(_("\n\
+ Copies a range of bytes from a file into the open file, overwriting any data\n\
+ already there.\n\
+\n\
+ Example:\n\
+ 'copy_range -s 100 -d 200 -l 300 some_file' - copies 300 bytes from some_file\n\
+                                               at offset 100 into the open\n\
+                                              file at offset 200\n\
+ 'copy_range some_file' - copies all bytes from some_file into the open file\n\
+                          at position 0\n\
+"));
+}
+
+static loff_t
+copy_file_range(int fd, loff_t *src, loff_t *dst, size_t len)
+{
+       loff_t ret;
+
+       do {
+               ret = syscall(__NR_copy_file_range, fd, src, file->fd, dst, len, 0);
+               if (ret == -1)
+                       return errno;
+               len -= ret;
+       } while (len > 0);
+
+       return 0;
+}
+
+static off64_t
+copy_src_filesize(int fd)
+{
+       struct stat64 st;
+
+       if (fstat64(fd, &st) < 0) {
+               perror("fstat64");
+               return -1;
+       };
+       return st.st_size;
+}
+
+static int
+copy_dst_truncate(void)
+{
+       int ret = ftruncate64(file->fd, 0);
+       if (ret < 0)
+               perror("ftruncate64");
+       return ret;
+}
+
+static int
+copy_range_f(int argc, char **argv)
+{
+       loff_t src = 0;
+       loff_t dst = 0;
+       size_t len = 0;
+       char *sp;
+       int opt;
+       int ret;
+       int fd;
+
+       while ((opt = getopt(argc, argv, "s:d:l:")) != -1) {
+               switch (opt) {
+               case 's':
+                       src = strtoull(optarg, &sp, 10);
+                       if (!sp || sp == optarg) {
+                               printf(_("invalid source offset -- %s\n"), sp);
+                               return 0;
+                       }
+                       break;
+               case 'd':
+                       dst = strtoull(optarg, &sp, 10);
+                       if (!sp || sp == optarg) {
+                               printf(_("invalid destination offset -- %s\n"), sp);
+                               return 0;
+                       }
+                       break;
+               case 'l':
+                       len = strtoull(optarg, &sp, 10);
+                       if (!sp || sp == optarg) {
+                               printf(_("invalid length -- %s\n"), sp);
+                               return 0;
+                       }
+                       break;
+               }
+       }
+
+       if (optind != argc - 1)
+               return command_usage(&copy_range_cmd);
+
+       fd = openfile(argv[optind], NULL, IO_READONLY, 0);
+       if (fd < 0)
+               return 0;
+
+       if (src == 0 && dst == 0 && len == 0) {
+               len = copy_src_filesize(fd);
+               copy_dst_truncate();
+       }
+
+       ret = copy_file_range(fd, &src, &dst, len);
+       close(fd);
+       return ret;
+}
+
+void
+copy_range_init(void)
+{
+       copy_range_cmd.name = "copy_range";
+       copy_range_cmd.cfunc = copy_range_f;
+       copy_range_cmd.argmin = 1;
+       copy_range_cmd.argmax = 7;
+       copy_range_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
+       copy_range_cmd.args = _("[-s src_off] [-d dst_off] [-l len] src_file");
+       copy_range_cmd.oneline = _("Copy a range of data between two files");
+       copy_range_cmd.help = copy_range_help;
+
+       add_command(&copy_range_cmd);
+}
index 51f1f5c96feb448f5dd0f0a4838f070e84e7d7eb..efe739051eb1dfd358b97f682384128cad7279a0 100644 (file)
--- a/io/init.c
+++ b/io/init.c
@@ -56,6 +56,7 @@ init_commands(void)
 {
        attr_init();
        bmap_init();
+       copy_range_init();
        fadvise_init();
        file_init();
        flink_init();
diff --git a/io/io.h b/io/io.h
index 172b1f84760fbee677c5a330332ad3934aea4391..2bc7ac4a14857ba6ba0fe4b692eedee52bced787 100644 (file)
--- a/io/io.h
+++ b/io/io.h
@@ -150,6 +150,12 @@ extern void                fiemap_init(void);
 #define fiemap_init()  do { } while (0)
 #endif
 
+#ifdef HAVE_COPY_FILE_RANGE
+extern void            copy_range_init(void);
+#else
+#define copy_range_init()      do { } while (0)
+#endif
+
 #ifdef HAVE_SYNC_FILE_RANGE
 extern void            sync_range_init(void);
 #else
index 016531076afd31c762fd049acd2a7b59ecc1dd51..7a847e91d482dadb773ece0dee7da2909d2ed8e4 100644 (file)
@@ -153,6 +153,23 @@ AC_DEFUN([AC_HAVE_PREADV],
     AC_SUBST(have_preadv)
   ])
 
+#
+# Check if we have a copy_file_range system call (Linux)
+#
+AC_DEFUN([AC_HAVE_COPY_FILE_RANGE],
+  [ AC_MSG_CHECKING([for copy_file_range])
+    AC_TRY_LINK([
+#define _GNU_SOURCE
+#include <sys/syscall.h>
+#include <unistd.h>
+    ], [
+         syscall(__NR_copy_file_range, 0, 0, 0, 0, 0, 0);
+    ], have_copy_file_range=yes
+       AC_MSG_RESULT(yes),
+       AC_MSG_RESULT(no))
+    AC_SUBST(have_copy_file_range)
+  ])
+
 #
 # Check if we have a sync_file_range libc call (Linux)
 #