2 * Copyright (C) 2011 Oracle. All rights reserved.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License v2 as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 021110-1307, USA.
18 #define _XOPEN_SOURCE 500
25 #include <sys/types.h>
39 #define MIN(a,b) ((a)<(b)?(a):(b))
43 error(const char *fmt, ...)
48 vsprintf(buf, fmt, args);
51 fprintf(stderr, "ERROR: [%s:%d] %s:%s\n", __func__, __LINE__,
52 buf, strerror(errno));
56 full_write(int fd, const void *buf, size_t count)
59 const char *ptr = (const char *) buf;
62 ssize_t n = write(fd, ptr, count);
66 error("failed as %s", strerror(errno));
71 error("%zu bytes transferred. Aborting.",
85 * Copy a data extent from source file to dest file.
86 * @data_off: data offset
87 * @hole_off: hole offset
88 * The length of this extent is (hole_off - data_off).
91 do_extent_copy(int src_fd, int dest_fd, off_t data_off, off_t hole_off)
93 uint64_t len = (uint64_t)(hole_off - data_off);
97 /* Seek to data_off for data reading */
98 ret = lseek(src_fd, data_off, SEEK_SET);
100 error("seek source file to %llu failed as %s",
101 (uint64_t)data_off, strerror(errno));
105 /* Seek to data_off for data writing, make holes as well */
106 ret = lseek(dest_fd, data_off, SEEK_SET);
108 error("seek dest file to %llu failed as %s",
109 (uint64_t)data_off, strerror(errno));
114 ssize_t nr_read = read(src_fd, buf, BUF_SIZE);
118 error("read source file extent failed as %s",
125 error("reached EOF");
129 if (full_write(dest_fd, buf, nr_read) != nr_read) {
130 error("write data to dest file failed as %s",
143 * If lseek(2) failed and the errno is set to ENXIO, for
144 * SEEK_DATA there are no more data regions past the supplied
145 * offset. For SEEK_HOLE, there are no more holes past the
146 * supplied offset. Set scan->hit_final_extent to true for
150 copy_extents(int src_fd, int dest_fd, off_t src_total_size)
153 off_t seek_start = 0;
155 off_t data_pos, hole_pos;
158 data_pos = lseek(src_fd, seek_start, SEEK_DATA);
163 error("SEEK_DATA failed due to %s",
170 hole_pos = lseek(src_fd, data_pos, SEEK_HOLE);
175 error("SEEK_HOLE failed due to %s\n",
183 ret = do_extent_copy(src_fd, dest_fd, data_pos, hole_pos);
185 error("copy extent failed");
189 dest_pos += (hole_pos - data_pos);
190 seek_start = hole_pos;
191 } while (seek_start < src_total_size);
193 if (dest_pos < src_total_size) {
194 ret = ftruncate(dest_fd, src_total_size);
196 error("truncate dest file to %lld bytes failed as %s",
197 (long long)src_total_size, strerror(errno));
205 main(int argc, char **argv)
211 size_t src_total_size;
214 fprintf(stdout, "Usage: %s source dest\n", argv[0]);
218 src_fd = open(argv[1], O_RDONLY, 0644);
220 error("create %s failed", argv[1]);
224 dest_fd = open(argv[2], O_RDWR|O_CREAT|O_EXCL, 0644);
226 error("create %s failed", argv[2]);
231 ret = fstat(src_fd, &st);
233 error("get file %s staticis failed", argv[1]);
238 src_total_size = st.st_size;
239 ret = copy_extents(src_fd, dest_fd, src_total_size);
241 error("extents copy failed");