]> git.saurik.com Git - apple/copyfile.git/blame - copyfile_test/sparse_test.c
copyfile-173.40.2.tar.gz
[apple/copyfile.git] / copyfile_test / sparse_test.c
CommitLineData
937356ff
A
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
937356ff
A
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 */
25static 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.
23896e53 67 assert_with_errno((copy_fd = open(copy_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM)) > 0);
937356ff
A
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
23896e53 73 // 1. The copy is a sparse file (if start_off is 0).
937356ff
A
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,
23896e53 78 start_off > 0 ? false : do_sparse, start_off);
937356ff
A
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.
97typedef off_t (*creator_func)(int, off_t);
98
99static 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
108static 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
116static 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
123static 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
132static 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
141static 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
151static 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
156static 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
165static 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
175static 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
85b8a2cb
A
185static off_t write_nothing(__unused int fd, __unused off_t block_size) {
186 return 0;
187}
188
937356ff
A
189typedef struct {
190 creator_func func; // pointer to function to create a sparse file
191 const char * name; // null terminated string
192} sparse_test_func;
193
85b8a2cb 194#define NUM_TEST_FUNCTIONS 11
937356ff
A
195sparse_test_func test_functions[NUM_TEST_FUNCTIONS] = {
196 {write_start_and_end_holes, "start_and_end_holes"},
197 {write_middle_hole, "middle_hole"},
198 {write_start_and_middle_holes, "start_and_middle_holes"},
199 {write_middle_and_end_holes, "middle_and_end_holes"},
200 {write_end_hole, "end_hole"},
201 {write_start_hole, "start_hole"},
202 {write_no_sparse, "no_sparse"},
203 {write_sparse_odd_offset, "write_sparse_odd_offset"},
204 {write_sparse_bs_offset, "write_sparse_bs_offset"},
85b8a2cb
A
205 {write_diff_adj_holes, "write_diff_adj_holes"},
206 {write_nothing, "write_nothing"},
937356ff
A
207};
208
209bool do_sparse_test(const char* apfs_test_directory, size_t block_size) {
210 int fd, test_file_id;
211 char out_name[BSIZE_B], sparse_copy_name[BSIZE_B], full_copy_name[BSIZE_B];
212 bool success = true, sub_test_success;
213 off_t start_off;
214
215 for (size_t sub_test = 0; sub_test < NUM_TEST_FUNCTIONS; sub_test++) {
216 printf("START [%s]\n", test_functions[sub_test].name);
217 sub_test_success = false;
218
219 // Make new names for this file and its copies.
23896e53 220 test_file_id = rand() % DEFAULT_NAME_MOD;
937356ff
A
221 create_test_file_name(apfs_test_directory, "sparse", test_file_id, out_name);
222 create_test_file_name(apfs_test_directory, "copy_sparse", test_file_id, sparse_copy_name);
223 create_test_file_name(apfs_test_directory, "copy_full", test_file_id, full_copy_name);
224
225 // Create the test file.
23896e53 226 fd = open(out_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
937356ff
A
227 assert_with_errno(fd >= 0);
228
229 // Write to the test file, making it sparse.
230 start_off = test_functions[sub_test].func(fd, (off_t) block_size);
231 assert_no_err(fsync(fd));
232
233 // Make sure that a sparse copy is successful.
234 sub_test_success = test_copy(fd, out_name, sparse_copy_name, true, start_off);
235 if (sub_test_success) {
236 // Make sure that a full copy is successful.
237 sub_test_success = test_copy(fd, out_name, full_copy_name, false, start_off);
238 }
239
240 // Report the result on stdout.
241 if (!sub_test_success) {
242 printf("FAIL [%s]\n", test_functions[sub_test].name);
243 success = false;
244 } else {
245 printf("PASS [%s]\n", test_functions[sub_test].name);
246 }
247
248 // Cleanup for the next test.
249 assert_no_err(close(fd));
250 (void)removefile(out_name, NULL, 0);
251 (void)removefile(sparse_copy_name, NULL, 0);
252 (void)removefile(full_copy_name, NULL, 0);
253 memset(out_name, 0, BSIZE_B);
254 memset(sparse_copy_name, 0, BSIZE_B);
255 memset(full_copy_name, 0, BSIZE_B);
256 }
257
258 return success ? EXIT_SUCCESS : EXIT_FAILURE;
259}
260
261bool do_sparse_recursive_test(const char *apfs_test_directory, size_t block_size) {
262 int exterior_file_src_fd, interior_file_src_fd, test_file_id;
263 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};
264 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};
265 struct stat exterior_file_src_sb, interior_file_src_sb, exterior_file_dst_sb, interior_file_dst_sb;
266 bool success = true;
267
268 printf("START [sparse_recursive]\n");
269
270 // Get ready for the test.
271 memset(&exterior_file_src_sb, 0, sizeof(exterior_file_src_sb));
272 memset(&interior_file_src_sb, 0, sizeof(interior_file_src_sb));
273 memset(&exterior_file_dst_sb, 0, sizeof(exterior_file_dst_sb));
274 memset(&interior_file_dst_sb, 0, sizeof(interior_file_dst_sb));
275
276 // Construct our source layout.
277 assert_with_errno(snprintf(exterior_dir_src, BSIZE_B, "%s/recursive_src", apfs_test_directory) > 0);
278 assert_with_errno(snprintf(interior_dir_src, BSIZE_B, "%s/interior", exterior_dir_src) > 0);
279
23896e53
A
280 assert_no_err(mkdir(exterior_dir_src, DEFAULT_MKDIR_PERM));
281 assert_no_err(mkdir(interior_dir_src, DEFAULT_MKDIR_PERM));
937356ff 282
23896e53 283 test_file_id = rand() % DEFAULT_NAME_MOD;
937356ff
A
284 create_test_file_name(exterior_dir_src, "exterior_sparse_file", test_file_id, exterior_file_src);
285 create_test_file_name(interior_dir_src, "interior_sparse_file", test_file_id, interior_file_src);
286
287 // Create the actual test files.
23896e53 288 exterior_file_src_fd = open(exterior_file_src, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
937356ff
A
289 assert_with_errno(exterior_file_src_fd >= 0);
290 write_start_and_end_holes(exterior_file_src_fd, block_size);
291
23896e53 292 interior_file_src_fd = open(interior_file_src, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
937356ff
A
293 assert_with_errno(interior_file_src_fd >= 0);
294 write_middle_hole(interior_file_src_fd, block_size);
295
296 // Now, recursively copy our folder using sparse data copying.
297 assert_with_errno(snprintf(exterior_dir_dst, BSIZE_B, "%s/recursive_dst", apfs_test_directory) > 0);
298 assert_no_err(copyfile(exterior_dir_src, exterior_dir_dst, NULL, COPYFILE_ALL|COPYFILE_RECURSIVE|COPYFILE_DATA_SPARSE));
299
300 // The files were (hopefully) copied. Now, we must verify three things:
301 // 1. Verify that the copy is a sparse file.
302 // For now, let's approximate this by testing that the sizes of the two files are equal.
303 // 2. The copy and the source have identical contents.
304
305 // First, construct our destination layout.
306 assert_with_errno(snprintf(exterior_dir_dst, BSIZE_B, "%s/recursive_dst", apfs_test_directory) > 0);
307 create_test_file_name(exterior_dir_dst, "exterior_sparse_file", test_file_id, exterior_file_dst);
308 assert_with_errno(snprintf(interior_dir_dst, BSIZE_B, "%s/interior", exterior_dir_dst) > 0);
309 create_test_file_name(interior_dir_dst, "interior_sparse_file", test_file_id, interior_file_dst);
310
311 // 1. The copy is a sparse file.
312 assert_no_err(fstat(exterior_file_src_fd, &exterior_file_src_sb));
313 assert_no_err(stat(exterior_file_dst, &exterior_file_dst_sb));
314
315 assert_no_err(fstat(interior_file_src_fd, &interior_file_src_sb));
316 assert_no_err(stat(interior_file_dst, &interior_file_dst_sb));
317
318 success &= verify_copy_sizes(&exterior_file_src_sb, &exterior_file_dst_sb, NULL, true, 0);
319 success &= verify_copy_sizes(&interior_file_src_sb, &interior_file_dst_sb, NULL, true, 0);
320
321 // 2. The copy and the source have identical contents.
322 success &= verify_copy_contents(exterior_file_src, exterior_file_dst);
323 success &= verify_copy_contents(interior_file_src, interior_file_dst);
324
325 if (success) {
326 printf("PASS [sparse_recursive]\n");
327 } else {
328 printf("FAIL [sparse_recursive]\n");
329 }
330
331 assert_no_err(close(interior_file_src_fd));
332 assert_no_err(close(exterior_file_src_fd));
333 (void)removefile(exterior_dir_src, NULL, REMOVEFILE_RECURSIVE);
334 (void)removefile(exterior_dir_dst, NULL, REMOVEFILE_RECURSIVE);
335
336 return success ? EXIT_SUCCESS : EXIT_FAILURE;
337}
338
339bool do_fcopyfile_offset_test(const char *apfs_test_directory, size_t block_size) {
340 int src_fd, sparse_copy_fd, full_copy_fd, test_file_id;
341 char out_name[BSIZE_B], sparse_copy_name[BSIZE_B], full_copy_name[BSIZE_B];
342 bool success = true;
343
344 printf("START [fcopyfile_offset]\n");
345
346 // Make new names for this file and its copies.
23896e53 347 test_file_id = rand() % DEFAULT_NAME_MOD;
937356ff
A
348
349 create_test_file_name(apfs_test_directory, "foff_sparse", test_file_id, out_name);
350 create_test_file_name(apfs_test_directory, "foff_copy_sparse", test_file_id, sparse_copy_name);
351 create_test_file_name(apfs_test_directory, "foff_copy_full", test_file_id, full_copy_name);
352
353 // Create the test file.
23896e53 354 src_fd = open(out_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
937356ff
A
355 assert_with_errno(src_fd >= 0);
356 // This writes 5 * block_size bytes.
357 assert_with_errno(lseek(src_fd, write_middle_hole(src_fd, block_size), SEEK_SET) == 0);
358
359 // Create a sparse copy using fcopyfile().
23896e53 360 sparse_copy_fd = open(sparse_copy_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
937356ff
A
361 assert_with_errno(sparse_copy_fd >= 0);
362
363 // Seek the sparse copy to a non-zero offset.
364 assert_with_errno(lseek(sparse_copy_fd, block_size, SEEK_SET) == (off_t) block_size);
365 // Write into the sparse copy a different byte.
366 assert_with_errno(pwrite(sparse_copy_fd, "z", 1, block_size) == 1);
367
368 // Run fcopyfile().
369 assert_no_err(fcopyfile(src_fd, sparse_copy_fd, NULL, COPYFILE_ALL|COPYFILE_DATA_SPARSE));
370
371 // Check that the source matches the copy at the appropriate region.
372 success &= verify_fd_contents(src_fd, 0, sparse_copy_fd, block_size, 4 * block_size);
373
374 // Now, repeat the same procedure with a full copy.
375 assert_with_errno(lseek(src_fd, 0, SEEK_SET) == 0);
23896e53 376 full_copy_fd = open(full_copy_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
937356ff
A
377 assert_with_errno(full_copy_name >= 0);
378
379 assert_with_errno(lseek(full_copy_fd, block_size, SEEK_SET) == (off_t) block_size);
380 assert_with_errno(pwrite(full_copy_fd, "r", 1, block_size) == 1);
381
382 // Run fcopyfile().
383 assert_no_err(fcopyfile(src_fd, full_copy_fd, NULL, COPYFILE_ALL));
384
385 // Check that the source matches the copy at the appropriate region.
386 success &= verify_fd_contents(src_fd, 0, full_copy_fd, block_size, 4 * block_size);
387
388 if (success) {
389 printf("PASS [fcopyfile_offset]\n");
390 } else {
391 printf("FAIL [fcopyfile_offset]\n");
392 }
393
394 assert_no_err(close(full_copy_fd));
395 assert_no_err(close(sparse_copy_fd));
396 assert_no_err(close(src_fd));
397 (void)removefile(full_copy_name, NULL, 0);
398 (void)removefile(sparse_copy_name, NULL, 0);
399 (void)removefile(out_name, NULL, 0);
400
401 return success ? EXIT_SUCCESS : EXIT_FAILURE;
402}