]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - io/copy_file_range.c
xfs_io: actually check copy file range helper return values
[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\
29"));
30}
31
8041435d
DW
32/*
33 * Issue a raw copy_file_range syscall; for our test program we don't want the
34 * glibc buffered copy fallback.
35 */
628e112a 36static loff_t
2a42470b 37copy_file_range_cmd(int fd, long long *src, long long *dst, size_t len)
628e112a
AS
38{
39 loff_t ret;
40
41 do {
8041435d
DW
42 ret = syscall(__NR_copy_file_range, fd, src, file->fd, dst,
43 len, 0);
43141802
AS
44 if (ret == -1) {
45 perror("copy_range");
628e112a 46 return errno;
43141802
AS
47 } else if (ret == 0)
48 break;
628e112a
AS
49 len -= ret;
50 } while (len > 0);
51
52 return 0;
53}
54
55static off64_t
56copy_src_filesize(int fd)
57{
f594a0d1 58 struct stat st;
628e112a 59
f594a0d1
FJ
60 if (fstat(fd, &st) < 0) {
61 perror("fstat");
628e112a
AS
62 return -1;
63 };
64 return st.st_size;
65}
66
67static int
68copy_dst_truncate(void)
69{
dde67673 70 int ret = ftruncate(file->fd, 0);
628e112a 71 if (ret < 0)
dde67673 72 perror("ftruncate");
628e112a
AS
73 return ret;
74}
75
76static int
77copy_range_f(int argc, char **argv)
78{
25b4549c
GR
79 long long src = 0;
80 long long dst = 0;
2a42470b 81 size_t len = 0;
628e112a
AS
82 int opt;
83 int ret;
84 int fd;
25b4549c
GR
85 size_t fsblocksize, fssectsize;
86
87 init_cvtnum(&fsblocksize, &fssectsize);
628e112a
AS
88
89 while ((opt = getopt(argc, argv, "s:d:l:")) != -1) {
90 switch (opt) {
91 case 's':
25b4549c
GR
92 src = cvtnum(fsblocksize, fssectsize, optarg);
93 if (src < 0) {
94 printf(_("invalid source offset -- %s\n"), optarg);
628e112a
AS
95 return 0;
96 }
97 break;
98 case 'd':
25b4549c
GR
99 dst = cvtnum(fsblocksize, fssectsize, optarg);
100 if (dst < 0) {
101 printf(_("invalid destination offset -- %s\n"), optarg);
628e112a
AS
102 return 0;
103 }
104 break;
105 case 'l':
25b4549c 106 len = cvtnum(fsblocksize, fssectsize, optarg);
2a42470b 107 if (len == -1LL) {
25b4549c 108 printf(_("invalid length -- %s\n"), optarg);
628e112a
AS
109 return 0;
110 }
111 break;
112 }
113 }
114
115 if (optind != argc - 1)
116 return command_usage(&copy_range_cmd);
117
3fcab549 118 fd = openfile(argv[optind], NULL, IO_READONLY, 0, NULL);
628e112a
AS
119 if (fd < 0)
120 return 0;
121
122 if (src == 0 && dst == 0 && len == 0) {
30cc0fda
DW
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 }
628e112a
AS
137 }
138
8041435d 139 ret = copy_file_range_cmd(fd, &src, &dst, len);
30cc0fda 140out:
628e112a
AS
141 close(fd);
142 return ret;
143}
144
145void
146copy_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}