11 #include <removefile.h>
12 #include <sys/fcntl.h>
15 #include "../copyfile.h"
16 #include "sparse_test.h"
17 #include "test_utils.h"
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.
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
;
29 copyfile_state_t cpf_state
;
31 // Get ready for the test.
32 memset(&orig_sb
, 0, sizeof(orig_sb
));
33 memset(©_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
);
37 // First, verify copyfile().
38 copyfile_flags_t flags
= COPYFILE_ALL
;
40 flags
|= COPYFILE_DATA_SPARSE
;
42 assert_no_err(copyfile(orig_name
, copy_name
, cpf_state
, flags
));
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.
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
, ©_sb
));
54 result
&= verify_copy_sizes(&orig_sb
, ©_sb
, cpf_state
, do_sparse
, 0);
56 // 3. The copy and the source have identical contents.
57 result
&= verify_copy_contents(orig_name
, copy_name
);
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(©_sb
, 0, sizeof(struct stat
));
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);
70 assert_with_errno((cpf_state
= copyfile_state_alloc()) != NULL
);
71 assert_no_err(fcopyfile(src_fd
, copy_fd
, cpf_state
, flags
));
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
, ©_sb
));
77 result
&= verify_copy_sizes(&orig_sb
, ©_sb
, cpf_state
,
78 start_off
> 0 ? false : do_sparse
, start_off
);
80 // 3. The copy and the source have identical contents.
82 result
&= verify_copy_contents(orig_name
, copy_name
);
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
));
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
);
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
));
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
));
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
));
112 assert_no_err(create_hole_in_fd(fd
, block_size
, 15 * block_size
));
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);
119 assert_no_err(create_hole_in_fd(fd
, 0, 16 * block_size
));
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
));
128 assert_no_err(create_hole_in_fd(fd
, block_size
, 3 * block_size
));
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);
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
));
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
));
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
));
151 static off_t
write_no_sparse(int fd
, __unused off_t block_size
) {
152 assert_with_errno(pwrite(fd
, "z", 1, 0) == 1);
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);
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);
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);
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
);
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
));
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
));
186 creator_func func
; // pointer to function to create a sparse file
187 const char * name
; // null terminated string
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"}
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
;
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;
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
);
220 // Create the test file.
221 fd
= open(out_name
, DEFAULT_OPEN_FLAGS
, DEFAULT_OPEN_PERM
);
222 assert_with_errno(fd
>= 0);
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
));
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
);
235 // Report the result on stdout.
236 if (!sub_test_success
) {
237 printf("FAIL [%s]\n", test_functions
[sub_test
].name
);
240 printf("PASS [%s]\n", test_functions
[sub_test
].name
);
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
);
253 return success
? EXIT_SUCCESS
: EXIT_FAILURE
;
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
;
263 printf("START [sparse_recursive]\n");
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
));
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);
275 assert_no_err(mkdir(exterior_dir_src
, DEFAULT_MKDIR_PERM
));
276 assert_no_err(mkdir(interior_dir_src
, DEFAULT_MKDIR_PERM
));
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
);
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
);
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
);
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
));
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.
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
);
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
));
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
));
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);
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
);
321 printf("PASS [sparse_recursive]\n");
323 printf("FAIL [sparse_recursive]\n");
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
);
331 return success
? EXIT_SUCCESS
: EXIT_FAILURE
;
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
];
339 printf("START [fcopyfile_offset]\n");
341 // Make new names for this file and its copies.
342 test_file_id
= rand() % DEFAULT_NAME_MOD
;
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
);
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);
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);
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);
364 assert_no_err(fcopyfile(src_fd
, sparse_copy_fd
, NULL
, COPYFILE_ALL
|COPYFILE_DATA_SPARSE
));
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
);
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);
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);
378 assert_no_err(fcopyfile(src_fd
, full_copy_fd
, NULL
, COPYFILE_ALL
));
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
);
384 printf("PASS [fcopyfile_offset]\n");
386 printf("FAIL [fcopyfile_offset]\n");
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);
396 return success
? EXIT_SUCCESS
: EXIT_FAILURE
;