From: Anna Schumaker Date: Wed, 20 Jul 2016 05:31:54 +0000 (+1000) Subject: xfs_io: implement 'copy_range' command X-Git-Tag: v4.7.0-rc2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;ds=sidebyside;h=628e112afdd98c5;hp=70a4820f5b800c690ba696230343446979decdc3;p=thirdparty%2Fxfsprogs-dev.git xfs_io: implement 'copy_range' command 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 Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner --- diff --git a/configure.ac b/configure.ac index 83f90206d..464b31478 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/include/builddefs.in b/include/builddefs.in index b5ce336aa..7153d7a76 100644 --- a/include/builddefs.in +++ b/include/builddefs.in @@ -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@ diff --git a/io/Makefile b/io/Makefile index 0b53f4187..62bc03b8f 100644 --- a/io/Makefile +++ b/io/Makefile @@ -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 index 000000000..eddc63436 --- /dev/null +++ b/io/copy_file_range.c @@ -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 +#include +#include +#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); +} diff --git a/io/init.c b/io/init.c index 51f1f5c96..efe739051 100644 --- 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 172b1f847..2bc7ac4a1 100644 --- 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 diff --git a/m4/package_libcdev.m4 b/m4/package_libcdev.m4 index 016531076..7a847e91d 100644 --- a/m4/package_libcdev.m4 +++ b/m4/package_libcdev.m4 @@ -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 +#include + ], [ + 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) #