]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/copy_file_range.c
xfs_io: actually check copy file range helper return values
[thirdparty/xfsprogs-dev.git] / io / copy_file_range.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (c) 2016 Netapp, Inc. All rights reserved.
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\
29 "));
30 }
31
32 /*
33 * Issue a raw copy_file_range syscall; for our test program we don't want the
34 * glibc buffered copy fallback.
35 */
36 static loff_t
37 copy_file_range_cmd(int fd, long long *src, long long *dst, size_t len)
38 {
39 loff_t ret;
40
41 do {
42 ret = syscall(__NR_copy_file_range, fd, src, file->fd, dst,
43 len, 0);
44 if (ret == -1) {
45 perror("copy_range");
46 return errno;
47 } else if (ret == 0)
48 break;
49 len -= ret;
50 } while (len > 0);
51
52 return 0;
53 }
54
55 static off64_t
56 copy_src_filesize(int fd)
57 {
58 struct stat st;
59
60 if (fstat(fd, &st) < 0) {
61 perror("fstat");
62 return -1;
63 };
64 return st.st_size;
65 }
66
67 static int
68 copy_dst_truncate(void)
69 {
70 int ret = ftruncate(file->fd, 0);
71 if (ret < 0)
72 perror("ftruncate");
73 return ret;
74 }
75
76 static int
77 copy_range_f(int argc, char **argv)
78 {
79 long long src = 0;
80 long long dst = 0;
81 size_t len = 0;
82 int opt;
83 int ret;
84 int fd;
85 size_t fsblocksize, fssectsize;
86
87 init_cvtnum(&fsblocksize, &fssectsize);
88
89 while ((opt = getopt(argc, argv, "s:d:l:")) != -1) {
90 switch (opt) {
91 case 's':
92 src = cvtnum(fsblocksize, fssectsize, optarg);
93 if (src < 0) {
94 printf(_("invalid source offset -- %s\n"), optarg);
95 return 0;
96 }
97 break;
98 case 'd':
99 dst = cvtnum(fsblocksize, fssectsize, optarg);
100 if (dst < 0) {
101 printf(_("invalid destination offset -- %s\n"), optarg);
102 return 0;
103 }
104 break;
105 case 'l':
106 len = cvtnum(fsblocksize, fssectsize, optarg);
107 if (len == -1LL) {
108 printf(_("invalid length -- %s\n"), optarg);
109 return 0;
110 }
111 break;
112 }
113 }
114
115 if (optind != argc - 1)
116 return command_usage(&copy_range_cmd);
117
118 fd = openfile(argv[optind], NULL, IO_READONLY, 0, NULL);
119 if (fd < 0)
120 return 0;
121
122 if (src == 0 && dst == 0 && len == 0) {
123 off64_t sz;
124
125 sz = copy_src_filesize(fd);
126 if (sz < 0 || (unsigned long long)sz > SIZE_MAX) {
127 ret = 1;
128 goto out;
129 }
130 len = sz;
131
132 ret = copy_dst_truncate();
133 if (ret < 0) {
134 ret = 1;
135 goto out;
136 }
137 }
138
139 ret = copy_file_range_cmd(fd, &src, &dst, len);
140 out:
141 close(fd);
142 return ret;
143 }
144
145 void
146 copy_range_init(void)
147 {
148 copy_range_cmd.name = "copy_range";
149 copy_range_cmd.cfunc = copy_range_f;
150 copy_range_cmd.argmin = 1;
151 copy_range_cmd.argmax = 7;
152 copy_range_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
153 copy_range_cmd.args = _("[-s src_off] [-d dst_off] [-l len] src_file");
154 copy_range_cmd.oneline = _("Copy a range of data between two files");
155 copy_range_cmd.help = copy_range_help;
156
157 add_command(&copy_range_cmd);
158 }