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