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