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