1 /*-
2 * Copyright (c) 2010-2012 Michihiro NAKAJIMA
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25 #include "test.h"
26
27 #ifdef HAVE_SYS_IOCTL_H
28 #include <sys/ioctl.h>
29 #endif
30 #ifdef HAVE_SYS_PARAM_H
31 #include <sys/param.h>
32 #endif
33 #ifdef HAVE_FCNTL_H
34 #include <fcntl.h>
35 #endif
36 #ifdef HAVE_LIMITS_H
37 #include <limits.h>
38 #endif
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42 #ifdef HAVE_LINUX_TYPES_H
43 #include <linux/types.h>
44 #endif
45 #ifdef HAVE_LINUX_FIEMAP_H
46 #include <linux/fiemap.h>
47 #endif
48 #ifdef HAVE_LINUX_FS_H
49 #include <linux/fs.h>
50 #endif
51
52 /* The logic to compare sparse file data read from disk with the
53 * specification is a little involved. Set to 1 to have the progress
54 * dumped. */
55 #define DEBUG 0
56
57 /*
58 * NOTE: On FreeBSD and Solaris, this test needs ZFS.
59 * You may perform this test as
60 * 'TMPDIR=<a directory on the ZFS> libarchive_test'.
61 */
62
63 struct sparse {
64 enum { DATA, HOLE, END } type;
65 size_t size;
66 };
67
68 static void create_sparse_file(const char *, const struct sparse *);
69
70 #if defined(__APPLE__)
71 /* On APFS holes need to be at least 4096x4097 bytes */
72 #define MIN_HOLE 16781312
73 #else
74 /* Elsewhere we work with 4096*10 bytes */
75 #define MIN_HOLE 409600
76 #endif
77
78 #if defined(_WIN32) && !defined(__CYGWIN__)
79 #include <winioctl.h>
80 /*
81 * Create a sparse file on Windows.
82 */
83
84 #if !defined(PATH_MAX)
85 #define PATH_MAX MAX_PATH
86 #endif
87 #if !defined(__BORLANDC__)
88 #define getcwd _getcwd
89 #endif
90
91 static int
is_sparse_supported(const char * path)92 is_sparse_supported(const char *path)
93 {
94 char root[MAX_PATH+1];
95 char vol[MAX_PATH+1];
96 char sys[MAX_PATH+1];
97 DWORD flags;
98 BOOL r;
99
100 strncpy(root, path, sizeof(root)-1);
101 if (((root[0] >= 'c' && root[0] <= 'z') ||
102 (root[0] >= 'C' && root[0] <= 'Z')) &&
103 root[1] == ':' &&
104 (root[2] == '\\' || root[2] == '/'))
105 root[3] = '\0';
106 else
107 return (0);
108 assertEqualInt((r = GetVolumeInformation(root, vol,
109 sizeof(vol), NULL, NULL, &flags, sys, sizeof(sys))), 1);
110 return (r != 0 && (flags & FILE_SUPPORTS_SPARSE_FILES) != 0);
111 }
112
113 static void
create_sparse_file(const char * path,const struct sparse * s)114 create_sparse_file(const char *path, const struct sparse *s)
115 {
116 char buff[1024];
117 HANDLE handle;
118 DWORD dmy;
119
120 memset(buff, ' ', sizeof(buff));
121
122 handle = CreateFileA(path, GENERIC_WRITE, 0,
123 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
124 NULL);
125 assert(handle != INVALID_HANDLE_VALUE);
126 assert(DeviceIoControl(handle, FSCTL_SET_SPARSE, NULL, 0,
127 NULL, 0, &dmy, NULL) != 0);
128
129 uint64_t offsetSoFar = 0;
130
131 while (s->type != END) {
132 if (s->type == HOLE) {
133 LARGE_INTEGER fileOffset, beyondOffset, distanceToMove;
134 fileOffset.QuadPart = offsetSoFar;
135 beyondOffset.QuadPart = offsetSoFar + s->size;
136 distanceToMove.QuadPart = s->size;
137
138 FILE_ZERO_DATA_INFORMATION zeroInformation;
139 zeroInformation.FileOffset = fileOffset;
140 zeroInformation.BeyondFinalZero = beyondOffset;
141
142 DWORD bytesReturned;
143 assert(SetFilePointerEx(handle, distanceToMove,
144 NULL, FILE_CURRENT) != 0);
145 assert(SetEndOfFile(handle) != 0);
146 assert(DeviceIoControl(handle, FSCTL_SET_ZERO_DATA, &zeroInformation,
147 sizeof(FILE_ZERO_DATA_INFORMATION), NULL, 0, &bytesReturned, NULL) != 0);
148 } else {
149 DWORD w, wr;
150 size_t size;
151
152 size = s->size;
153 while (size) {
154 if (size > sizeof(buff))
155 w = sizeof(buff);
156 else
157 w = (DWORD)size;
158 assert(WriteFile(handle, buff, w, &wr, NULL) != 0);
159 size -= wr;
160 }
161 }
162 offsetSoFar += s->size;
163 s++;
164 }
165 assertEqualInt(CloseHandle(handle), 1);
166 }
167
168 #else
169
170 #if defined(HAVE_LINUX_FIEMAP_H)
171 /*
172 * FIEMAP, which can detect 'hole' of a sparse file, has
173 * been supported from 2.6.28
174 */
175
176 static int
is_sparse_supported_fiemap(const char * path)177 is_sparse_supported_fiemap(const char *path)
178 {
179 const struct sparse sparse_file[] = {
180 /* This hole size is too small to create a sparse
181 * files for almost filesystem. */
182 { HOLE, 1024 }, { DATA, 10240 },
183 { END, 0 }
184 };
185 int fd, r;
186 struct fiemap *fm;
187 char buff[1024];
188 const char *testfile = "can_sparse";
189
190 (void)path; /* UNUSED */
191 memset(buff, 0, sizeof(buff));
192 create_sparse_file(testfile, sparse_file);
193 fd = open(testfile, O_RDWR);
194 if (fd < 0)
195 return (0);
196 fm = (struct fiemap *)buff;
197 fm->fm_start = 0;
198 fm->fm_length = ~0ULL;;
199 fm->fm_flags = FIEMAP_FLAG_SYNC;
200 fm->fm_extent_count = (sizeof(buff) - sizeof(*fm))/
201 sizeof(struct fiemap_extent);
202 r = ioctl(fd, FS_IOC_FIEMAP, fm);
203 close(fd);
204 unlink(testfile);
205 return (r >= 0);
206 }
207
208 #if !defined(SEEK_HOLE) || !defined(SEEK_DATA)
209 static int
is_sparse_supported(const char * path)210 is_sparse_supported(const char *path)
211 {
212 return is_sparse_supported_fiemap(path);
213 }
214 #endif
215 #endif
216
217 #if defined(_PC_MIN_HOLE_SIZE)
218
219 /*
220 * FreeBSD and Solaris can detect 'hole' of a sparse file
221 * through lseek(HOLE) on ZFS. (UFS does not support yet)
222 */
223
224 static int
is_sparse_supported(const char * path)225 is_sparse_supported(const char *path)
226 {
227 return (pathconf(path, _PC_MIN_HOLE_SIZE) > 0);
228 }
229
230 #elif defined(SEEK_HOLE) && defined(SEEK_DATA)
231
232 static int
is_sparse_supported(const char * path)233 is_sparse_supported(const char *path)
234 {
235 const struct sparse sparse_file[] = {
236 /* This hole size is too small to create a sparse
237 * files for almost filesystem. */
238 { HOLE, 1024 }, { DATA, 10240 },
239 { END, 0 }
240 };
241 int fd, r;
242 const char *testfile = "can_sparse";
243
244 (void)path; /* UNUSED */
245 create_sparse_file(testfile, sparse_file);
246 fd = open(testfile, O_RDWR);
247 if (fd < 0)
248 return (0);
249 r = lseek(fd, 0, SEEK_HOLE);
250 close(fd);
251 unlink(testfile);
252 #if defined(HAVE_LINUX_FIEMAP_H)
253 if (r < 0)
254 return (is_sparse_supported_fiemap(path));
255 return (1);
256 #else
257 return (r >= 0);
258 #endif
259 }
260
261 #elif !defined(HAVE_LINUX_FIEMAP_H)
262
263 /*
264 * Other system may do not have the API such as lseek(HOLE),
265 * which detect 'hole' of a sparse file.
266 */
267
268 static int
is_sparse_supported(const char * path)269 is_sparse_supported(const char *path)
270 {
271 (void)path; /* UNUSED */
272 return (0);
273 }
274
275 #endif
276
277 /*
278 * Create a sparse file on POSIX like system.
279 */
280
281 static void
create_sparse_file(const char * path,const struct sparse * s)282 create_sparse_file(const char *path, const struct sparse *s)
283 {
284 char buff[1024];
285 int fd;
286 uint64_t total_size = 0;
287 const struct sparse *cur = s;
288
289 memset(buff, ' ', sizeof(buff));
290 assert((fd = open(path, O_CREAT | O_WRONLY, 0600)) != -1);
291
292 /* Handle holes at the end by extending the file */
293 while (cur->type != END) {
294 total_size += cur->size;
295 ++cur;
296 }
297 assert(ftruncate(fd, total_size) != -1);
298
299 while (s->type != END) {
300 if (s->type == HOLE) {
301 assert(lseek(fd, s->size, SEEK_CUR) != (off_t)-1);
302 } else {
303 size_t w, size;
304
305 size = s->size;
306 while (size) {
307 if (size > sizeof(buff))
308 w = sizeof(buff);
309 else
310 w = size;
311 assert(write(fd, buff, w) != (ssize_t)-1);
312 size -= w;
313 }
314 }
315 s++;
316 }
317 close(fd);
318 }
319
320 #endif
321
322 /*
323 * Sparse test with directory traversals.
324 */
325 static void
verify_sparse_file(struct archive * a,const char * path,const struct sparse * sparse,int expected_holes)326 verify_sparse_file(struct archive *a, const char *path,
327 const struct sparse *sparse, int expected_holes)
328 {
329 struct archive_entry *ae;
330 const void *buff;
331 size_t bytes_read;
332 int64_t offset, expected_offset, last_offset;
333 int holes_seen = 0;
334
335 create_sparse_file(path, sparse);
336 assert((ae = archive_entry_new()) != NULL);
337 assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, path));
338 assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae));
339
340 expected_offset = 0;
341 last_offset = 0;
342 while (ARCHIVE_OK == archive_read_data_block(a, &buff, &bytes_read,
343 &offset)) {
344 const char *start = buff;
345 #if DEBUG
346 fprintf(stderr, "%s: bytes_read=%d offset=%d\n", path, (int)bytes_read, (int)offset);
347 #endif
348 if (offset > last_offset) {
349 ++holes_seen;
350 }
351 /* Blocks entirely before the data we just read. */
352 while (expected_offset + (int64_t)sparse->size < offset) {
353 #if DEBUG
354 fprintf(stderr, " skipping expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
355 #endif
356 /* Must be holes. */
357 assert(sparse->type == HOLE);
358 expected_offset += sparse->size;
359 ++sparse;
360 }
361 /* Block that overlaps beginning of data */
362 if (expected_offset < offset
363 && expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) {
364 const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size;
365 #if DEBUG
366 fprintf(stderr, " overlapping hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
367 #endif
368 if (sparse->type == HOLE) {
369 assertMemoryFilledWith(start, end - start, '\0');
370 } else if (assert(sparse->type == DATA)) {
371 assertMemoryFilledWith(start, end - start, ' ');
372 }
373 start = end;
374 expected_offset += sparse->size;
375 ++sparse;
376 }
377 /* Blocks completely contained in data we just read. */
378 while (expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) {
379 const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size;
380 if (sparse->type == HOLE) {
381 #if DEBUG
382 fprintf(stderr, " contained hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
383 #endif
384
385 /* verify data corresponding to hole is '\0' */
386 if (end > (const char *)buff + bytes_read) {
387 end = (const char *)buff + bytes_read;
388 }
389 assertMemoryFilledWith(start, end - start, '\0');
390 start = end;
391 expected_offset += sparse->size;
392 ++sparse;
393 } else if (sparse->type == DATA) {
394 #if DEBUG
395 fprintf(stderr, " contained data expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
396 #endif
397 /* verify data corresponding to hole is ' ' */
398 if (assert(expected_offset + sparse->size <= offset + bytes_read)) {
399 assert(start == (const char *)buff + (size_t)(expected_offset - offset));
400 assertMemoryFilledWith(start, end - start, ' ');
401 }
402 start = end;
403 expected_offset += sparse->size;
404 ++sparse;
405 } else {
406 break;
407 }
408 }
409 /* Block that overlaps end of data */
410 if (expected_offset < offset + (int64_t)bytes_read) {
411 const char *end = (const char *)buff + bytes_read;
412 #if DEBUG
413 fprintf(stderr, " trailing overlap expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
414 #endif
415 if (sparse->type == HOLE) {
416 assertMemoryFilledWith(start, end - start, '\0');
417 } else if (assert(sparse->type == DATA)) {
418 assertMemoryFilledWith(start, end - start, ' ');
419 }
420 }
421 last_offset = offset + bytes_read;
422 }
423 /* Count a hole at EOF? */
424 if (last_offset < archive_entry_size(ae)) {
425 ++holes_seen;
426 }
427
428 /* Verify blocks after last read */
429 while (sparse->type == HOLE) {
430 expected_offset += sparse->size;
431 ++sparse;
432 }
433 assert(sparse->type == END);
434 assertEqualInt(expected_offset, archive_entry_size(ae));
435
436 failure("%s", path);
437 assertEqualInt(holes_seen, expected_holes);
438
439 assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
440 archive_entry_free(ae);
441 }
442
443 #if defined(_WIN32) && !defined(__CYGWIN__)
444 #define close _close
445 #define open _open
446 #endif
447
448 /*
449 * Sparse test without directory traversals.
450 */
451 static void
verify_sparse_file2(struct archive * a,const char * path,const struct sparse * sparse,int blocks,int preopen)452 verify_sparse_file2(struct archive *a, const char *path,
453 const struct sparse *sparse, int blocks, int preopen)
454 {
455 struct archive_entry *ae;
456 int fd;
457
458 (void)sparse; /* UNUSED */
459 assert((ae = archive_entry_new()) != NULL);
460 archive_entry_set_pathname(ae, path);
461 if (preopen)
462 fd = open(path, O_RDONLY | O_BINARY);
463 else
464 fd = -1;
465 assertEqualIntA(a, ARCHIVE_OK,
466 archive_read_disk_entry_from_file(a, ae, fd, NULL));
467 if (fd >= 0)
468 close(fd);
469 /* Verify the number of holes only, not its offset nor its
470 * length because those alignments are deeply dependence on
471 * its filesystem. */
472 failure("%s", path);
473 assertEqualInt(blocks, archive_entry_sparse_count(ae));
474 archive_entry_free(ae);
475 }
476
477 static void
test_sparse_whole_file_data(void)478 test_sparse_whole_file_data(void)
479 {
480 struct archive_entry *ae;
481 int64_t offset;
482 int i;
483
484 assert((ae = archive_entry_new()) != NULL);
485 archive_entry_set_size(ae, 1024*10);
486
487 /*
488 * Add sparse block data up to the file size.
489 */
490 offset = 0;
491 for (i = 0; i < 10; i++) {
492 archive_entry_sparse_add_entry(ae, offset, 1024);
493 offset += 1024;
494 }
495
496 failure("There should be no sparse");
497 assertEqualInt(0, archive_entry_sparse_count(ae));
498 archive_entry_free(ae);
499 }
500
DEFINE_TEST(test_sparse_basic)501 DEFINE_TEST(test_sparse_basic)
502 {
503 char *cwd;
504 struct archive *a;
505 const char *skip_sparse_tests;
506 /*
507 * The alignment of the hole of sparse files deeply depends
508 * on filesystem. In my experience, sparse_file2 test with
509 * 204800 bytes hole size did not pass on ZFS and the result
510 * of that test seemed the size was too small, thus you should
511 * keep a hole size more than 409600 bytes to pass this test
512 * on all platform.
513 */
514 const struct sparse sparse_file0[] = {
515 // 0 // 1024
516 { DATA, 1024 }, { HOLE, MIN_HOLE + 1638400 },
517 // 2049024 // 2051072
518 { DATA, 2048 }, { HOLE, MIN_HOLE + 1638400 },
519 // 4099072 // 4103168
520 { DATA, 4096 }, { HOLE, MIN_HOLE + 20070400 },
521 // 24583168 // 24591360
522 { DATA, 8192 }, { HOLE, MIN_HOLE + 204390400 },
523 // 229391360 // 229391361
524 { DATA, 1 }, { END, 0 }
525 };
526 const struct sparse sparse_file1[] = {
527 { HOLE, MIN_HOLE }, { DATA, 1 },
528 { HOLE, MIN_HOLE }, { DATA, 1 },
529 { HOLE, MIN_HOLE }, { END, 0 }
530 };
531 const struct sparse sparse_file2[] = {
532 { HOLE, MIN_HOLE }, { DATA, 1024 },
533 { HOLE, MIN_HOLE + 409600 * 1 }, { DATA, 1024 },
534 { HOLE, MIN_HOLE + 409600 * 2 }, { DATA, 1024 },
535 { HOLE, MIN_HOLE + 409600 * 3 }, { DATA, 1024 },
536 { HOLE, MIN_HOLE + 409600 * 4 }, { DATA, 1024 },
537 { HOLE, MIN_HOLE + 409600 * 5 }, { DATA, 1024 },
538 { HOLE, MIN_HOLE + 409600 * 6 }, { DATA, 1024 },
539 { HOLE, MIN_HOLE + 409600 * 7 }, { DATA, 1024 },
540 { HOLE, MIN_HOLE + 409600 * 8 }, { DATA, 1024 },
541 { HOLE, MIN_HOLE + 409600 * 9}, { DATA, 1024 },/* 10 */
542 { HOLE, MIN_HOLE }, { DATA, 1024 * 1 },
543 { HOLE, MIN_HOLE + 409600 * 1 }, { DATA, 1024 * 2 },
544 { HOLE, MIN_HOLE + 409600 * 2 }, { DATA, 1024 * 3 },
545 { HOLE, MIN_HOLE + 409600 * 3 }, { DATA, 1024 * 4 },
546 { HOLE, MIN_HOLE + 409600 * 4 }, { DATA, 1024 * 5 },
547 { HOLE, MIN_HOLE + 409600 * 5 }, { DATA, 1024 * 6 },
548 { HOLE, MIN_HOLE + 409600 * 6 }, { DATA, 1024 * 7 },
549 { HOLE, MIN_HOLE + 409600 * 7 }, { DATA, 1024 * 8 },
550 { HOLE, MIN_HOLE + 409600 * 8 }, { DATA, 1024 * 9 },
551 { HOLE, MIN_HOLE + 409600 * 9}, { DATA, 1024 * 10},/* 20 */
552 { END, 0 }
553 };
554 const struct sparse sparse_file3[] = {
555 /* This hole size is too small to create a sparse file */
556 { HOLE, 1 }, { DATA, 10240 },
557 { HOLE, 1 }, { DATA, 10240 },
558 { HOLE, 1 }, { DATA, 10240 },
559 { END, 0 }
560 };
561 const struct sparse sparse_file4[] = {
562 { DATA, 4096 }, { HOLE, 0xc0000000 },
563 /* This hole overflows the offset if stored in 32 bits. */
564 { DATA, 4096 }, { HOLE, 0x50000000 },
565 { END, 0 }
566 };
567
568 /*
569 * Test for the case that sparse data indicates just the whole file
570 * data.
571 */
572 test_sparse_whole_file_data();
573
574 skip_sparse_tests = getenv("SKIP_TEST_SPARSE");
575 if (skip_sparse_tests != NULL) {
576 skipping("Skipping sparse tests due to SKIP_TEST_SPARSE "
577 "environment variable");
578 return;
579 }
580
581 /* Check if the filesystem where CWD on can
582 * report the number of the holes of a sparse file. */
583 #if defined(PATH_MAX) && !defined(__GLIBC__)
584 cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */
585 #else
586 cwd = getcwd(NULL, 0);
587 #endif
588 if (!assert(cwd != NULL))
589 return;
590 if (!is_sparse_supported(cwd)) {
591 free(cwd);
592 skipping("This filesystem or platform do not support "
593 "the reporting of the holes of a sparse file through "
594 "API such as lseek(HOLE)");
595 return;
596 }
597
598 /*
599 * Get sparse data through directory traversals.
600 */
601 assert((a = archive_read_disk_new()) != NULL);
602
603 verify_sparse_file(a, "file0", sparse_file0, 4);
604 verify_sparse_file(a, "file1", sparse_file1, 3);
605 verify_sparse_file(a, "file2", sparse_file2, 20);
606 /* Encoded non sparse; expect a data block but no sparse entries. */
607 verify_sparse_file(a, "file3", sparse_file3, 0);
608 verify_sparse_file(a, "file4", sparse_file4, 2);
609
610 assertEqualInt(ARCHIVE_OK, archive_read_free(a));
611
612 /*
613 * Get sparse data through archive_read_disk_entry_from_file().
614 */
615 assert((a = archive_read_disk_new()) != NULL);
616
617 verify_sparse_file2(a, "file0", sparse_file0, 5, 0);
618 verify_sparse_file2(a, "file0", sparse_file0, 5, 1);
619
620 assertEqualInt(ARCHIVE_OK, archive_read_free(a));
621
622 /*
623 * Test that setting ARCHIVE_READDISK_NO_SPARSE
624 * creates no sparse entries.
625 */
626 assert((a = archive_read_disk_new()) != NULL);
627
628 assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_behavior(a,
629 ARCHIVE_READDISK_NO_SPARSE));
630
631 verify_sparse_file(a, "file0", sparse_file0, 0);
632 verify_sparse_file(a, "file1", sparse_file1, 0);
633 verify_sparse_file(a, "file2", sparse_file2, 0);
634 verify_sparse_file(a, "file3", sparse_file3, 0);
635 verify_sparse_file(a, "file4", sparse_file4, 0);
636
637 assertEqualInt(ARCHIVE_OK, archive_read_free(a));
638
639 assert((a = archive_read_disk_new()) != NULL);
640
641 assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_behavior(a,
642 ARCHIVE_READDISK_NO_SPARSE));
643
644 verify_sparse_file2(a, "file0", sparse_file0, 0, 0);
645 verify_sparse_file2(a, "file0", sparse_file0, 0, 1);
646
647 assertEqualInt(ARCHIVE_OK, archive_read_free(a));
648 free(cwd);
649 }
650
DEFINE_TEST(test_fully_sparse_files)651 DEFINE_TEST(test_fully_sparse_files)
652 {
653 char *cwd;
654 struct archive *a;
655 const char *skip_sparse_tests;
656
657 const struct sparse sparse_file[] = {
658 { HOLE, MIN_HOLE }, { END, 0 }
659 };
660
661 skip_sparse_tests = getenv("SKIP_TEST_SPARSE");
662 if (skip_sparse_tests != NULL) {
663 skipping("Skipping sparse tests due to SKIP_TEST_SPARSE "
664 "environment variable");
665 return;
666 }
667
668 /* Check if the filesystem where CWD on can
669 * report the number of the holes of a sparse file. */
670 #if defined(PATH_MAX) && !defined(__GLIBC__)
671 cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */
672 #else
673 cwd = getcwd(NULL, 0);
674 #endif
675 if (!assert(cwd != NULL))
676 return;
677 if (!is_sparse_supported(cwd)) {
678 free(cwd);
679 skipping("This filesystem or platform do not support "
680 "the reporting of the holes of a sparse file through "
681 "API such as lseek(HOLE)");
682 return;
683 }
684
685 assert((a = archive_read_disk_new()) != NULL);
686
687 /* Fully sparse files are encoded with a zero-length "data" block. */
688 verify_sparse_file(a, "file0", sparse_file, 1);
689
690 assertEqualInt(ARCHIVE_OK, archive_read_free(a));
691 free(cwd);
692 }
693