]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - io/copy_file_range.c
xfsprogs: Release v6.7.0
[thirdparty/xfsprogs-dev.git] / io / copy_file_range.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0+
628e112a
AS
2/*
3 * Copyright (c) 2016 Netapp, Inc. All rights reserved.
628e112a
AS
4 */
5
6#include <sys/syscall.h>
7#include <sys/uio.h>
8#include <xfs/xfs.h>
9#include "command.h"
10#include "input.h"
11#include "init.h"
12#include "io.h"
13
14static cmdinfo_t copy_range_cmd;
15
16static void
17copy_range_help(void)
18{
19 printf(_("\n\
20 Copies a range of bytes from a file into the open file, overwriting any data\n\
21 already there.\n\
22\n\
23 Example:\n\
24 'copy_range -s 100 -d 200 -l 300 some_file' - copies 300 bytes from some_file\n\
25 at offset 100 into the open\n\
26 file at offset 200\n\
27 'copy_range some_file' - copies all bytes from some_file into the open file\n\
28 at position 0\n\
10d4ca4a
AG
29 'copy_range -f 2' - copies all bytes from open file 2 into the current open file\n\
30 at position 0\n\
628e112a
AS
31"));
32}
33
8041435d
DW
34/*
35 * Issue a raw copy_file_range syscall; for our test program we don't want the
36 * glibc buffered copy fallback.
37 */
628e112a 38static loff_t
64e366d9 39copy_file_range_cmd(int fd, long long *src_off, long long *dst_off, size_t len)
628e112a
AS
40{
41 loff_t ret;
42
43 do {
64e366d9
JY
44 ret = syscall(__NR_copy_file_range, fd, src_off,
45 file->fd, dst_off, len, 0);
43141802
AS
46 if (ret == -1) {
47 perror("copy_range");
628e112a 48 return errno;
43141802
AS
49 } else if (ret == 0)
50 break;
628e112a
AS
51 len -= ret;
52 } while (len > 0);
53
54 return 0;
55}
56
9e726740 57static off_t
628e112a
AS
58copy_src_filesize(int fd)
59{
f594a0d1 60 struct stat st;
628e112a 61
f594a0d1
FJ
62 if (fstat(fd, &st) < 0) {
63 perror("fstat");
628e112a
AS
64 return -1;
65 };
66 return st.st_size;
67}
68
628e112a
AS
69static int
70copy_range_f(int argc, char **argv)
71{
64e366d9
JY
72 long long src_off = 0;
73 long long dst_off = 0;
2dd63108 74 long long llen;
2a42470b 75 size_t len = 0;
64e366d9 76 bool len_specified = false;
628e112a
AS
77 int opt;
78 int ret;
79 int fd;
328148f3
ES
80 int src_path_arg = 1;
81 int src_file_nr = 0;
25b4549c
GR
82 size_t fsblocksize, fssectsize;
83
84 init_cvtnum(&fsblocksize, &fssectsize);
628e112a 85
10d4ca4a 86 while ((opt = getopt(argc, argv, "s:d:l:f:")) != -1) {
628e112a
AS
87 switch (opt) {
88 case 's':
64e366d9
JY
89 src_off = cvtnum(fsblocksize, fssectsize, optarg);
90 if (src_off < 0) {
25b4549c 91 printf(_("invalid source offset -- %s\n"), optarg);
9e1595e6 92 exitcode = 1;
628e112a
AS
93 return 0;
94 }
95 break;
96 case 'd':
64e366d9
JY
97 dst_off = cvtnum(fsblocksize, fssectsize, optarg);
98 if (dst_off < 0) {
25b4549c 99 printf(_("invalid destination offset -- %s\n"), optarg);
9e1595e6 100 exitcode = 1;
628e112a
AS
101 return 0;
102 }
103 break;
104 case 'l':
2dd63108
DW
105 llen = cvtnum(fsblocksize, fssectsize, optarg);
106 if (llen == -1LL) {
25b4549c 107 printf(_("invalid length -- %s\n"), optarg);
9e1595e6 108 exitcode = 1;
628e112a
AS
109 return 0;
110 }
2dd63108
DW
111 /*
112 * If size_t can't hold what's in llen, report a
113 * length overflow.
114 */
115 if ((size_t)llen != llen) {
116 errno = EOVERFLOW;
117 perror("copy_range");
9e1595e6 118 exitcode = 1;
2dd63108
DW
119 return 0;
120 }
121 len = llen;
64e366d9 122 len_specified = true;
628e112a 123 break;
10d4ca4a 124 case 'f':
328148f3
ES
125 src_file_nr = atoi(argv[1]);
126 if (src_file_nr < 0 || src_file_nr >= filecount) {
127 printf(_("file value %d is out of range (0-%d)\n"),
128 src_file_nr, filecount - 1);
9e1595e6 129 exitcode = 1;
10d4ca4a
AG
130 return 0;
131 }
328148f3
ES
132 /* Expect no src_path arg */
133 src_path_arg = 0;
10d4ca4a 134 break;
78aeaffd 135 default:
9e1595e6 136 exitcode = 1;
78aeaffd 137 return command_usage(&copy_range_cmd);
628e112a
AS
138 }
139 }
140
9e1595e6
DC
141 if (optind != argc - src_path_arg) {
142 exitcode = 1;
628e112a 143 return command_usage(&copy_range_cmd);
9e1595e6 144 }
628e112a 145
328148f3 146 if (src_path_arg) {
10d4ca4a 147 fd = openfile(argv[optind], NULL, IO_READONLY, 0, NULL);
9e1595e6
DC
148 if (fd < 0) {
149 exitcode = 1;
10d4ca4a 150 return 0;
9e1595e6 151 }
328148f3
ES
152 } else {
153 fd = filetable[src_file_nr].fd;
10d4ca4a 154 }
628e112a 155
64e366d9 156 if (!len_specified) {
9e726740 157 off_t sz;
30cc0fda
DW
158
159 sz = copy_src_filesize(fd);
160 if (sz < 0 || (unsigned long long)sz > SIZE_MAX) {
161 ret = 1;
162 goto out;
163 }
64e366d9
JY
164 if (sz > src_off)
165 len = sz - src_off;
628e112a
AS
166 }
167
64e366d9 168 ret = copy_file_range_cmd(fd, &src_off, &dst_off, len);
30cc0fda 169out:
628e112a 170 close(fd);
9e1595e6
DC
171 if (ret < 0)
172 exitcode = 1;
628e112a
AS
173 return ret;
174}
175
176void
177copy_range_init(void)
178{
179 copy_range_cmd.name = "copy_range";
180 copy_range_cmd.cfunc = copy_range_f;
181 copy_range_cmd.argmin = 1;
baed08dd 182 copy_range_cmd.argmax = 8;
628e112a 183 copy_range_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
10d4ca4a 184 copy_range_cmd.args = _("[-s src_off] [-d dst_off] [-l len] src_file | -f N");
628e112a
AS
185 copy_range_cmd.oneline = _("Copy a range of data between two files");
186 copy_range_cmd.help = copy_range_help;
187
188 add_command(&copy_range_cmd);
189}