]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/reflink.c
xfs_scrub: clean up error level table
[thirdparty/xfsprogs-dev.git] / io / reflink.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2015 Oracle, Inc.
4 * All Rights Reserved.
5 */
6
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 dedupe_cmd;
15 static cmdinfo_t reflink_cmd;
16
17 static void
18 dedupe_help(void)
19 {
20 printf(_("\n\
21 Links a range of bytes (in block size increments) from a file into a range\n\
22 of bytes in the open file. The contents of both file ranges must match.\n\
23 \n\
24 Example:\n\
25 'dedupe some_file 0 4096 32768' - links 32768 bytes from some_file at\n\
26 offset 0 to into the open file at\n\
27 position 4096\n\
28 \n\
29 Reflink a range of blocks from a given input file to the open file. Both\n\
30 files share the same range of physical disk blocks; a write to the shared\n\
31 range of either file should result in the write landing in a new block and\n\
32 that range of the file being remapped (i.e. copy-on-write). Both files\n\
33 must reside on the same filesystem, and the contents of both ranges must\n\
34 match.\n\
35 "));
36 }
37
38 static uint64_t
39 dedupe_ioctl(
40 int fd,
41 uint64_t soffset,
42 uint64_t doffset,
43 uint64_t len,
44 int *ops)
45 {
46 struct xfs_extent_data *args;
47 struct xfs_extent_data_info *info;
48 int error;
49 uint64_t deduped = 0;
50
51 args = calloc(1, sizeof(struct xfs_extent_data) +
52 sizeof(struct xfs_extent_data_info));
53 if (!args)
54 goto done;
55 info = (struct xfs_extent_data_info *)(args + 1);
56 args->logical_offset = soffset;
57 args->length = len;
58 args->dest_count = 1;
59 info->fd = file->fd;
60 info->logical_offset = doffset;
61
62 while (args->length > 0 || !*ops) {
63 error = ioctl(fd, XFS_IOC_FILE_EXTENT_SAME, args);
64 if (error) {
65 perror("XFS_IOC_FILE_EXTENT_SAME");
66 goto done;
67 }
68 if (info->status < 0) {
69 fprintf(stderr, "XFS_IOC_FILE_EXTENT_SAME: %s\n",
70 _(strerror(-info->status)));
71 goto done;
72 }
73 if (info->status == XFS_EXTENT_DATA_DIFFERS) {
74 fprintf(stderr, "XFS_IOC_FILE_EXTENT_SAME: %s\n",
75 _("Extents did not match."));
76 goto done;
77 }
78 if (args->length != 0 &&
79 (info->bytes_deduped == 0 ||
80 info->bytes_deduped > args->length))
81 break;
82
83 (*ops)++;
84 args->logical_offset += info->bytes_deduped;
85 info->logical_offset += info->bytes_deduped;
86 if (args->length >= info->bytes_deduped)
87 args->length -= info->bytes_deduped;
88 deduped += info->bytes_deduped;
89 }
90 done:
91 free(args);
92 return deduped;
93 }
94
95 static int
96 dedupe_f(
97 int argc,
98 char **argv)
99 {
100 off64_t soffset, doffset;
101 long long count, total;
102 char *infile;
103 int condensed, quiet_flag;
104 size_t fsblocksize, fssectsize;
105 struct timeval t1, t2;
106 int c, ops = 0, fd = -1;
107
108 condensed = quiet_flag = 0;
109 init_cvtnum(&fsblocksize, &fssectsize);
110
111 while ((c = getopt(argc, argv, "Cq")) != EOF) {
112 switch (c) {
113 case 'C':
114 condensed = 1;
115 break;
116 case 'q':
117 quiet_flag = 1;
118 break;
119 default:
120 return command_usage(&dedupe_cmd);
121 }
122 }
123 if (optind != argc - 4)
124 return command_usage(&dedupe_cmd);
125 infile = argv[optind];
126 optind++;
127 soffset = cvtnum(fsblocksize, fssectsize, argv[optind]);
128 if (soffset < 0) {
129 printf(_("non-numeric src offset argument -- %s\n"), argv[optind]);
130 return 0;
131 }
132 optind++;
133 doffset = cvtnum(fsblocksize, fssectsize, argv[optind]);
134 if (doffset < 0) {
135 printf(_("non-numeric dest offset argument -- %s\n"), argv[optind]);
136 return 0;
137 }
138 optind++;
139 count = cvtnum(fsblocksize, fssectsize, argv[optind]);
140 if (count < 0) {
141 printf(_("non-positive length argument -- %s\n"), argv[optind]);
142 return 0;
143 }
144
145 fd = openfile(infile, NULL, IO_READONLY, 0, NULL);
146 if (fd < 0)
147 return 0;
148
149 gettimeofday(&t1, NULL);
150 total = dedupe_ioctl(fd, soffset, doffset, count, &ops);
151 if (ops == 0 || quiet_flag)
152 goto done;
153 gettimeofday(&t2, NULL);
154 t2 = tsub(t2, t1);
155
156 report_io_times("deduped", &t2, (long long)doffset, count, total, ops,
157 condensed);
158 done:
159 close(fd);
160 return 0;
161 }
162
163 static void
164 reflink_help(void)
165 {
166 printf(_("\n\
167 Links a range of bytes (in block size increments) from a file into a range\n\
168 of bytes in the open file. The two extent ranges need not contain identical\n\
169 data.\n\
170 \n\
171 Example:\n\
172 'reflink some_file 0 4096 32768' - links 32768 bytes from some_file at\n\
173 offset 0 to into the open file at\n\
174 position 4096\n\
175 'reflink some_file' - links all bytes from some_file into the open file\n\
176 at position 0\n\
177 \n\
178 Reflink a range of blocks from a given input file to the open file. Both\n\
179 files share the same range of physical disk blocks; a write to the shared\n\
180 range of either file should result in the write landing in a new block and\n\
181 that range of the file being remapped (i.e. copy-on-write). Both files\n\
182 must reside on the same filesystem.\n\
183 "));
184 }
185
186 static uint64_t
187 reflink_ioctl(
188 int fd,
189 uint64_t soffset,
190 uint64_t doffset,
191 uint64_t len,
192 int *ops)
193 {
194 struct xfs_clone_args args;
195 int error;
196
197 if (soffset == 0 && doffset == 0 && len == 0) {
198 error = ioctl(file->fd, XFS_IOC_CLONE, fd);
199 if (error)
200 perror("XFS_IOC_CLONE");
201 } else {
202 args.src_fd = fd;
203 args.src_offset = soffset;
204 args.src_length = len;
205 args.dest_offset = doffset;
206 error = ioctl(file->fd, XFS_IOC_CLONE_RANGE, &args);
207 if (error)
208 perror("XFS_IOC_CLONE_RANGE");
209 }
210 if (!error)
211 (*ops)++;
212 return error ? 0 : len;
213 }
214
215 static int
216 reflink_f(
217 int argc,
218 char **argv)
219 {
220 off64_t soffset, doffset;
221 long long count = 0, total;
222 char *infile = NULL;
223 int condensed, quiet_flag;
224 size_t fsblocksize, fssectsize;
225 struct timeval t1, t2;
226 int c, ops = 0, fd = -1;
227
228 condensed = quiet_flag = 0;
229 doffset = soffset = 0;
230 init_cvtnum(&fsblocksize, &fssectsize);
231
232 while ((c = getopt(argc, argv, "Cq")) != EOF) {
233 switch (c) {
234 case 'C':
235 condensed = 1;
236 break;
237 case 'q':
238 quiet_flag = 1;
239 break;
240 default:
241 return command_usage(&reflink_cmd);
242 }
243 }
244 if (optind != argc - 4 && optind != argc - 1)
245 return command_usage(&reflink_cmd);
246 infile = argv[optind];
247 optind++;
248 if (optind == argc)
249 goto clone_all;
250 soffset = cvtnum(fsblocksize, fssectsize, argv[optind]);
251 if (soffset < 0) {
252 printf(_("non-numeric src offset argument -- %s\n"), argv[optind]);
253 return 0;
254 }
255 optind++;
256 doffset = cvtnum(fsblocksize, fssectsize, argv[optind]);
257 if (doffset < 0) {
258 printf(_("non-numeric dest offset argument -- %s\n"), argv[optind]);
259 return 0;
260 }
261 optind++;
262 count = cvtnum(fsblocksize, fssectsize, argv[optind]);
263 if (count < 0) {
264 printf(_("non-positive length argument -- %s\n"), argv[optind]);
265 return 0;
266 }
267
268 clone_all:
269 fd = openfile(infile, NULL, IO_READONLY, 0, NULL);
270 if (fd < 0)
271 return 0;
272
273 gettimeofday(&t1, NULL);
274 total = reflink_ioctl(fd, soffset, doffset, count, &ops);
275 if (ops == 0 || quiet_flag)
276 goto done;
277 gettimeofday(&t2, NULL);
278 t2 = tsub(t2, t1);
279
280 report_io_times("linked", &t2, (long long)doffset, count, total, ops,
281 condensed);
282 done:
283 close(fd);
284 return 0;
285 }
286
287 void
288 reflink_init(void)
289 {
290 reflink_cmd.name = "reflink";
291 reflink_cmd.altname = "rl";
292 reflink_cmd.cfunc = reflink_f;
293 reflink_cmd.argmin = 1;
294 reflink_cmd.argmax = -1;
295 reflink_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK | CMD_FLAG_ONESHOT;
296 reflink_cmd.args =
297 _("infile [src_off dst_off len]");
298 reflink_cmd.oneline =
299 _("reflinks an entire file, or a number of bytes at a specified offset");
300 reflink_cmd.help = reflink_help;
301
302 add_command(&reflink_cmd);
303
304 dedupe_cmd.name = "dedupe";
305 dedupe_cmd.altname = "dd";
306 dedupe_cmd.cfunc = dedupe_f;
307 dedupe_cmd.argmin = 4;
308 dedupe_cmd.argmax = -1;
309 dedupe_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK | CMD_FLAG_ONESHOT;
310 dedupe_cmd.args =
311 _("infile src_off dst_off len");
312 dedupe_cmd.oneline =
313 _("dedupes a number of bytes at a specified offset");
314 dedupe_cmd.help = dedupe_help;
315
316 add_command(&dedupe_cmd);
317 }