11 #include <removefile.h>
12 #include <sys/fcntl.h>
15 #include "../copyfile.h"
16 #include "sparse_test.h"
17 #include "test_utils.h"
20 #define OPEN_FLAGS O_CREAT|O_TRUNC|O_RDWR
21 #define OPEN_PERM 0666
22 #define MKDIR_PERM 0777
26 * Copy the file pointed to by src_fd (and orig_name) to copy_name,
27 * using copyfile()/fcopyfile() and COPYFILE_DATA. If do_sparse, also pass COPYFILE_DATA_SPARSE.
28 * Before copying, rewind src_fd to start_off.
30 static bool test_copy(int src_fd
, char* orig_name
, char* copy_name
, bool do_sparse
, off_t start_off
) {
31 struct stat orig_sb
, copy_sb
;
34 copyfile_state_t cpf_state
;
36 // Get ready for the test.
37 memset(&orig_sb
, 0, sizeof(orig_sb
));
38 memset(©_sb
, 0, sizeof(copy_sb
));
39 assert_with_errno((cpf_state
= copyfile_state_alloc()) != NULL
);
40 assert_with_errno(lseek(src_fd
, start_off
, SEEK_SET
) == start_off
);
42 // First, verify copyfile().
43 copyfile_flags_t flags
= COPYFILE_ALL
;
45 flags
|= COPYFILE_DATA_SPARSE
;
47 assert_no_err(copyfile(orig_name
, copy_name
, cpf_state
, flags
));
49 // The file was (hopefully) copied. Now, we must verify three things:
50 // 1. If (do_sparse), verify that the copy is a sparse file.
51 // For now, let's approximate this by testing that the sizes of the two files are equal.
52 // 2. The copyfile_state_t for the copy returns that all bytes were copied.
53 // 3. The copy and the source have identical contents.
55 // 1. The copy is a sparse file.
56 // 2. The copyfile_state_t for the copy returns that all bytes were copied.
57 assert_no_err(stat(orig_name
, &orig_sb
));
58 assert_no_err(stat(copy_name
, ©_sb
));
59 result
&= verify_copy_sizes(&orig_sb
, ©_sb
, cpf_state
, do_sparse
, 0);
61 // 3. The copy and the source have identical contents.
62 result
&= verify_copy_contents(orig_name
, copy_name
);
65 assert_no_err(copyfile_state_free(cpf_state
));
66 assert_no_err(removefile(copy_name
, NULL
, REMOVEFILE_RECURSIVE
));
67 memset(&orig_sb
, 0, sizeof(struct stat
));
68 memset(©_sb
, 0, sizeof(struct stat
));
70 // Next, verify fcopyfile().
71 // Make an fd for the destination.
72 assert_with_errno((copy_fd
= open(copy_name
, OPEN_FLAGS
, OPEN_PERM
)) > 0);
75 assert_with_errno((cpf_state
= copyfile_state_alloc()) != NULL
);
76 assert_no_err(fcopyfile(src_fd
, copy_fd
, cpf_state
, flags
));
78 // 1. The copy is a sparse file (if start_off is a multiple of the block size).
79 // 2. The copyfile_state_t for the copy returns that all bytes were copied.
80 assert_no_err(fstat(src_fd
, &orig_sb
));
81 assert_no_err(fstat(copy_fd
, ©_sb
));
82 result
&= verify_copy_sizes(&orig_sb
, ©_sb
, cpf_state
,
83 start_off
% copy_sb
.st_blksize
? false : do_sparse
, start_off
);
85 // 3. The copy and the source have identical contents.
87 result
&= verify_copy_contents(orig_name
, copy_name
);
91 assert_no_err(copyfile_state_free(cpf_state
));
92 assert_no_err(removefile(copy_name
, NULL
, REMOVEFILE_RECURSIVE
));
93 assert_no_err(close(copy_fd
));
99 // Sparse file creation functions.
100 // Each take the source file descriptor pointing at the beginning of the file and the block size.
101 // Each return the offset we should return the fd to before any copying should be performed.
102 typedef off_t (*creator_func
)(int, off_t
);
104 static off_t
write_start_and_end_holes(int fd
, off_t block_size
) {
105 assert_with_errno(pwrite(fd
, "j", 1, block_size
) == 1);
106 assert_no_err(ftruncate(fd
, 3 * block_size
));
108 assert_no_err(create_hole_in_fd(fd
, 0, block_size
));
109 assert_no_err(create_hole_in_fd(fd
, 2 * block_size
, block_size
));
113 static off_t
write_end_hole(int fd
, off_t block_size
) {
114 assert_with_errno(pwrite(fd
, "n", 1, 0) == 1);
115 assert_no_err(ftruncate(fd
, 16 * block_size
));
117 assert_no_err(create_hole_in_fd(fd
, block_size
, 15 * block_size
));
121 static off_t
write_start_hole(int fd
, off_t block_size
) {
122 assert_with_errno(pwrite(fd
, "p", 1, 16 * block_size
) == 1);
124 assert_no_err(create_hole_in_fd(fd
, 0, 16 * block_size
));
128 static off_t
write_middle_hole(int fd
, off_t block_size
) {
129 assert_with_errno(pwrite(fd
, "k", 1, 0) == 1);
130 assert_with_errno(pwrite(fd
, "k", 1, 4 * block_size
) == 1);
131 assert_no_err(ftruncate(fd
, 5 * block_size
));
133 assert_no_err(create_hole_in_fd(fd
, block_size
, 3 * block_size
));
137 static off_t
write_start_and_middle_holes(int fd
, off_t block_size
) {
138 assert_with_errno(pwrite(fd
, "l", 1, 16 * block_size
) == 1);
139 assert_with_errno(pwrite(fd
, "l", 1, 32 * block_size
) == 1);
141 assert_no_err(create_hole_in_fd(fd
, 0, 16 * block_size
));
142 assert_no_err(create_hole_in_fd(fd
, 17 * block_size
, 15 * block_size
));
146 static off_t
write_middle_and_end_holes(int fd
, off_t block_size
) {
147 assert_with_errno(pwrite(fd
, "m", 1, 0) == 1);
148 assert_with_errno(pwrite(fd
, "m", 1, 16 * block_size
) == 1);
149 assert_no_err(ftruncate(fd
, 32 * block_size
));
151 assert_no_err(create_hole_in_fd(fd
, block_size
, 15 * block_size
));
152 assert_no_err(create_hole_in_fd(fd
, 17 * block_size
, 15 * block_size
));
156 static off_t
write_no_sparse(int fd
, __unused off_t block_size
) {
157 assert_with_errno(pwrite(fd
, "z", 1, 0) == 1);
161 static off_t
write_sparse_odd_offset(int fd
, off_t block_size
) {
162 assert_with_errno(pwrite(fd
, "q", 1, block_size
) == 1);
164 assert_no_err(create_hole_in_fd(fd
, 0, block_size
));
165 // Return with the fd pointing at offset 1.
166 assert_with_errno(lseek(fd
, 1, SEEK_SET
) == 1);
170 static off_t
write_sparse_bs_offset(int fd
, off_t block_size
) {
171 assert_with_errno(pwrite(fd
, "a", 1, block_size
) == 1);
172 assert_with_errno(pwrite(fd
, "b", 1, 2 * block_size
) == 1);
174 assert_no_err(create_hole_in_fd(fd
, 0, block_size
));
175 // Return with the fd pointing at block_size.
176 assert_with_errno(lseek(fd
, block_size
, SEEK_SET
) == block_size
);
180 static off_t
write_diff_adj_holes(int fd
, off_t block_size
) {
181 assert_with_errno(pwrite(fd
, "w", 1, 0));
182 assert_with_errno(pwrite(fd
, "w", 1, 3 * block_size
));
183 assert_no_err(ftruncate(fd
, 4 * block_size
));
185 assert_no_err(create_hole_in_fd(fd
, block_size
, block_size
));
186 assert_no_err(create_hole_in_fd(fd
, 2 * block_size
, block_size
));
191 creator_func func
; // pointer to function to create a sparse file
192 const char * name
; // null terminated string
195 #define NUM_TEST_FUNCTIONS 10
196 sparse_test_func test_functions
[NUM_TEST_FUNCTIONS
] = {
197 {write_start_and_end_holes
, "start_and_end_holes"},
198 {write_middle_hole
, "middle_hole"},
199 {write_start_and_middle_holes
, "start_and_middle_holes"},
200 {write_middle_and_end_holes
, "middle_and_end_holes"},
201 {write_end_hole
, "end_hole"},
202 {write_start_hole
, "start_hole"},
203 {write_no_sparse
, "no_sparse"},
204 {write_sparse_odd_offset
, "write_sparse_odd_offset"},
205 {write_sparse_bs_offset
, "write_sparse_bs_offset"},
206 {write_diff_adj_holes
, "write_diff_adj_holes"}
209 bool 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
;
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;
219 // Make new names for this file and its copies.
220 test_file_id
= rand() % NAME_MOD
;
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
);
225 // Create the test file.
226 fd
= open(out_name
, OPEN_FLAGS
, OPEN_PERM
);
227 assert_with_errno(fd
>= 0);
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
));
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
);
240 // Report the result on stdout.
241 if (!sub_test_success
) {
242 printf("FAIL [%s]\n", test_functions
[sub_test
].name
);
245 printf("PASS [%s]\n", test_functions
[sub_test
].name
);
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
);
258 return success
? EXIT_SUCCESS
: EXIT_FAILURE
;
261 bool 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
;
268 printf("START [sparse_recursive]\n");
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
));
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);
280 assert_no_err(mkdir(exterior_dir_src
, MKDIR_PERM
));
281 assert_no_err(mkdir(interior_dir_src
, MKDIR_PERM
));
283 test_file_id
= rand() % NAME_MOD
;
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
);
287 // Create the actual test files.
288 exterior_file_src_fd
= open(exterior_file_src
, OPEN_FLAGS
, OPEN_PERM
);
289 assert_with_errno(exterior_file_src_fd
>= 0);
290 write_start_and_end_holes(exterior_file_src_fd
, block_size
);
292 interior_file_src_fd
= open(interior_file_src
, OPEN_FLAGS
, OPEN_PERM
);
293 assert_with_errno(interior_file_src_fd
>= 0);
294 write_middle_hole(interior_file_src_fd
, block_size
);
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
));
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.
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
);
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
));
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
));
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);
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
);
326 printf("PASS [sparse_recursive]\n");
328 printf("FAIL [sparse_recursive]\n");
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
);
336 return success
? EXIT_SUCCESS
: EXIT_FAILURE
;
339 bool 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
];
344 printf("START [fcopyfile_offset]\n");
346 // Make new names for this file and its copies.
347 test_file_id
= rand() % NAME_MOD
;
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
);
353 // Create the test file.
354 src_fd
= open(out_name
, OPEN_FLAGS
, OPEN_PERM
);
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);
359 // Create a sparse copy using fcopyfile().
360 sparse_copy_fd
= open(sparse_copy_name
, OPEN_FLAGS
, OPEN_PERM
);
361 assert_with_errno(sparse_copy_fd
>= 0);
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);
369 assert_no_err(fcopyfile(src_fd
, sparse_copy_fd
, NULL
, COPYFILE_ALL
|COPYFILE_DATA_SPARSE
));
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
);
374 // Now, repeat the same procedure with a full copy.
375 assert_with_errno(lseek(src_fd
, 0, SEEK_SET
) == 0);
376 full_copy_fd
= open(full_copy_name
, OPEN_FLAGS
, OPEN_PERM
);
377 assert_with_errno(full_copy_name
>= 0);
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);
383 assert_no_err(fcopyfile(src_fd
, full_copy_fd
, NULL
, COPYFILE_ALL
));
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
);
389 printf("PASS [fcopyfile_offset]\n");
391 printf("FAIL [fcopyfile_offset]\n");
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);
401 return success
? EXIT_SUCCESS
: EXIT_FAILURE
;