@@ -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
@@ -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@
@@ -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
new file mode 100644
@@ -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(©_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(©_range_cmd);
+}
@@ -56,6 +56,7 @@ init_commands(void)
{
attr_init();
bmap_init();
+ copy_range_init();
fadvise_init();
file_init();
flink_init();
@@ -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
@@ -154,6 +154,23 @@ AC_DEFUN([AC_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)
#
AC_DEFUN([AC_HAVE_SYNC_FILE_RANGE],
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> --- configure.ac | 1 + include/builddefs.in | 1 + io/Makefile | 5 ++ io/copy_file_range.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++ io/init.c | 1 + io/io.h | 6 ++ m4/package_libcdev.m4 | 17 ++++++ 7 files changed, 179 insertions(+) create mode 100644 io/copy_file_range.c