]>
Commit | Line | Data |
---|---|---|
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 | ||
14 | static cmdinfo_t copy_range_cmd; | |
15 | ||
16 | static void | |
17 | copy_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 | 38 | static loff_t |
64e366d9 | 39 | copy_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 | ||
57 | static off64_t | |
58 | copy_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 |
69 | static int |
70 | copy_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(©_range_cmd); |
628e112a AS |
138 | } |
139 | } | |
140 | ||
9e1595e6 DC |
141 | if (optind != argc - src_path_arg) { |
142 | exitcode = 1; | |
628e112a | 143 | return command_usage(©_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) { |
30cc0fda DW |
157 | off64_t sz; |
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 | 169 | out: |
628e112a | 170 | close(fd); |
9e1595e6 DC |
171 | if (ret < 0) |
172 | exitcode = 1; | |
628e112a AS |
173 | return ret; |
174 | } | |
175 | ||
176 | void | |
177 | copy_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(©_range_cmd); | |
189 | } |