test_sparse_basic.c revision 311041
1231200Smm/*-
2238856Smm * Copyright (c) 2010-2012 Michihiro NAKAJIMA
3231200Smm * All rights reserved.
4231200Smm *
5231200Smm * Redistribution and use in source and binary forms, with or without
6231200Smm * modification, are permitted provided that the following conditions
7231200Smm * are met:
8231200Smm * 1. Redistributions of source code must retain the above copyright
9231200Smm *    notice, this list of conditions and the following disclaimer.
10231200Smm * 2. Redistributions in binary form must reproduce the above copyright
11231200Smm *    notice, this list of conditions and the following disclaimer in the
12231200Smm *    documentation and/or other materials provided with the distribution.
13231200Smm *
14231200Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15231200Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16231200Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17231200Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18231200Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19231200Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20231200Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21231200Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22231200Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23231200Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24231200Smm */
25231200Smm#include "test.h"
26231200Smm__FBSDID("$FreeBSD$");
27231200Smm
28231200Smm#ifdef HAVE_SYS_IOCTL_H
29231200Smm#include <sys/ioctl.h>
30231200Smm#endif
31231200Smm#ifdef HAVE_SYS_PARAM_H
32231200Smm#include <sys/param.h>
33231200Smm#endif
34231200Smm#ifdef HAVE_FCNTL_H
35231200Smm#include <fcntl.h>
36231200Smm#endif
37231200Smm#ifdef HAVE_LIMITS_H
38231200Smm#include <limits.h>
39231200Smm#endif
40231200Smm#ifdef HAVE_UNISTD_H
41231200Smm#include <unistd.h>
42231200Smm#endif
43299529Smm#ifdef HAVE_LINUX_TYPES_H
44299529Smm#include <linux/types.h>
45299529Smm#endif
46231200Smm#ifdef HAVE_LINUX_FIEMAP_H
47231200Smm#include <linux/fiemap.h>
48231200Smm#endif
49231200Smm#ifdef HAVE_LINUX_FS_H
50231200Smm#include <linux/fs.h>
51231200Smm#endif
52231200Smm
53299529Smm/* The logic to compare sparse file data read from disk with the
54299529Smm * specification is a little involved.  Set to 1 to have the progress
55299529Smm * dumped. */
56299529Smm#define DEBUG 0
57299529Smm
58231200Smm/*
59231200Smm * NOTE: On FreeBSD and Solaris, this test needs ZFS.
60231200Smm * You may should perfom this test as
61231200Smm * 'TMPDIR=<a directory on the ZFS> libarchive_test'.
62231200Smm */
63231200Smm
64231200Smmstruct sparse {
65231200Smm	enum { DATA, HOLE, END } type;
66231200Smm	size_t	size;
67231200Smm};
68231200Smm
69231200Smmstatic void create_sparse_file(const char *, const struct sparse *);
70231200Smm
71231200Smm#if defined(_WIN32) && !defined(__CYGWIN__)
72231200Smm#include <winioctl.h>
73231200Smm/*
74231200Smm * Create a sparse file on Windows.
75231200Smm */
76231200Smm
77231200Smm#if !defined(PATH_MAX)
78231200Smm#define	PATH_MAX	MAX_PATH
79231200Smm#endif
80231200Smm#if !defined(__BORLANDC__)
81231200Smm#define getcwd _getcwd
82231200Smm#endif
83231200Smm
84231200Smmstatic int
85231200Smmis_sparse_supported(const char *path)
86231200Smm{
87231200Smm	char root[MAX_PATH+1];
88231200Smm	char vol[MAX_PATH+1];
89231200Smm	char sys[MAX_PATH+1];
90231200Smm	DWORD flags;
91231200Smm	BOOL r;
92231200Smm
93231200Smm	strncpy(root, path, sizeof(root)-1);
94231200Smm	if (((root[0] >= 'c' && root[0] <= 'z') ||
95231200Smm	    (root[0] >= 'C' && root[0] <= 'Z')) &&
96231200Smm		root[1] == ':' &&
97231200Smm	    (root[2] == '\\' || root[2] == '/'))
98231200Smm		root[3] = '\0';
99231200Smm	else
100231200Smm		return (0);
101231200Smm	assertEqualInt((r = GetVolumeInformation(root, vol,
102231200Smm	    sizeof(vol), NULL, NULL, &flags, sys, sizeof(sys))), 1);
103231200Smm	return (r != 0 && (flags & FILE_SUPPORTS_SPARSE_FILES) != 0);
104231200Smm}
105231200Smm
106231200Smmstatic void
107231200Smmcreate_sparse_file(const char *path, const struct sparse *s)
108231200Smm{
109231200Smm	char buff[1024];
110231200Smm	HANDLE handle;
111231200Smm	DWORD dmy;
112231200Smm
113231200Smm	memset(buff, ' ', sizeof(buff));
114231200Smm
115231200Smm	handle = CreateFileA(path, GENERIC_WRITE, 0,
116231200Smm	    NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL,
117231200Smm	    NULL);
118231200Smm	assert(handle != INVALID_HANDLE_VALUE);
119231200Smm	assert(DeviceIoControl(handle, FSCTL_SET_SPARSE, NULL, 0,
120231200Smm	    NULL, 0, &dmy, NULL) != 0);
121231200Smm	while (s->type != END) {
122231200Smm		if (s->type == HOLE) {
123231200Smm			LARGE_INTEGER distance;
124231200Smm
125231200Smm			distance.QuadPart = s->size;
126231200Smm			assert(SetFilePointerEx(handle, distance,
127231200Smm			    NULL, FILE_CURRENT) != 0);
128231200Smm		} else {
129248616Smm			DWORD w, wr;
130248616Smm			size_t size;
131231200Smm
132231200Smm			size = s->size;
133231200Smm			while (size) {
134231200Smm				if (size > sizeof(buff))
135231200Smm					w = sizeof(buff);
136231200Smm				else
137248616Smm					w = (DWORD)size;
138231200Smm				assert(WriteFile(handle, buff, w, &wr, NULL) != 0);
139231200Smm				size -= wr;
140231200Smm			}
141231200Smm		}
142231200Smm		s++;
143231200Smm	}
144231200Smm	assertEqualInt(CloseHandle(handle), 1);
145231200Smm}
146231200Smm
147231200Smm#else
148231200Smm
149311041Smm#if defined(HAVE_LINUX_FIEMAP_H)
150231200Smm/*
151231200Smm * FIEMAP, which can detect 'hole' of a sparse file, has
152231200Smm * been supported from 2.6.28
153231200Smm */
154231200Smm
155231200Smmstatic int
156311041Smmis_sparse_supported_fiemap(const char *path)
157231200Smm{
158231200Smm	const struct sparse sparse_file[] = {
159231200Smm 		/* This hole size is too small to create a sparse
160231200Smm		 * files for almost filesystem. */
161231200Smm		{ HOLE,	 1024 }, { DATA, 10240 },
162231200Smm		{ END,	0 }
163231200Smm	};
164231200Smm	int fd, r;
165231200Smm	struct fiemap *fm;
166231200Smm	char buff[1024];
167231200Smm	const char *testfile = "can_sparse";
168231200Smm
169232153Smm	(void)path; /* UNUSED */
170248616Smm	memset(buff, 0, sizeof(buff));
171231200Smm	create_sparse_file(testfile, sparse_file);
172231200Smm	fd = open(testfile,  O_RDWR);
173231200Smm	if (fd < 0)
174231200Smm		return (0);
175231200Smm	fm = (struct fiemap *)buff;
176231200Smm	fm->fm_start = 0;
177231200Smm	fm->fm_length = ~0ULL;;
178231200Smm	fm->fm_flags = FIEMAP_FLAG_SYNC;
179231200Smm	fm->fm_extent_count = (sizeof(buff) - sizeof(*fm))/
180231200Smm		sizeof(struct fiemap_extent);
181231200Smm	r = ioctl(fd, FS_IOC_FIEMAP, fm);
182231200Smm	close(fd);
183231200Smm	unlink(testfile);
184238856Smm	return (r >= 0);
185231200Smm}
186231200Smm
187311041Smm#if !defined(SEEK_HOLE) || !defined(SEEK_DATA)
188311041Smmstatic int
189311041Smmis_sparse_supported(const char *path)
190311041Smm{
191311041Smm	return is_sparse_supported_fiemap(path);
192311041Smm}
193311041Smm#endif
194311041Smm#endif
195231200Smm
196311041Smm#if defined(_PC_MIN_HOLE_SIZE)
197311041Smm
198231200Smm/*
199311041Smm * FreeBSD and Solaris can detect 'hole' of a sparse file
200311041Smm * through lseek(HOLE) on ZFS. (UFS does not support yet)
201311041Smm */
202311041Smm
203311041Smmstatic int
204311041Smmis_sparse_supported(const char *path)
205311041Smm{
206311041Smm	return (pathconf(path, _PC_MIN_HOLE_SIZE) > 0);
207311041Smm}
208311041Smm
209311041Smm#elif defined(SEEK_HOLE) && defined(SEEK_DATA)
210311041Smm
211311041Smmstatic int
212311041Smmis_sparse_supported(const char *path)
213311041Smm{
214311041Smm	const struct sparse sparse_file[] = {
215311041Smm 		/* This hole size is too small to create a sparse
216311041Smm		 * files for almost filesystem. */
217311041Smm		{ HOLE,	 1024 }, { DATA, 10240 },
218311041Smm		{ END,	0 }
219311041Smm	};
220311041Smm	int fd, r;
221311041Smm	const char *testfile = "can_sparse";
222311041Smm
223311041Smm	(void)path; /* UNUSED */
224311041Smm	create_sparse_file(testfile, sparse_file);
225311041Smm	fd = open(testfile,  O_RDWR);
226311041Smm	if (fd < 0)
227311041Smm		return (0);
228311041Smm	r = lseek(fd, 0, SEEK_HOLE);
229311041Smm	close(fd);
230311041Smm	unlink(testfile);
231311041Smm#if defined(HAVE_LINUX_FIEMAP_H)
232311041Smm	if (r < 0)
233311041Smm		return (is_sparse_supported_fiemap(path));
234311041Smm#endif
235311041Smm	return (r >= 0);
236311041Smm}
237311041Smm
238311041Smm#elif !defined(HAVE_LINUX_FIEMAP_H)
239311041Smm
240311041Smm/*
241231200Smm * Other system may do not have the API such as lseek(HOLE),
242231200Smm * which detect 'hole' of a sparse file.
243231200Smm */
244231200Smm
245231200Smmstatic int
246231200Smmis_sparse_supported(const char *path)
247231200Smm{
248232153Smm	(void)path; /* UNUSED */
249231200Smm	return (0);
250231200Smm}
251231200Smm
252231200Smm#endif
253231200Smm
254231200Smm/*
255231200Smm * Create a sparse file on POSIX like system.
256231200Smm */
257231200Smm
258231200Smmstatic void
259231200Smmcreate_sparse_file(const char *path, const struct sparse *s)
260231200Smm{
261231200Smm	char buff[1024];
262231200Smm	int fd;
263299529Smm	size_t total_size = 0;
264299529Smm	const struct sparse *cur = s;
265231200Smm
266231200Smm	memset(buff, ' ', sizeof(buff));
267231200Smm	assert((fd = open(path, O_CREAT | O_WRONLY, 0600)) != -1);
268299529Smm
269299529Smm	/* Handle holes at the end by extending the file */
270299529Smm	while (cur->type != END) {
271299529Smm		total_size += cur->size;
272299529Smm		++cur;
273299529Smm	}
274299529Smm	assert(ftruncate(fd, total_size) != -1);
275299529Smm
276231200Smm	while (s->type != END) {
277231200Smm		if (s->type == HOLE) {
278231200Smm			assert(lseek(fd, s->size, SEEK_CUR) != (off_t)-1);
279231200Smm		} else {
280231200Smm			size_t w, size;
281231200Smm
282231200Smm			size = s->size;
283231200Smm			while (size) {
284231200Smm				if (size > sizeof(buff))
285231200Smm					w = sizeof(buff);
286231200Smm				else
287231200Smm					w = size;
288231200Smm				assert(write(fd, buff, w) != (ssize_t)-1);
289231200Smm				size -= w;
290231200Smm			}
291231200Smm		}
292231200Smm		s++;
293231200Smm	}
294231200Smm	close(fd);
295231200Smm}
296231200Smm
297231200Smm#endif
298231200Smm
299231200Smm/*
300231200Smm * Sparse test with directory traversals.
301231200Smm */
302231200Smmstatic void
303231200Smmverify_sparse_file(struct archive *a, const char *path,
304299529Smm    const struct sparse *sparse, int expected_holes)
305231200Smm{
306231200Smm	struct archive_entry *ae;
307231200Smm	const void *buff;
308231200Smm	size_t bytes_read;
309299529Smm	int64_t offset, expected_offset, last_offset;
310299529Smm	int holes_seen = 0;
311231200Smm
312231200Smm	create_sparse_file(path, sparse);
313231200Smm	assert((ae = archive_entry_new()) != NULL);
314231200Smm	assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, path));
315231200Smm	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae));
316299529Smm
317299529Smm	expected_offset = 0;
318299529Smm	last_offset = 0;
319231200Smm	while (ARCHIVE_OK == archive_read_data_block(a, &buff, &bytes_read,
320231200Smm	    &offset)) {
321299529Smm		const char *start = buff;
322299529Smm#if DEBUG
323299529Smm		fprintf(stderr, "%s: bytes_read=%d offset=%d\n", path, (int)bytes_read, (int)offset);
324299529Smm#endif
325299529Smm		if (offset > last_offset) {
326299529Smm			++holes_seen;
327231200Smm		}
328299529Smm		/* Blocks entirely before the data we just read. */
329299529Smm		while (expected_offset + (int64_t)sparse->size < offset) {
330299529Smm#if DEBUG
331299529Smm			fprintf(stderr, "    skipping expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
332299529Smm#endif
333299529Smm			/* Must be holes. */
334299529Smm			assert(sparse->type == HOLE);
335299529Smm			expected_offset += sparse->size;
336299529Smm			++sparse;
337299529Smm		}
338299529Smm		/* Block that overlaps beginning of data */
339299529Smm		if (expected_offset < offset
340299529Smm		    && expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) {
341299529Smm			const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size;
342299529Smm#if DEBUG
343299529Smm			fprintf(stderr, "    overlapping hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
344299529Smm#endif
345299529Smm			/* Must be a hole, overlap must be filled with '\0' */
346299529Smm			if (assert(sparse->type == HOLE)) {
347299529Smm				assertMemoryFilledWith(start, end - start, '\0');
348299529Smm			}
349299529Smm			start = end;
350299529Smm			expected_offset += sparse->size;
351299529Smm			++sparse;
352299529Smm		}
353299529Smm		/* Blocks completely contained in data we just read. */
354299529Smm		while (expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) {
355299529Smm			const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size;
356299529Smm			if (sparse->type == HOLE) {
357299529Smm#if DEBUG
358299529Smm				fprintf(stderr, "    contained hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
359299529Smm#endif
360299529Smm
361299529Smm				/* verify data corresponding to hole is '\0' */
362299529Smm				if (end > (const char *)buff + bytes_read) {
363299529Smm					end = (const char *)buff + bytes_read;
364299529Smm				}
365299529Smm				assertMemoryFilledWith(start, end - start, '\0');
366299529Smm				start = end;
367299529Smm				expected_offset += sparse->size;
368299529Smm				++sparse;
369299529Smm			} else if (sparse->type == DATA) {
370299529Smm#if DEBUG
371299529Smm				fprintf(stderr, "    contained data expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
372299529Smm#endif
373299529Smm				/* verify data corresponding to hole is ' ' */
374299529Smm				if (assert(expected_offset + sparse->size <= offset + bytes_read)) {
375299529Smm					assert(start == (const char *)buff + (size_t)(expected_offset - offset));
376299529Smm					assertMemoryFilledWith(start, end - start, ' ');
377299529Smm				}
378299529Smm				start = end;
379299529Smm				expected_offset += sparse->size;
380299529Smm				++sparse;
381299529Smm			} else {
382299529Smm				break;
383299529Smm			}
384299529Smm		}
385299529Smm		/* Block that overlaps end of data */
386299529Smm		if (expected_offset < offset + (int64_t)bytes_read) {
387299529Smm			const char *end = (const char *)buff + bytes_read;
388299529Smm#if DEBUG
389299529Smm			fprintf(stderr, "    trailing overlap expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
390299529Smm#endif
391299529Smm			/* Must be a hole, overlap must be filled with '\0' */
392299529Smm			if (assert(sparse->type == HOLE)) {
393299529Smm				assertMemoryFilledWith(start, end - start, '\0');
394299529Smm			}
395299529Smm		}
396299529Smm		last_offset = offset + bytes_read;
397231200Smm	}
398299529Smm	/* Count a hole at EOF? */
399299529Smm	if (last_offset < archive_entry_size(ae)) {
400299529Smm		++holes_seen;
401299529Smm	}
402231200Smm
403299529Smm	/* Verify blocks after last read */
404299529Smm	while (sparse->type == HOLE) {
405299529Smm		expected_offset += sparse->size;
406299529Smm		++sparse;
407299529Smm	}
408299529Smm	assert(sparse->type == END);
409299529Smm	assertEqualInt(expected_offset, archive_entry_size(ae));
410299529Smm
411299529Smm	assertEqualInt(holes_seen, expected_holes);
412299529Smm
413231200Smm	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
414231200Smm	archive_entry_free(ae);
415231200Smm}
416231200Smm
417231200Smm#if defined(_WIN32) && !defined(__CYGWIN__)
418231200Smm#define	close		_close
419231200Smm#define	open		_open
420231200Smm#endif
421231200Smm
422231200Smm/*
423231200Smm * Sparse test without directory traversals.
424231200Smm */
425231200Smmstatic void
426231200Smmverify_sparse_file2(struct archive *a, const char *path,
427231200Smm    const struct sparse *sparse, int blocks, int preopen)
428231200Smm{
429231200Smm	struct archive_entry *ae;
430231200Smm	int fd;
431231200Smm
432231200Smm	(void)sparse; /* UNUSED */
433231200Smm	assert((ae = archive_entry_new()) != NULL);
434231200Smm	archive_entry_set_pathname(ae, path);
435231200Smm	if (preopen)
436231200Smm		fd = open(path, O_RDONLY | O_BINARY);
437231200Smm	else
438231200Smm		fd = -1;
439231200Smm	assertEqualIntA(a, ARCHIVE_OK,
440231200Smm	    archive_read_disk_entry_from_file(a, ae, fd, NULL));
441231200Smm	if (fd >= 0)
442231200Smm		close(fd);
443231200Smm	/* Verify the number of holes only, not its offset nor its
444231200Smm	 * length because those alignments are deeply dependence on
445231200Smm	 * its filesystem. */
446231200Smm	assertEqualInt(blocks, archive_entry_sparse_count(ae));
447231200Smm	archive_entry_free(ae);
448231200Smm}
449231200Smm
450231200Smmstatic void
451231200Smmtest_sparse_whole_file_data()
452231200Smm{
453231200Smm	struct archive_entry *ae;
454231200Smm	int64_t offset;
455231200Smm	int i;
456231200Smm
457231200Smm	assert((ae = archive_entry_new()) != NULL);
458231200Smm	archive_entry_set_size(ae, 1024*10);
459231200Smm
460231200Smm	/*
461231200Smm	 * Add sparse block data up to the file size.
462231200Smm	 */
463231200Smm	offset = 0;
464231200Smm	for (i = 0; i < 10; i++) {
465231200Smm		archive_entry_sparse_add_entry(ae, offset, 1024);
466231200Smm		offset += 1024;
467231200Smm	}
468231200Smm
469231200Smm	failure("There should be no sparse");
470231200Smm	assertEqualInt(0, archive_entry_sparse_count(ae));
471231200Smm	archive_entry_free(ae);
472231200Smm}
473231200Smm
474231200SmmDEFINE_TEST(test_sparse_basic)
475231200Smm{
476231200Smm	char *cwd;
477231200Smm	struct archive *a;
478231200Smm	/*
479231200Smm	 * The alignment of the hole of sparse files deeply depends
480231200Smm	 * on filesystem. In my experience, sparse_file2 test with
481231200Smm	 * 204800 bytes hole size did not pass on ZFS and the result
482231200Smm	 * of that test seemed the size was too small, thus you should
483231200Smm	 * keep a hole size more than 409600 bytes to pass this test
484231200Smm	 * on all platform.
485231200Smm	 */
486231200Smm	const struct sparse sparse_file0[] = {
487231200Smm		{ DATA,	 1024 }, { HOLE,   2048000 },
488231200Smm		{ DATA,	 2048 }, { HOLE,   2048000 },
489231200Smm		{ DATA,	 4096 }, { HOLE,  20480000 },
490231200Smm		{ DATA,	 8192 }, { HOLE, 204800000 },
491231200Smm		{ DATA,     1 }, { END,	0 }
492231200Smm	};
493231200Smm	const struct sparse sparse_file1[] = {
494231200Smm		{ HOLE,	409600 }, { DATA, 1 },
495231200Smm		{ HOLE,	409600 }, { DATA, 1 },
496231200Smm		{ HOLE,	409600 }, { END,  0 }
497231200Smm	};
498231200Smm	const struct sparse sparse_file2[] = {
499231200Smm		{ HOLE,	409600 * 1 }, { DATA, 1024 },
500231200Smm		{ HOLE,	409600 * 2 }, { DATA, 1024 },
501231200Smm		{ HOLE,	409600 * 3 }, { DATA, 1024 },
502231200Smm		{ HOLE,	409600 * 4 }, { DATA, 1024 },
503231200Smm		{ HOLE,	409600 * 5 }, { DATA, 1024 },
504231200Smm		{ HOLE,	409600 * 6 }, { DATA, 1024 },
505231200Smm		{ HOLE,	409600 * 7 }, { DATA, 1024 },
506231200Smm		{ HOLE,	409600 * 8 }, { DATA, 1024 },
507231200Smm		{ HOLE,	409600 * 9 }, { DATA, 1024 },
508231200Smm		{ HOLE,	409600 * 10}, { DATA, 1024 },/* 10 */
509231200Smm		{ HOLE,	409600 * 1 }, { DATA, 1024 * 1 },
510231200Smm		{ HOLE,	409600 * 2 }, { DATA, 1024 * 2 },
511231200Smm		{ HOLE,	409600 * 3 }, { DATA, 1024 * 3 },
512231200Smm		{ HOLE,	409600 * 4 }, { DATA, 1024 * 4 },
513231200Smm		{ HOLE,	409600 * 5 }, { DATA, 1024 * 5 },
514231200Smm		{ HOLE,	409600 * 6 }, { DATA, 1024 * 6 },
515231200Smm		{ HOLE,	409600 * 7 }, { DATA, 1024 * 7 },
516231200Smm		{ HOLE,	409600 * 8 }, { DATA, 1024 * 8 },
517231200Smm		{ HOLE,	409600 * 9 }, { DATA, 1024 * 9 },
518231200Smm		{ HOLE,	409600 * 10}, { DATA, 1024 * 10},/* 20 */
519231200Smm		{ END,	0 }
520231200Smm	};
521231200Smm	const struct sparse sparse_file3[] = {
522299529Smm 		/* This hole size is too small to create a sparse file */
523299529Smm		{ HOLE,	 1 }, { DATA, 10240 },
524299529Smm		{ HOLE,	 1 }, { DATA, 10240 },
525299529Smm		{ HOLE,	 1 }, { DATA, 10240 },
526231200Smm		{ END,	0 }
527231200Smm	};
528231200Smm
529231200Smm	/*
530231200Smm	 * Test for the case that sparse data indicates just the whole file
531231200Smm	 * data.
532231200Smm	 */
533231200Smm	test_sparse_whole_file_data();
534231200Smm
535231200Smm	/* Check if the filesystem where CWD on can
536231200Smm	 * report the number of the holes of a sparse file. */
537231200Smm#ifdef PATH_MAX
538231200Smm	cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */
539231200Smm#else
540231200Smm	cwd = getcwd(NULL, 0);
541231200Smm#endif
542231200Smm	if (!assert(cwd != NULL))
543231200Smm		return;
544231200Smm	if (!is_sparse_supported(cwd)) {
545231200Smm		free(cwd);
546231200Smm		skipping("This filesystem or platform do not support "
547231200Smm		    "the reporting of the holes of a sparse file through "
548231200Smm		    "API such as lseek(HOLE)");
549231200Smm		return;
550231200Smm	}
551231200Smm
552231200Smm	/*
553231200Smm	 * Get sparse data through directory traversals.
554231200Smm	 */
555231200Smm	assert((a = archive_read_disk_new()) != NULL);
556231200Smm
557299529Smm	verify_sparse_file(a, "file0", sparse_file0, 4);
558299529Smm	verify_sparse_file(a, "file1", sparse_file1, 3);
559231200Smm	verify_sparse_file(a, "file2", sparse_file2, 20);
560299529Smm	/* Encoded non sparse; expect a data block but no sparse entries. */
561231200Smm	verify_sparse_file(a, "file3", sparse_file3, 0);
562231200Smm
563231200Smm	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
564231200Smm
565231200Smm	/*
566231200Smm	 * Get sparse data through archive_read_disk_entry_from_file().
567231200Smm	 */
568231200Smm	assert((a = archive_read_disk_new()) != NULL);
569231200Smm
570231200Smm	verify_sparse_file2(a, "file0", sparse_file0, 5, 0);
571231200Smm	verify_sparse_file2(a, "file0", sparse_file0, 5, 1);
572231200Smm
573231200Smm	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
574231200Smm	free(cwd);
575231200Smm}
576299529Smm
577299529SmmDEFINE_TEST(test_fully_sparse_files)
578299529Smm{
579299529Smm	char *cwd;
580299529Smm	struct archive *a;
581299529Smm
582299529Smm	const struct sparse sparse_file[] = {
583299529Smm		{ HOLE, 409600 }, { END, 0 }
584299529Smm	};
585299529Smm	/* Check if the filesystem where CWD on can
586299529Smm	 * report the number of the holes of a sparse file. */
587299529Smm#ifdef PATH_MAX
588299529Smm	cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */
589299529Smm#else
590299529Smm	cwd = getcwd(NULL, 0);
591299529Smm#endif
592299529Smm	if (!assert(cwd != NULL))
593299529Smm		return;
594299529Smm	if (!is_sparse_supported(cwd)) {
595299529Smm		free(cwd);
596299529Smm		skipping("This filesystem or platform do not support "
597299529Smm		    "the reporting of the holes of a sparse file through "
598299529Smm		    "API such as lseek(HOLE)");
599299529Smm		return;
600299529Smm	}
601299529Smm
602299529Smm	assert((a = archive_read_disk_new()) != NULL);
603299529Smm
604299529Smm	/* Fully sparse files are encoded with a zero-length "data" block. */
605299529Smm	verify_sparse_file(a, "file0", sparse_file, 1);
606299529Smm
607299529Smm	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
608299529Smm	free(cwd);
609299529Smm}
610