]> git.saurik.com Git - apple/copyfile.git/blob - copyfile_test/sparse_test.c
fc126d76710fbb9065c80c285e6e19fbed0a222e
[apple/copyfile.git] / copyfile_test / sparse_test.c
1 //
2 // sparse_test.c
3 // copyfile_test
4 //
5
6 #include <stdbool.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <removefile.h>
12 #include <sys/fcntl.h>
13 #include <sys/stat.h>
14
15 #include "../copyfile.h"
16 #include "sparse_test.h"
17 #include "test_utils.h"
18 #include "systemx.h"
19
20 /*
21 * Copy the file pointed to by src_fd (and orig_name) to copy_name,
22 * using copyfile()/fcopyfile() and COPYFILE_DATA. If do_sparse, also pass COPYFILE_DATA_SPARSE.
23 * Before copying, rewind src_fd to start_off.
24 */
25 static bool test_copy(int src_fd, char* orig_name, char* copy_name, bool do_sparse, off_t start_off) {
26 struct stat orig_sb, copy_sb;
27 int copy_fd;
28 bool result = true;
29 copyfile_state_t cpf_state;
30
31 // Get ready for the test.
32 memset(&orig_sb, 0, sizeof(orig_sb));
33 memset(&copy_sb, 0, sizeof(copy_sb));
34 assert_with_errno((cpf_state = copyfile_state_alloc()) != NULL);
35 assert_with_errno(lseek(src_fd, start_off, SEEK_SET) == start_off);
36
37 // First, verify copyfile().
38 copyfile_flags_t flags = COPYFILE_ALL;
39 if (do_sparse) {
40 flags |= COPYFILE_DATA_SPARSE;
41 }
42 assert_no_err(copyfile(orig_name, copy_name, cpf_state, flags));
43
44 // The file was (hopefully) copied. Now, we must verify three things:
45 // 1. If (do_sparse), verify that the copy is a sparse file.
46 // For now, let's approximate this by testing that the sizes of the two files are equal.
47 // 2. The copyfile_state_t for the copy returns that all bytes were copied.
48 // 3. The copy and the source have identical contents.
49
50 // 1. The copy is a sparse file.
51 // 2. The copyfile_state_t for the copy returns that all bytes were copied.
52 assert_no_err(stat(orig_name, &orig_sb));
53 assert_no_err(stat(copy_name, &copy_sb));
54 result &= verify_copy_sizes(&orig_sb, &copy_sb, cpf_state, do_sparse, 0);
55
56 // 3. The copy and the source have identical contents.
57 result &= verify_copy_contents(orig_name, copy_name);
58
59 // Post-test cleanup.
60 assert_no_err(copyfile_state_free(cpf_state));
61 assert_no_err(removefile(copy_name, NULL, REMOVEFILE_RECURSIVE));
62 memset(&orig_sb, 0, sizeof(struct stat));
63 memset(&copy_sb, 0, sizeof(struct stat));
64
65 // Next, verify fcopyfile().
66 // Make an fd for the destination.
67 assert_with_errno((copy_fd = open(copy_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM)) > 0);
68
69 // Call fcopyfile().
70 assert_with_errno((cpf_state = copyfile_state_alloc()) != NULL);
71 assert_no_err(fcopyfile(src_fd, copy_fd, cpf_state, flags));
72
73 // 1. The copy is a sparse file (if start_off is 0).
74 // 2. The copyfile_state_t for the copy returns that all bytes were copied.
75 assert_no_err(fstat(src_fd, &orig_sb));
76 assert_no_err(fstat(copy_fd, &copy_sb));
77 result &= verify_copy_sizes(&orig_sb, &copy_sb, cpf_state,
78 start_off > 0 ? false : do_sparse, start_off);
79
80 // 3. The copy and the source have identical contents.
81 if (start_off == 0) {
82 result &= verify_copy_contents(orig_name, copy_name);
83 }
84
85 // Post-test cleanup.
86 assert_no_err(copyfile_state_free(cpf_state));
87 assert_no_err(removefile(copy_name, NULL, REMOVEFILE_RECURSIVE));
88 assert_no_err(close(copy_fd));
89
90 return result;
91 }
92
93
94 // Sparse file creation functions.
95 // Each take the source file descriptor pointing at the beginning of the file and the block size.
96 // Each return the offset we should return the fd to before any copying should be performed.
97 typedef off_t (*creator_func)(int, off_t);
98
99 static off_t write_start_and_end_holes(int fd, off_t block_size) {
100 assert_with_errno(pwrite(fd, "j", 1, block_size) == 1);
101 assert_no_err(ftruncate(fd, 3 * block_size));
102
103 assert_no_err(create_hole_in_fd(fd, 0, block_size));
104 assert_no_err(create_hole_in_fd(fd, 2 * block_size, block_size));
105 return 0;
106 }
107
108 static off_t write_end_hole(int fd, off_t block_size) {
109 assert_with_errno(pwrite(fd, "n", 1, 0) == 1);
110 assert_no_err(ftruncate(fd, 16 * block_size));
111
112 assert_no_err(create_hole_in_fd(fd, block_size, 15 * block_size));
113 return 0;
114 }
115
116 static off_t write_start_hole(int fd, off_t block_size) {
117 assert_with_errno(pwrite(fd, "p", 1, 16 * block_size) == 1);
118
119 assert_no_err(create_hole_in_fd(fd, 0, 16 * block_size));
120 return 0;
121 }
122
123 static off_t write_middle_hole(int fd, off_t block_size) {
124 assert_with_errno(pwrite(fd, "k", 1, 0) == 1);
125 assert_with_errno(pwrite(fd, "k", 1, 4 * block_size) == 1);
126 assert_no_err(ftruncate(fd, 5 * block_size));
127
128 assert_no_err(create_hole_in_fd(fd, block_size, 3 * block_size));
129 return 0;
130 }
131
132 static off_t write_start_and_middle_holes(int fd, off_t block_size) {
133 assert_with_errno(pwrite(fd, "l", 1, 16 * block_size) == 1);
134 assert_with_errno(pwrite(fd, "l", 1, 32 * block_size) == 1);
135
136 assert_no_err(create_hole_in_fd(fd, 0, 16 * block_size));
137 assert_no_err(create_hole_in_fd(fd, 17 * block_size, 15 * block_size));
138 return 0;
139 }
140
141 static off_t write_middle_and_end_holes(int fd, off_t block_size) {
142 assert_with_errno(pwrite(fd, "m", 1, 0) == 1);
143 assert_with_errno(pwrite(fd, "m", 1, 16 * block_size) == 1);
144 assert_no_err(ftruncate(fd, 32 * block_size));
145
146 assert_no_err(create_hole_in_fd(fd, block_size, 15 * block_size));
147 assert_no_err(create_hole_in_fd(fd, 17 * block_size, 15 * block_size));
148 return 0;
149 }
150
151 static off_t write_no_sparse(int fd, __unused off_t block_size) {
152 assert_with_errno(pwrite(fd, "z", 1, 0) == 1);
153 return 0;
154 }
155
156 static off_t write_sparse_odd_offset(int fd, off_t block_size) {
157 assert_with_errno(pwrite(fd, "q", 1, block_size) == 1);
158
159 assert_no_err(create_hole_in_fd(fd, 0, block_size));
160 // Return with the fd pointing at offset 1.
161 assert_with_errno(lseek(fd, 1, SEEK_SET) == 1);
162 return 1;
163 }
164
165 static off_t write_sparse_bs_offset(int fd, off_t block_size) {
166 assert_with_errno(pwrite(fd, "a", 1, block_size) == 1);
167 assert_with_errno(pwrite(fd, "b", 1, 2 * block_size) == 1);
168
169 assert_no_err(create_hole_in_fd(fd, 0, block_size));
170 // Return with the fd pointing at block_size.
171 assert_with_errno(lseek(fd, block_size, SEEK_SET) == block_size);
172 return block_size;
173 }
174
175 static off_t write_diff_adj_holes(int fd, off_t block_size) {
176 assert_with_errno(pwrite(fd, "w", 1, 0));
177 assert_with_errno(pwrite(fd, "w", 1, 3 * block_size));
178 assert_no_err(ftruncate(fd, 4 * block_size));
179
180 assert_no_err(create_hole_in_fd(fd, block_size, block_size));
181 assert_no_err(create_hole_in_fd(fd, 2 * block_size, block_size));
182 return 0;
183 }
184
185 typedef struct {
186 creator_func func; // pointer to function to create a sparse file
187 const char * name; // null terminated string
188 } sparse_test_func;
189
190 #define NUM_TEST_FUNCTIONS 10
191 sparse_test_func test_functions[NUM_TEST_FUNCTIONS] = {
192 {write_start_and_end_holes, "start_and_end_holes"},
193 {write_middle_hole, "middle_hole"},
194 {write_start_and_middle_holes, "start_and_middle_holes"},
195 {write_middle_and_end_holes, "middle_and_end_holes"},
196 {write_end_hole, "end_hole"},
197 {write_start_hole, "start_hole"},
198 {write_no_sparse, "no_sparse"},
199 {write_sparse_odd_offset, "write_sparse_odd_offset"},
200 {write_sparse_bs_offset, "write_sparse_bs_offset"},
201 {write_diff_adj_holes, "write_diff_adj_holes"}
202 };
203
204 bool do_sparse_test(const char* apfs_test_directory, size_t block_size) {
205 int fd, test_file_id;
206 char out_name[BSIZE_B], sparse_copy_name[BSIZE_B], full_copy_name[BSIZE_B];
207 bool success = true, sub_test_success;
208 off_t start_off;
209
210 for (size_t sub_test = 0; sub_test < NUM_TEST_FUNCTIONS; sub_test++) {
211 printf("START [%s]\n", test_functions[sub_test].name);
212 sub_test_success = false;
213
214 // Make new names for this file and its copies.
215 test_file_id = rand() % DEFAULT_NAME_MOD;
216 create_test_file_name(apfs_test_directory, "sparse", test_file_id, out_name);
217 create_test_file_name(apfs_test_directory, "copy_sparse", test_file_id, sparse_copy_name);
218 create_test_file_name(apfs_test_directory, "copy_full", test_file_id, full_copy_name);
219
220 // Create the test file.
221 fd = open(out_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
222 assert_with_errno(fd >= 0);
223
224 // Write to the test file, making it sparse.
225 start_off = test_functions[sub_test].func(fd, (off_t) block_size);
226 assert_no_err(fsync(fd));
227
228 // Make sure that a sparse copy is successful.
229 sub_test_success = test_copy(fd, out_name, sparse_copy_name, true, start_off);
230 if (sub_test_success) {
231 // Make sure that a full copy is successful.
232 sub_test_success = test_copy(fd, out_name, full_copy_name, false, start_off);
233 }
234
235 // Report the result on stdout.
236 if (!sub_test_success) {
237 printf("FAIL [%s]\n", test_functions[sub_test].name);
238 success = false;
239 } else {
240 printf("PASS [%s]\n", test_functions[sub_test].name);
241 }
242
243 // Cleanup for the next test.
244 assert_no_err(close(fd));
245 (void)removefile(out_name, NULL, 0);
246 (void)removefile(sparse_copy_name, NULL, 0);
247 (void)removefile(full_copy_name, NULL, 0);
248 memset(out_name, 0, BSIZE_B);
249 memset(sparse_copy_name, 0, BSIZE_B);
250 memset(full_copy_name, 0, BSIZE_B);
251 }
252
253 return success ? EXIT_SUCCESS : EXIT_FAILURE;
254 }
255
256 bool do_sparse_recursive_test(const char *apfs_test_directory, size_t block_size) {
257 int exterior_file_src_fd, interior_file_src_fd, test_file_id;
258 char exterior_dir_src[BSIZE_B] = {0}, interior_dir_src[BSIZE_B] = {0}, exterior_dir_dst[BSIZE_B] = {0}, interior_dir_dst[BSIZE_B] = {0};
259 char exterior_file_src[BSIZE_B] = {0}, interior_file_src[BSIZE_B] = {0}, exterior_file_dst[BSIZE_B] = {0}, interior_file_dst[BSIZE_B] = {0};
260 struct stat exterior_file_src_sb, interior_file_src_sb, exterior_file_dst_sb, interior_file_dst_sb;
261 bool success = true;
262
263 printf("START [sparse_recursive]\n");
264
265 // Get ready for the test.
266 memset(&exterior_file_src_sb, 0, sizeof(exterior_file_src_sb));
267 memset(&interior_file_src_sb, 0, sizeof(interior_file_src_sb));
268 memset(&exterior_file_dst_sb, 0, sizeof(exterior_file_dst_sb));
269 memset(&interior_file_dst_sb, 0, sizeof(interior_file_dst_sb));
270
271 // Construct our source layout.
272 assert_with_errno(snprintf(exterior_dir_src, BSIZE_B, "%s/recursive_src", apfs_test_directory) > 0);
273 assert_with_errno(snprintf(interior_dir_src, BSIZE_B, "%s/interior", exterior_dir_src) > 0);
274
275 assert_no_err(mkdir(exterior_dir_src, DEFAULT_MKDIR_PERM));
276 assert_no_err(mkdir(interior_dir_src, DEFAULT_MKDIR_PERM));
277
278 test_file_id = rand() % DEFAULT_NAME_MOD;
279 create_test_file_name(exterior_dir_src, "exterior_sparse_file", test_file_id, exterior_file_src);
280 create_test_file_name(interior_dir_src, "interior_sparse_file", test_file_id, interior_file_src);
281
282 // Create the actual test files.
283 exterior_file_src_fd = open(exterior_file_src, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
284 assert_with_errno(exterior_file_src_fd >= 0);
285 write_start_and_end_holes(exterior_file_src_fd, block_size);
286
287 interior_file_src_fd = open(interior_file_src, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
288 assert_with_errno(interior_file_src_fd >= 0);
289 write_middle_hole(interior_file_src_fd, block_size);
290
291 // Now, recursively copy our folder using sparse data copying.
292 assert_with_errno(snprintf(exterior_dir_dst, BSIZE_B, "%s/recursive_dst", apfs_test_directory) > 0);
293 assert_no_err(copyfile(exterior_dir_src, exterior_dir_dst, NULL, COPYFILE_ALL|COPYFILE_RECURSIVE|COPYFILE_DATA_SPARSE));
294
295 // The files were (hopefully) copied. Now, we must verify three things:
296 // 1. Verify that the copy is a sparse file.
297 // For now, let's approximate this by testing that the sizes of the two files are equal.
298 // 2. The copy and the source have identical contents.
299
300 // First, construct our destination layout.
301 assert_with_errno(snprintf(exterior_dir_dst, BSIZE_B, "%s/recursive_dst", apfs_test_directory) > 0);
302 create_test_file_name(exterior_dir_dst, "exterior_sparse_file", test_file_id, exterior_file_dst);
303 assert_with_errno(snprintf(interior_dir_dst, BSIZE_B, "%s/interior", exterior_dir_dst) > 0);
304 create_test_file_name(interior_dir_dst, "interior_sparse_file", test_file_id, interior_file_dst);
305
306 // 1. The copy is a sparse file.
307 assert_no_err(fstat(exterior_file_src_fd, &exterior_file_src_sb));
308 assert_no_err(stat(exterior_file_dst, &exterior_file_dst_sb));
309
310 assert_no_err(fstat(interior_file_src_fd, &interior_file_src_sb));
311 assert_no_err(stat(interior_file_dst, &interior_file_dst_sb));
312
313 success &= verify_copy_sizes(&exterior_file_src_sb, &exterior_file_dst_sb, NULL, true, 0);
314 success &= verify_copy_sizes(&interior_file_src_sb, &interior_file_dst_sb, NULL, true, 0);
315
316 // 2. The copy and the source have identical contents.
317 success &= verify_copy_contents(exterior_file_src, exterior_file_dst);
318 success &= verify_copy_contents(interior_file_src, interior_file_dst);
319
320 if (success) {
321 printf("PASS [sparse_recursive]\n");
322 } else {
323 printf("FAIL [sparse_recursive]\n");
324 }
325
326 assert_no_err(close(interior_file_src_fd));
327 assert_no_err(close(exterior_file_src_fd));
328 (void)removefile(exterior_dir_src, NULL, REMOVEFILE_RECURSIVE);
329 (void)removefile(exterior_dir_dst, NULL, REMOVEFILE_RECURSIVE);
330
331 return success ? EXIT_SUCCESS : EXIT_FAILURE;
332 }
333
334 bool do_fcopyfile_offset_test(const char *apfs_test_directory, size_t block_size) {
335 int src_fd, sparse_copy_fd, full_copy_fd, test_file_id;
336 char out_name[BSIZE_B], sparse_copy_name[BSIZE_B], full_copy_name[BSIZE_B];
337 bool success = true;
338
339 printf("START [fcopyfile_offset]\n");
340
341 // Make new names for this file and its copies.
342 test_file_id = rand() % DEFAULT_NAME_MOD;
343
344 create_test_file_name(apfs_test_directory, "foff_sparse", test_file_id, out_name);
345 create_test_file_name(apfs_test_directory, "foff_copy_sparse", test_file_id, sparse_copy_name);
346 create_test_file_name(apfs_test_directory, "foff_copy_full", test_file_id, full_copy_name);
347
348 // Create the test file.
349 src_fd = open(out_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
350 assert_with_errno(src_fd >= 0);
351 // This writes 5 * block_size bytes.
352 assert_with_errno(lseek(src_fd, write_middle_hole(src_fd, block_size), SEEK_SET) == 0);
353
354 // Create a sparse copy using fcopyfile().
355 sparse_copy_fd = open(sparse_copy_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
356 assert_with_errno(sparse_copy_fd >= 0);
357
358 // Seek the sparse copy to a non-zero offset.
359 assert_with_errno(lseek(sparse_copy_fd, block_size, SEEK_SET) == (off_t) block_size);
360 // Write into the sparse copy a different byte.
361 assert_with_errno(pwrite(sparse_copy_fd, "z", 1, block_size) == 1);
362
363 // Run fcopyfile().
364 assert_no_err(fcopyfile(src_fd, sparse_copy_fd, NULL, COPYFILE_ALL|COPYFILE_DATA_SPARSE));
365
366 // Check that the source matches the copy at the appropriate region.
367 success &= verify_fd_contents(src_fd, 0, sparse_copy_fd, block_size, 4 * block_size);
368
369 // Now, repeat the same procedure with a full copy.
370 assert_with_errno(lseek(src_fd, 0, SEEK_SET) == 0);
371 full_copy_fd = open(full_copy_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
372 assert_with_errno(full_copy_name >= 0);
373
374 assert_with_errno(lseek(full_copy_fd, block_size, SEEK_SET) == (off_t) block_size);
375 assert_with_errno(pwrite(full_copy_fd, "r", 1, block_size) == 1);
376
377 // Run fcopyfile().
378 assert_no_err(fcopyfile(src_fd, full_copy_fd, NULL, COPYFILE_ALL));
379
380 // Check that the source matches the copy at the appropriate region.
381 success &= verify_fd_contents(src_fd, 0, full_copy_fd, block_size, 4 * block_size);
382
383 if (success) {
384 printf("PASS [fcopyfile_offset]\n");
385 } else {
386 printf("FAIL [fcopyfile_offset]\n");
387 }
388
389 assert_no_err(close(full_copy_fd));
390 assert_no_err(close(sparse_copy_fd));
391 assert_no_err(close(src_fd));
392 (void)removefile(full_copy_name, NULL, 0);
393 (void)removefile(sparse_copy_name, NULL, 0);
394 (void)removefile(out_name, NULL, 0);
395
396 return success ? EXIT_SUCCESS : EXIT_FAILURE;
397 }