tests: remove udf/101
[xfstests-dev.git] / src / seek_copy_test.c
1 /*
2  * Copyright (C) 2011 Oracle.  All rights reserved.
3  *
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.
7  *
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.
12  *
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.
17  */
18 #define _XOPEN_SOURCE 500
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdint.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <limits.h>
31
32 #ifndef SEEK_DATA
33 #define SEEK_DATA       3
34 #define SEEK_HOLE       4
35 #endif
36
37 #define BUF_SIZE        4096
38 #ifndef MIN
39 #define MIN(a,b) ((a)<(b)?(a):(b))
40 #endif
41
42 static void
43 error(const char *fmt, ...)
44 {
45         char buf[256];
46         va_list args;
47         va_start(args, fmt);
48         vsprintf(buf, fmt, args);
49         va_end(args);
50
51         fprintf(stderr, "ERROR: [%s:%d] %s:%s\n", __func__, __LINE__,
52                 buf, strerror(errno));
53 }
54
55 static size_t
56 full_write(int fd, const void *buf, size_t count)
57 {
58         size_t total = 0;
59         const char *ptr = (const char *) buf;
60
61         while (count > 0) {
62                 ssize_t n = write(fd, ptr, count);
63                 if (n < 0) {
64                         if (errno == EINTR)
65                                 continue;
66                         error("failed as %s", strerror(errno));
67                         break;
68                 }
69
70                 if (n == 0) {
71                         error("%zu bytes transferred. Aborting.",
72                                total);
73                         break;
74                 }
75
76                 total += n;
77                 ptr += n;
78                 count -= n;
79         }
80
81         return total;
82 }
83
84 /*
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).
89  */
90 static int
91 do_extent_copy(int src_fd, int dest_fd, off_t data_off, off_t hole_off)
92 {
93         uint64_t len = (uint64_t)(hole_off - data_off);
94         char buf[BUF_SIZE];
95         int ret;
96
97         /* Seek to data_off for data reading */
98         ret = lseek(src_fd, data_off, SEEK_SET);
99         if (ret < 0) {
100                 error("seek source file to %llu failed as %s",
101                        (uint64_t)data_off, strerror(errno));
102                 return ret;
103         }
104
105         /* Seek to data_off for data writing, make holes as well */
106         ret = lseek(dest_fd, data_off, SEEK_SET);
107         if (ret < 0) {
108                 error("seek dest file to %llu failed as %s",
109                        (uint64_t)data_off, strerror(errno));
110                 return ret;
111         }
112
113         while (len > 0) {
114                 ssize_t nr_read = read(src_fd, buf, BUF_SIZE);
115                 if (nr_read < 0) {
116                         if (errno == EINTR)
117                                 continue;
118                         error("read source file extent failed as %s",
119                               strerror(errno));
120                         ret = -1;
121                         break;
122                 }
123
124                 if (nr_read == 0) {
125                         error("reached EOF");
126                         break;
127                 }
128
129                 if (full_write(dest_fd, buf, nr_read) != nr_read) {
130                         error("write data to dest file failed as %s",
131                                strerror(errno));
132                         ret = -1;
133                         break;
134                 }
135
136                 len -= nr_read;
137         }
138
139         return ret;
140 }
141
142 /*
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
147  * either case.
148  */
149 static int
150 copy_extents(int src_fd, int dest_fd, off_t src_total_size)
151 {
152         int ret = 0;
153         off_t seek_start = 0;
154         off_t dest_pos = 0;
155         off_t data_pos, hole_pos;
156
157         do {
158                 data_pos = lseek(src_fd, seek_start, SEEK_DATA);
159                 if (data_pos < 0) {
160                         if (errno == ENXIO)
161                                 ret = 0;
162                         else {
163                                 error("SEEK_DATA failed due to %s",
164                                        strerror(errno));
165                                 ret = -1;
166                         }
167                         break;
168                 }
169
170                 hole_pos = lseek(src_fd, data_pos, SEEK_HOLE);
171                 if (hole_pos < 0) {
172                         if (errno == ENXIO)
173                                 ret = 0;
174                         else {
175                                 error("SEEK_HOLE failed due to %s\n",
176                                        strerror(errno));
177                                 ret = -1;
178                         }
179                         break;
180                 }
181
182                 /* do extent copy */
183                 ret = do_extent_copy(src_fd, dest_fd, data_pos, hole_pos);
184                 if (ret < 0) {
185                         error("copy extent failed");
186                         break;
187                 }
188
189                 dest_pos += (hole_pos - data_pos);
190                 seek_start = hole_pos;
191         } while (seek_start < src_total_size);
192
193         if (dest_pos < src_total_size) {
194                 ret = ftruncate(dest_fd, src_total_size);
195                 if (ret < 0) {
196                         error("truncate dest file to %lld bytes failed as %s",
197                               (long long)src_total_size, strerror(errno));
198                 }
199         }
200
201         return ret;
202 }
203
204 int
205 main(int argc, char **argv)
206 {
207         int ret = 0;
208         int src_fd;
209         int dest_fd;
210         struct stat st;
211         size_t src_total_size;
212
213         if (argc != 3) {
214                 fprintf(stdout, "Usage: %s source dest\n", argv[0]);
215                 return 1;
216         }
217
218         src_fd = open(argv[1], O_RDONLY, 0644);
219         if (src_fd < 0) {
220                 error("create %s failed", argv[1]);
221                 return -1;
222         }
223
224         dest_fd = open(argv[2], O_RDWR|O_CREAT|O_EXCL, 0644);
225         if (dest_fd < 0) {
226                 error("create %s failed", argv[2]);
227                 ret = -errno;
228                 goto close_src_fd;
229         }
230
231         ret = fstat(src_fd, &st);
232         if (ret < 0) {
233                 error("get file %s staticis failed", argv[1]);
234                 ret = -errno;
235                 goto close_dest_fd;
236         }
237
238         src_total_size = st.st_size;
239         ret = copy_extents(src_fd, dest_fd, src_total_size);
240         if (ret < 0)
241                 error("extents copy failed");
242
243 close_dest_fd:
244         close(dest_fd);
245 close_src_fd:
246         close(src_fd);
247
248         return ret;
249 }