]> git.saurik.com Git - apple/copyfile.git/blame - copyfile_test/sparse_test.c
copyfile-146.50.5.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
20#define OPEN_FLAGS O_CREAT|O_TRUNC|O_RDWR
21#define OPEN_PERM 0666
22#define MKDIR_PERM 0777
23#define NAME_MOD 999
24
25/*
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.
29 */
30static 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;
32 int copy_fd;
33 bool result = true;
34 copyfile_state_t cpf_state;
35
36 // Get ready for the test.
37 memset(&orig_sb, 0, sizeof(orig_sb));
38 memset(&copy_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);
41
42 // First, verify copyfile().
43 copyfile_flags_t flags = COPYFILE_ALL;
44 if (do_sparse) {
45 flags |= COPYFILE_DATA_SPARSE;
46 }
47 assert_no_err(copyfile(orig_name, copy_name, cpf_state, flags));
48
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.
54
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, &copy_sb));
59 result &= verify_copy_sizes(&orig_sb, &copy_sb, cpf_state, do_sparse, 0);
60
61 // 3. The copy and the source have identical contents.
62 result &= verify_copy_contents(orig_name, copy_name);
63
64 // Post-test cleanup.
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(&copy_sb, 0, sizeof(struct stat));
69
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);
73
74 // Call fcopyfile().
75 assert_with_errno((cpf_state = copyfile_state_alloc()) != NULL);
76 assert_no_err(fcopyfile(src_fd, copy_fd, cpf_state, flags));
77
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, &copy_sb));
82 result &= verify_copy_sizes(&orig_sb, &copy_sb, cpf_state,
83 start_off % copy_sb.st_blksize ? false : do_sparse, start_off);
84
85 // 3. The copy and the source have identical contents.
86 if (start_off == 0) {
87 result &= verify_copy_contents(orig_name, copy_name);
88 }
89
90 // Post-test cleanup.
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));
94
95 return result;
96}
97
98
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.
102typedef off_t (*creator_func)(int, off_t);
103
104static 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));
107
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));
110 return 0;
111}
112
113static 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));
116
117 assert_no_err(create_hole_in_fd(fd, block_size, 15 * block_size));
118 return 0;
119}
120
121static off_t write_start_hole(int fd, off_t block_size) {
122 assert_with_errno(pwrite(fd, "p", 1, 16 * block_size) == 1);
123
124 assert_no_err(create_hole_in_fd(fd, 0, 16 * block_size));
125 return 0;
126}
127
128static 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));
132
133 assert_no_err(create_hole_in_fd(fd, block_size, 3 * block_size));
134 return 0;
135}
136
137static 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);
140
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));
143 return 0;
144}
145
146static 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));
150
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));
153 return 0;
154}
155
156static off_t write_no_sparse(int fd, __unused off_t block_size) {
157 assert_with_errno(pwrite(fd, "z", 1, 0) == 1);
158 return 0;
159}
160
161static off_t write_sparse_odd_offset(int fd, off_t block_size) {
162 assert_with_errno(pwrite(fd, "q", 1, block_size) == 1);
163
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);
167 return 1;
168}
169
170static 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);
173
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);
177 return block_size;
178}
179
180static 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));
184
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));
187 return 0;
188}
189
190typedef struct {
191 creator_func func; // pointer to function to create a sparse file
192 const char * name; // null terminated string
193} sparse_test_func;
194
195#define NUM_TEST_FUNCTIONS 10
196sparse_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"}
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.
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);
224
225 // Create the test file.
226 fd = open(out_name, OPEN_FLAGS, OPEN_PERM);
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
280 assert_no_err(mkdir(exterior_dir_src, MKDIR_PERM));
281 assert_no_err(mkdir(interior_dir_src, MKDIR_PERM));
282
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);
286
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);
291
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);
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.
347 test_file_id = rand() % NAME_MOD;
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.
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);
358
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);
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);
376 full_copy_fd = open(full_copy_name, OPEN_FLAGS, OPEN_PERM);
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}