16c95142eSMartin Matuska /*-
2fd082e96SMartin Matuska  * Copyright (c) 2010-2012 Michihiro NAKAJIMA
36c95142eSMartin Matuska  * All rights reserved.
46c95142eSMartin Matuska  *
56c95142eSMartin Matuska  * Redistribution and use in source and binary forms, with or without
66c95142eSMartin Matuska  * modification, are permitted provided that the following conditions
76c95142eSMartin Matuska  * are met:
86c95142eSMartin Matuska  * 1. Redistributions of source code must retain the above copyright
96c95142eSMartin Matuska  *    notice, this list of conditions and the following disclaimer.
106c95142eSMartin Matuska  * 2. Redistributions in binary form must reproduce the above copyright
116c95142eSMartin Matuska  *    notice, this list of conditions and the following disclaimer in the
126c95142eSMartin Matuska  *    documentation and/or other materials provided with the distribution.
136c95142eSMartin Matuska  *
146c95142eSMartin Matuska  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
156c95142eSMartin Matuska  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
166c95142eSMartin Matuska  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
176c95142eSMartin Matuska  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
186c95142eSMartin Matuska  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
196c95142eSMartin Matuska  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
206c95142eSMartin Matuska  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
216c95142eSMartin Matuska  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
226c95142eSMartin Matuska  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
236c95142eSMartin Matuska  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
246c95142eSMartin Matuska  */
256c95142eSMartin Matuska #include "test.h"
266c95142eSMartin Matuska __FBSDID("$FreeBSD$");
276c95142eSMartin Matuska 
286c95142eSMartin Matuska #ifdef HAVE_SYS_IOCTL_H
296c95142eSMartin Matuska #include <sys/ioctl.h>
306c95142eSMartin Matuska #endif
316c95142eSMartin Matuska #ifdef HAVE_SYS_PARAM_H
326c95142eSMartin Matuska #include <sys/param.h>
336c95142eSMartin Matuska #endif
346c95142eSMartin Matuska #ifdef HAVE_FCNTL_H
356c95142eSMartin Matuska #include <fcntl.h>
366c95142eSMartin Matuska #endif
376c95142eSMartin Matuska #ifdef HAVE_LIMITS_H
386c95142eSMartin Matuska #include <limits.h>
396c95142eSMartin Matuska #endif
406c95142eSMartin Matuska #ifdef HAVE_UNISTD_H
416c95142eSMartin Matuska #include <unistd.h>
426c95142eSMartin Matuska #endif
43cdf63a70SMartin Matuska #ifdef HAVE_LINUX_TYPES_H
44cdf63a70SMartin Matuska #include <linux/types.h>
45cdf63a70SMartin Matuska #endif
466c95142eSMartin Matuska #ifdef HAVE_LINUX_FIEMAP_H
476c95142eSMartin Matuska #include <linux/fiemap.h>
486c95142eSMartin Matuska #endif
496c95142eSMartin Matuska #ifdef HAVE_LINUX_FS_H
506c95142eSMartin Matuska #include <linux/fs.h>
516c95142eSMartin Matuska #endif
526c95142eSMartin Matuska 
53cdf63a70SMartin Matuska /* The logic to compare sparse file data read from disk with the
54cdf63a70SMartin Matuska  * specification is a little involved.  Set to 1 to have the progress
55cdf63a70SMartin Matuska  * dumped. */
56cdf63a70SMartin Matuska #define DEBUG 0
57cdf63a70SMartin Matuska 
586c95142eSMartin Matuska /*
596c95142eSMartin Matuska  * NOTE: On FreeBSD and Solaris, this test needs ZFS.
602dbf8c4aSMartin Matuska  * You may perform this test as
616c95142eSMartin Matuska  * 'TMPDIR=<a directory on the ZFS> libarchive_test'.
626c95142eSMartin Matuska  */
636c95142eSMartin Matuska 
646c95142eSMartin Matuska struct sparse {
656c95142eSMartin Matuska 	enum { DATA, HOLE, END } type;
666c95142eSMartin Matuska 	size_t	size;
676c95142eSMartin Matuska };
686c95142eSMartin Matuska 
696c95142eSMartin Matuska static void create_sparse_file(const char *, const struct sparse *);
706c95142eSMartin Matuska 
71*590d65eeSMartin Matuska #if defined(__APPLE__)
72*590d65eeSMartin Matuska /* On APFS holes need to be at least 4096x4097 bytes */
73*590d65eeSMartin Matuska #define MIN_HOLE 16781312
74*590d65eeSMartin Matuska #else
75*590d65eeSMartin Matuska /* Elsewhere we work with 4096*10 bytes */
76*590d65eeSMartin Matuska #define MIN_HOLE 409600
77*590d65eeSMartin Matuska #endif
78*590d65eeSMartin Matuska 
796c95142eSMartin Matuska #if defined(_WIN32) && !defined(__CYGWIN__)
806c95142eSMartin Matuska #include <winioctl.h>
816c95142eSMartin Matuska /*
826c95142eSMartin Matuska  * Create a sparse file on Windows.
836c95142eSMartin Matuska  */
846c95142eSMartin Matuska 
856c95142eSMartin Matuska #if !defined(PATH_MAX)
866c95142eSMartin Matuska #define	PATH_MAX	MAX_PATH
876c95142eSMartin Matuska #endif
886c95142eSMartin Matuska #if !defined(__BORLANDC__)
896c95142eSMartin Matuska #define getcwd _getcwd
906c95142eSMartin Matuska #endif
916c95142eSMartin Matuska 
926c95142eSMartin Matuska static int
is_sparse_supported(const char * path)936c95142eSMartin Matuska is_sparse_supported(const char *path)
946c95142eSMartin Matuska {
956c95142eSMartin Matuska 	char root[MAX_PATH+1];
966c95142eSMartin Matuska 	char vol[MAX_PATH+1];
976c95142eSMartin Matuska 	char sys[MAX_PATH+1];
986c95142eSMartin Matuska 	DWORD flags;
996c95142eSMartin Matuska 	BOOL r;
1006c95142eSMartin Matuska 
1016c95142eSMartin Matuska 	strncpy(root, path, sizeof(root)-1);
1026c95142eSMartin Matuska 	if (((root[0] >= 'c' && root[0] <= 'z') ||
1036c95142eSMartin Matuska 	    (root[0] >= 'C' && root[0] <= 'Z')) &&
1046c95142eSMartin Matuska 		root[1] == ':' &&
1056c95142eSMartin Matuska 	    (root[2] == '\\' || root[2] == '/'))
1066c95142eSMartin Matuska 		root[3] = '\0';
1076c95142eSMartin Matuska 	else
1086c95142eSMartin Matuska 		return (0);
1096c95142eSMartin Matuska 	assertEqualInt((r = GetVolumeInformation(root, vol,
1106c95142eSMartin Matuska 	    sizeof(vol), NULL, NULL, &flags, sys, sizeof(sys))), 1);
1116c95142eSMartin Matuska 	return (r != 0 && (flags & FILE_SUPPORTS_SPARSE_FILES) != 0);
1126c95142eSMartin Matuska }
1136c95142eSMartin Matuska 
1146c95142eSMartin Matuska static void
create_sparse_file(const char * path,const struct sparse * s)1156c95142eSMartin Matuska create_sparse_file(const char *path, const struct sparse *s)
1166c95142eSMartin Matuska {
1176c95142eSMartin Matuska 	char buff[1024];
1186c95142eSMartin Matuska 	HANDLE handle;
1196c95142eSMartin Matuska 	DWORD dmy;
1206c95142eSMartin Matuska 
1216c95142eSMartin Matuska 	memset(buff, ' ', sizeof(buff));
1226c95142eSMartin Matuska 
1236c95142eSMartin Matuska 	handle = CreateFileA(path, GENERIC_WRITE, 0,
1246c95142eSMartin Matuska 	    NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL,
1256c95142eSMartin Matuska 	    NULL);
1266c95142eSMartin Matuska 	assert(handle != INVALID_HANDLE_VALUE);
1276c95142eSMartin Matuska 	assert(DeviceIoControl(handle, FSCTL_SET_SPARSE, NULL, 0,
1286c95142eSMartin Matuska 	    NULL, 0, &dmy, NULL) != 0);
129f1951fd7SMartin Matuska 
130f1951fd7SMartin Matuska 	size_t offsetSoFar = 0;
131f1951fd7SMartin Matuska 
1326c95142eSMartin Matuska 	while (s->type != END) {
1336c95142eSMartin Matuska 		if (s->type == HOLE) {
134f1951fd7SMartin Matuska 			LARGE_INTEGER fileOffset, beyondOffset, distanceToMove;
135f1951fd7SMartin Matuska 			fileOffset.QuadPart = offsetSoFar;
136f1951fd7SMartin Matuska 			beyondOffset.QuadPart = offsetSoFar + s->size;
137f1951fd7SMartin Matuska 			distanceToMove.QuadPart = s->size;
1386c95142eSMartin Matuska 
139f1951fd7SMartin Matuska 			FILE_ZERO_DATA_INFORMATION zeroInformation;
140f1951fd7SMartin Matuska 			zeroInformation.FileOffset = fileOffset;
141f1951fd7SMartin Matuska 			zeroInformation.BeyondFinalZero = beyondOffset;
142f1951fd7SMartin Matuska 
143f1951fd7SMartin Matuska 			DWORD bytesReturned;
144f1951fd7SMartin Matuska 			assert(SetFilePointerEx(handle, distanceToMove,
1456c95142eSMartin Matuska 				NULL, FILE_CURRENT) != 0);
146f1951fd7SMartin Matuska 			assert(SetEndOfFile(handle) != 0);
147f1951fd7SMartin Matuska 			assert(DeviceIoControl(handle, FSCTL_SET_ZERO_DATA, &zeroInformation,
148f1951fd7SMartin Matuska 				sizeof(FILE_ZERO_DATA_INFORMATION), NULL, 0, &bytesReturned, NULL) != 0);
1496c95142eSMartin Matuska 		} else {
150acc60b03SMartin Matuska 			DWORD w, wr;
151acc60b03SMartin Matuska 			size_t size;
1526c95142eSMartin Matuska 
1536c95142eSMartin Matuska 			size = s->size;
1546c95142eSMartin Matuska 			while (size) {
1556c95142eSMartin Matuska 				if (size > sizeof(buff))
1566c95142eSMartin Matuska 					w = sizeof(buff);
1576c95142eSMartin Matuska 				else
158acc60b03SMartin Matuska 					w = (DWORD)size;
1596c95142eSMartin Matuska 				assert(WriteFile(handle, buff, w, &wr, NULL) != 0);
1606c95142eSMartin Matuska 				size -= wr;
1616c95142eSMartin Matuska 			}
1626c95142eSMartin Matuska 		}
163f1951fd7SMartin Matuska 		offsetSoFar += s->size;
1646c95142eSMartin Matuska 		s++;
1656c95142eSMartin Matuska 	}
1666c95142eSMartin Matuska 	assertEqualInt(CloseHandle(handle), 1);
1676c95142eSMartin Matuska }
1686c95142eSMartin Matuska 
1696c95142eSMartin Matuska #else
1706c95142eSMartin Matuska 
171d5d08d29SMartin Matuska #if defined(HAVE_LINUX_FIEMAP_H)
1726c95142eSMartin Matuska /*
1736c95142eSMartin Matuska  * FIEMAP, which can detect 'hole' of a sparse file, has
1746c95142eSMartin Matuska  * been supported from 2.6.28
1756c95142eSMartin Matuska  */
1766c95142eSMartin Matuska 
1776c95142eSMartin Matuska static int
is_sparse_supported_fiemap(const char * path)178d5d08d29SMartin Matuska is_sparse_supported_fiemap(const char *path)
1796c95142eSMartin Matuska {
1806c95142eSMartin Matuska 	const struct sparse sparse_file[] = {
1816c95142eSMartin Matuska  		/* This hole size is too small to create a sparse
1826c95142eSMartin Matuska 		 * files for almost filesystem. */
1836c95142eSMartin Matuska 		{ HOLE,	 1024 }, { DATA, 10240 },
1846c95142eSMartin Matuska 		{ END,	0 }
1856c95142eSMartin Matuska 	};
1866c95142eSMartin Matuska 	int fd, r;
1876c95142eSMartin Matuska 	struct fiemap *fm;
1886c95142eSMartin Matuska 	char buff[1024];
1896c95142eSMartin Matuska 	const char *testfile = "can_sparse";
1906c95142eSMartin Matuska 
1916c95142eSMartin Matuska 	(void)path; /* UNUSED */
192acc60b03SMartin Matuska 	memset(buff, 0, sizeof(buff));
1936c95142eSMartin Matuska 	create_sparse_file(testfile, sparse_file);
1946c95142eSMartin Matuska 	fd = open(testfile,  O_RDWR);
1956c95142eSMartin Matuska 	if (fd < 0)
1966c95142eSMartin Matuska 		return (0);
1976c95142eSMartin Matuska 	fm = (struct fiemap *)buff;
1986c95142eSMartin Matuska 	fm->fm_start = 0;
1996c95142eSMartin Matuska 	fm->fm_length = ~0ULL;;
2006c95142eSMartin Matuska 	fm->fm_flags = FIEMAP_FLAG_SYNC;
2016c95142eSMartin Matuska 	fm->fm_extent_count = (sizeof(buff) - sizeof(*fm))/
2026c95142eSMartin Matuska 		sizeof(struct fiemap_extent);
2036c95142eSMartin Matuska 	r = ioctl(fd, FS_IOC_FIEMAP, fm);
2046c95142eSMartin Matuska 	close(fd);
2056c95142eSMartin Matuska 	unlink(testfile);
206fd082e96SMartin Matuska 	return (r >= 0);
2076c95142eSMartin Matuska }
2086c95142eSMartin Matuska 
209d5d08d29SMartin Matuska #if !defined(SEEK_HOLE) || !defined(SEEK_DATA)
210d5d08d29SMartin Matuska static int
is_sparse_supported(const char * path)211d5d08d29SMartin Matuska is_sparse_supported(const char *path)
212d5d08d29SMartin Matuska {
213d5d08d29SMartin Matuska 	return is_sparse_supported_fiemap(path);
214d5d08d29SMartin Matuska }
215d5d08d29SMartin Matuska #endif
216d5d08d29SMartin Matuska #endif
217d5d08d29SMartin Matuska 
218d5d08d29SMartin Matuska #if defined(_PC_MIN_HOLE_SIZE)
219d5d08d29SMartin Matuska 
220d5d08d29SMartin Matuska /*
221d5d08d29SMartin Matuska  * FreeBSD and Solaris can detect 'hole' of a sparse file
222d5d08d29SMartin Matuska  * through lseek(HOLE) on ZFS. (UFS does not support yet)
223d5d08d29SMartin Matuska  */
224d5d08d29SMartin Matuska 
225d5d08d29SMartin Matuska static int
is_sparse_supported(const char * path)226d5d08d29SMartin Matuska is_sparse_supported(const char *path)
227d5d08d29SMartin Matuska {
228d5d08d29SMartin Matuska 	return (pathconf(path, _PC_MIN_HOLE_SIZE) > 0);
229d5d08d29SMartin Matuska }
230d5d08d29SMartin Matuska 
231d5d08d29SMartin Matuska #elif defined(SEEK_HOLE) && defined(SEEK_DATA)
232d5d08d29SMartin Matuska 
233d5d08d29SMartin Matuska static int
is_sparse_supported(const char * path)234d5d08d29SMartin Matuska is_sparse_supported(const char *path)
235d5d08d29SMartin Matuska {
236d5d08d29SMartin Matuska 	const struct sparse sparse_file[] = {
237d5d08d29SMartin Matuska  		/* This hole size is too small to create a sparse
238d5d08d29SMartin Matuska 		 * files for almost filesystem. */
239d5d08d29SMartin Matuska 		{ HOLE,	 1024 }, { DATA, 10240 },
240d5d08d29SMartin Matuska 		{ END,	0 }
241d5d08d29SMartin Matuska 	};
242d5d08d29SMartin Matuska 	int fd, r;
243d5d08d29SMartin Matuska 	const char *testfile = "can_sparse";
244d5d08d29SMartin Matuska 
245d5d08d29SMartin Matuska 	(void)path; /* UNUSED */
246d5d08d29SMartin Matuska 	create_sparse_file(testfile, sparse_file);
247d5d08d29SMartin Matuska 	fd = open(testfile,  O_RDWR);
248d5d08d29SMartin Matuska 	if (fd < 0)
249d5d08d29SMartin Matuska 		return (0);
250d5d08d29SMartin Matuska 	r = lseek(fd, 0, SEEK_HOLE);
251d5d08d29SMartin Matuska 	close(fd);
252d5d08d29SMartin Matuska 	unlink(testfile);
253d5d08d29SMartin Matuska #if defined(HAVE_LINUX_FIEMAP_H)
254d5d08d29SMartin Matuska 	if (r < 0)
255d5d08d29SMartin Matuska 		return (is_sparse_supported_fiemap(path));
256d5d08d29SMartin Matuska #endif
257d5d08d29SMartin Matuska 	return (r >= 0);
258d5d08d29SMartin Matuska }
259d5d08d29SMartin Matuska 
260d5d08d29SMartin Matuska #elif !defined(HAVE_LINUX_FIEMAP_H)
2616c95142eSMartin Matuska 
2626c95142eSMartin Matuska /*
2636c95142eSMartin Matuska  * Other system may do not have the API such as lseek(HOLE),
2646c95142eSMartin Matuska  * which detect 'hole' of a sparse file.
2656c95142eSMartin Matuska  */
2666c95142eSMartin Matuska 
2676c95142eSMartin Matuska static int
is_sparse_supported(const char * path)2686c95142eSMartin Matuska is_sparse_supported(const char *path)
2696c95142eSMartin Matuska {
2706c95142eSMartin Matuska 	(void)path; /* UNUSED */
2716c95142eSMartin Matuska 	return (0);
2726c95142eSMartin Matuska }
2736c95142eSMartin Matuska 
2746c95142eSMartin Matuska #endif
2756c95142eSMartin Matuska 
2766c95142eSMartin Matuska /*
2776c95142eSMartin Matuska  * Create a sparse file on POSIX like system.
2786c95142eSMartin Matuska  */
2796c95142eSMartin Matuska 
2806c95142eSMartin Matuska static void
create_sparse_file(const char * path,const struct sparse * s)2816c95142eSMartin Matuska create_sparse_file(const char *path, const struct sparse *s)
2826c95142eSMartin Matuska {
2836c95142eSMartin Matuska 	char buff[1024];
2846c95142eSMartin Matuska 	int fd;
285cdf63a70SMartin Matuska 	size_t total_size = 0;
286cdf63a70SMartin Matuska 	const struct sparse *cur = s;
2876c95142eSMartin Matuska 
2886c95142eSMartin Matuska 	memset(buff, ' ', sizeof(buff));
2896c95142eSMartin Matuska 	assert((fd = open(path, O_CREAT | O_WRONLY, 0600)) != -1);
290cdf63a70SMartin Matuska 
291cdf63a70SMartin Matuska 	/* Handle holes at the end by extending the file */
292cdf63a70SMartin Matuska 	while (cur->type != END) {
293cdf63a70SMartin Matuska 		total_size += cur->size;
294cdf63a70SMartin Matuska 		++cur;
295cdf63a70SMartin Matuska 	}
296cdf63a70SMartin Matuska 	assert(ftruncate(fd, total_size) != -1);
297cdf63a70SMartin Matuska 
2986c95142eSMartin Matuska 	while (s->type != END) {
2996c95142eSMartin Matuska 		if (s->type == HOLE) {
3006c95142eSMartin Matuska 			assert(lseek(fd, s->size, SEEK_CUR) != (off_t)-1);
3016c95142eSMartin Matuska 		} else {
3026c95142eSMartin Matuska 			size_t w, size;
3036c95142eSMartin Matuska 
3046c95142eSMartin Matuska 			size = s->size;
3056c95142eSMartin Matuska 			while (size) {
3066c95142eSMartin Matuska 				if (size > sizeof(buff))
3076c95142eSMartin Matuska 					w = sizeof(buff);
3086c95142eSMartin Matuska 				else
3096c95142eSMartin Matuska 					w = size;
3106c95142eSMartin Matuska 				assert(write(fd, buff, w) != (ssize_t)-1);
3116c95142eSMartin Matuska 				size -= w;
3126c95142eSMartin Matuska 			}
3136c95142eSMartin Matuska 		}
3146c95142eSMartin Matuska 		s++;
3156c95142eSMartin Matuska 	}
3166c95142eSMartin Matuska 	close(fd);
3176c95142eSMartin Matuska }
3186c95142eSMartin Matuska 
3196c95142eSMartin Matuska #endif
3206c95142eSMartin Matuska 
3216c95142eSMartin Matuska /*
3226c95142eSMartin Matuska  * Sparse test with directory traversals.
3236c95142eSMartin Matuska  */
3246c95142eSMartin Matuska static void
verify_sparse_file(struct archive * a,const char * path,const struct sparse * sparse,int expected_holes)3256c95142eSMartin Matuska verify_sparse_file(struct archive *a, const char *path,
326cdf63a70SMartin Matuska     const struct sparse *sparse, int expected_holes)
3276c95142eSMartin Matuska {
3286c95142eSMartin Matuska 	struct archive_entry *ae;
3296c95142eSMartin Matuska 	const void *buff;
3306c95142eSMartin Matuska 	size_t bytes_read;
331cdf63a70SMartin Matuska 	int64_t offset, expected_offset, last_offset;
332cdf63a70SMartin Matuska 	int holes_seen = 0;
3336c95142eSMartin Matuska 
3346c95142eSMartin Matuska 	create_sparse_file(path, sparse);
3356c95142eSMartin Matuska 	assert((ae = archive_entry_new()) != NULL);
3366c95142eSMartin Matuska 	assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, path));
3376c95142eSMartin Matuska 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae));
338cdf63a70SMartin Matuska 
339cdf63a70SMartin Matuska 	expected_offset = 0;
340cdf63a70SMartin Matuska 	last_offset = 0;
3416c95142eSMartin Matuska 	while (ARCHIVE_OK == archive_read_data_block(a, &buff, &bytes_read,
3426c95142eSMartin Matuska 	    &offset)) {
343cdf63a70SMartin Matuska 		const char *start = buff;
344cdf63a70SMartin Matuska #if DEBUG
345cdf63a70SMartin Matuska 		fprintf(stderr, "%s: bytes_read=%d offset=%d\n", path, (int)bytes_read, (int)offset);
346cdf63a70SMartin Matuska #endif
347cdf63a70SMartin Matuska 		if (offset > last_offset) {
348cdf63a70SMartin Matuska 			++holes_seen;
3496c95142eSMartin Matuska 		}
350cdf63a70SMartin Matuska 		/* Blocks entirely before the data we just read. */
351cdf63a70SMartin Matuska 		while (expected_offset + (int64_t)sparse->size < offset) {
352cdf63a70SMartin Matuska #if DEBUG
353cdf63a70SMartin Matuska 			fprintf(stderr, "    skipping expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
354cdf63a70SMartin Matuska #endif
355cdf63a70SMartin Matuska 			/* Must be holes. */
356cdf63a70SMartin Matuska 			assert(sparse->type == HOLE);
357cdf63a70SMartin Matuska 			expected_offset += sparse->size;
358cdf63a70SMartin Matuska 			++sparse;
3596c95142eSMartin Matuska 		}
360cdf63a70SMartin Matuska 		/* Block that overlaps beginning of data */
361cdf63a70SMartin Matuska 		if (expected_offset < offset
362cdf63a70SMartin Matuska 		    && expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) {
363cdf63a70SMartin Matuska 			const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size;
364cdf63a70SMartin Matuska #if DEBUG
365cdf63a70SMartin Matuska 			fprintf(stderr, "    overlapping hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
366cdf63a70SMartin Matuska #endif
367cdf63a70SMartin Matuska 			/* Must be a hole, overlap must be filled with '\0' */
368cdf63a70SMartin Matuska 			if (assert(sparse->type == HOLE)) {
369cdf63a70SMartin Matuska 				assertMemoryFilledWith(start, end - start, '\0');
370cdf63a70SMartin Matuska 			}
371cdf63a70SMartin Matuska 			start = end;
372cdf63a70SMartin Matuska 			expected_offset += sparse->size;
373cdf63a70SMartin Matuska 			++sparse;
374cdf63a70SMartin Matuska 		}
375cdf63a70SMartin Matuska 		/* Blocks completely contained in data we just read. */
376cdf63a70SMartin Matuska 		while (expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) {
377cdf63a70SMartin Matuska 			const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size;
378cdf63a70SMartin Matuska 			if (sparse->type == HOLE) {
379cdf63a70SMartin Matuska #if DEBUG
380cdf63a70SMartin Matuska 				fprintf(stderr, "    contained hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
381cdf63a70SMartin Matuska #endif
382cdf63a70SMartin Matuska 
383cdf63a70SMartin Matuska 				/* verify data corresponding to hole is '\0' */
384cdf63a70SMartin Matuska 				if (end > (const char *)buff + bytes_read) {
385cdf63a70SMartin Matuska 					end = (const char *)buff + bytes_read;
386cdf63a70SMartin Matuska 				}
387cdf63a70SMartin Matuska 				assertMemoryFilledWith(start, end - start, '\0');
388cdf63a70SMartin Matuska 				start = end;
389cdf63a70SMartin Matuska 				expected_offset += sparse->size;
390cdf63a70SMartin Matuska 				++sparse;
391cdf63a70SMartin Matuska 			} else if (sparse->type == DATA) {
392cdf63a70SMartin Matuska #if DEBUG
393cdf63a70SMartin Matuska 				fprintf(stderr, "    contained data expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
394cdf63a70SMartin Matuska #endif
395cdf63a70SMartin Matuska 				/* verify data corresponding to hole is ' ' */
396cdf63a70SMartin Matuska 				if (assert(expected_offset + sparse->size <= offset + bytes_read)) {
397cdf63a70SMartin Matuska 					assert(start == (const char *)buff + (size_t)(expected_offset - offset));
398cdf63a70SMartin Matuska 					assertMemoryFilledWith(start, end - start, ' ');
399cdf63a70SMartin Matuska 				}
400cdf63a70SMartin Matuska 				start = end;
401cdf63a70SMartin Matuska 				expected_offset += sparse->size;
402cdf63a70SMartin Matuska 				++sparse;
403cdf63a70SMartin Matuska 			} else {
404cdf63a70SMartin Matuska 				break;
405cdf63a70SMartin Matuska 			}
406cdf63a70SMartin Matuska 		}
407cdf63a70SMartin Matuska 		/* Block that overlaps end of data */
408cdf63a70SMartin Matuska 		if (expected_offset < offset + (int64_t)bytes_read) {
409cdf63a70SMartin Matuska 			const char *end = (const char *)buff + bytes_read;
410cdf63a70SMartin Matuska #if DEBUG
411cdf63a70SMartin Matuska 			fprintf(stderr, "    trailing overlap expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
412cdf63a70SMartin Matuska #endif
413cdf63a70SMartin Matuska 			/* Must be a hole, overlap must be filled with '\0' */
414cdf63a70SMartin Matuska 			if (assert(sparse->type == HOLE)) {
415cdf63a70SMartin Matuska 				assertMemoryFilledWith(start, end - start, '\0');
416cdf63a70SMartin Matuska 			}
417cdf63a70SMartin Matuska 		}
418cdf63a70SMartin Matuska 		last_offset = offset + bytes_read;
419cdf63a70SMartin Matuska 	}
420cdf63a70SMartin Matuska 	/* Count a hole at EOF? */
421cdf63a70SMartin Matuska 	if (last_offset < archive_entry_size(ae)) {
422cdf63a70SMartin Matuska 		++holes_seen;
423cdf63a70SMartin Matuska 	}
424cdf63a70SMartin Matuska 
425cdf63a70SMartin Matuska 	/* Verify blocks after last read */
426cdf63a70SMartin Matuska 	while (sparse->type == HOLE) {
427cdf63a70SMartin Matuska 		expected_offset += sparse->size;
428cdf63a70SMartin Matuska 		++sparse;
429cdf63a70SMartin Matuska 	}
430cdf63a70SMartin Matuska 	assert(sparse->type == END);
431cdf63a70SMartin Matuska 	assertEqualInt(expected_offset, archive_entry_size(ae));
432cdf63a70SMartin Matuska 
433482a8150SMartin Matuska 	failure(path);
434cdf63a70SMartin Matuska 	assertEqualInt(holes_seen, expected_holes);
4356c95142eSMartin Matuska 
4366c95142eSMartin Matuska 	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
4376c95142eSMartin Matuska 	archive_entry_free(ae);
4386c95142eSMartin Matuska }
4396c95142eSMartin Matuska 
4406c95142eSMartin Matuska #if defined(_WIN32) && !defined(__CYGWIN__)
4416c95142eSMartin Matuska #define	close		_close
4426c95142eSMartin Matuska #define	open		_open
4436c95142eSMartin Matuska #endif
4446c95142eSMartin Matuska 
4456c95142eSMartin Matuska /*
4466c95142eSMartin Matuska  * Sparse test without directory traversals.
4476c95142eSMartin Matuska  */
4486c95142eSMartin Matuska static void
verify_sparse_file2(struct archive * a,const char * path,const struct sparse * sparse,int blocks,int preopen)4496c95142eSMartin Matuska verify_sparse_file2(struct archive *a, const char *path,
4506c95142eSMartin Matuska     const struct sparse *sparse, int blocks, int preopen)
4516c95142eSMartin Matuska {
4526c95142eSMartin Matuska 	struct archive_entry *ae;
4536c95142eSMartin Matuska 	int fd;
4546c95142eSMartin Matuska 
4556c95142eSMartin Matuska 	(void)sparse; /* UNUSED */
4566c95142eSMartin Matuska 	assert((ae = archive_entry_new()) != NULL);
4576c95142eSMartin Matuska 	archive_entry_set_pathname(ae, path);
4586c95142eSMartin Matuska 	if (preopen)
4596c95142eSMartin Matuska 		fd = open(path, O_RDONLY | O_BINARY);
4606c95142eSMartin Matuska 	else
4616c95142eSMartin Matuska 		fd = -1;
4626c95142eSMartin Matuska 	assertEqualIntA(a, ARCHIVE_OK,
4636c95142eSMartin Matuska 	    archive_read_disk_entry_from_file(a, ae, fd, NULL));
4646c95142eSMartin Matuska 	if (fd >= 0)
4656c95142eSMartin Matuska 		close(fd);
4666c95142eSMartin Matuska 	/* Verify the number of holes only, not its offset nor its
4676c95142eSMartin Matuska 	 * length because those alignments are deeply dependence on
4686c95142eSMartin Matuska 	 * its filesystem. */
469482a8150SMartin Matuska 	failure(path);
4706c95142eSMartin Matuska 	assertEqualInt(blocks, archive_entry_sparse_count(ae));
4716c95142eSMartin Matuska 	archive_entry_free(ae);
4726c95142eSMartin Matuska }
4736c95142eSMartin Matuska 
4746c95142eSMartin Matuska static void
test_sparse_whole_file_data()4756c95142eSMartin Matuska test_sparse_whole_file_data()
4766c95142eSMartin Matuska {
4776c95142eSMartin Matuska 	struct archive_entry *ae;
4786c95142eSMartin Matuska 	int64_t offset;
4796c95142eSMartin Matuska 	int i;
4806c95142eSMartin Matuska 
4816c95142eSMartin Matuska 	assert((ae = archive_entry_new()) != NULL);
4826c95142eSMartin Matuska 	archive_entry_set_size(ae, 1024*10);
4836c95142eSMartin Matuska 
4846c95142eSMartin Matuska 	/*
4856c95142eSMartin Matuska 	 * Add sparse block data up to the file size.
4866c95142eSMartin Matuska 	 */
4876c95142eSMartin Matuska 	offset = 0;
4886c95142eSMartin Matuska 	for (i = 0; i < 10; i++) {
4896c95142eSMartin Matuska 		archive_entry_sparse_add_entry(ae, offset, 1024);
4906c95142eSMartin Matuska 		offset += 1024;
4916c95142eSMartin Matuska 	}
4926c95142eSMartin Matuska 
4936c95142eSMartin Matuska 	failure("There should be no sparse");
4946c95142eSMartin Matuska 	assertEqualInt(0, archive_entry_sparse_count(ae));
4956c95142eSMartin Matuska 	archive_entry_free(ae);
4966c95142eSMartin Matuska }
4976c95142eSMartin Matuska 
DEFINE_TEST(test_sparse_basic)4986c95142eSMartin Matuska DEFINE_TEST(test_sparse_basic)
4996c95142eSMartin Matuska {
5006c95142eSMartin Matuska 	char *cwd;
5016c95142eSMartin Matuska 	struct archive *a;
502*590d65eeSMartin Matuska 	const char *skip_sparse_tests;
5036c95142eSMartin Matuska 	/*
5046c95142eSMartin Matuska 	 * The alignment of the hole of sparse files deeply depends
5056c95142eSMartin Matuska 	 * on filesystem. In my experience, sparse_file2 test with
5066c95142eSMartin Matuska 	 * 204800 bytes hole size did not pass on ZFS and the result
5076c95142eSMartin Matuska 	 * of that test seemed the size was too small, thus you should
5086c95142eSMartin Matuska 	 * keep a hole size more than 409600 bytes to pass this test
5096c95142eSMartin Matuska 	 * on all platform.
5106c95142eSMartin Matuska 	 */
5116c95142eSMartin Matuska 	const struct sparse sparse_file0[] = {
512f1951fd7SMartin Matuska 		// 0             // 1024
513*590d65eeSMartin Matuska 		{ DATA,	 1024 }, { HOLE,   MIN_HOLE + 1638400 },
514f1951fd7SMartin Matuska 		// 2049024       // 2051072
515*590d65eeSMartin Matuska 		{ DATA,	 2048 }, { HOLE,   MIN_HOLE + 1638400 },
516f1951fd7SMartin Matuska 		// 4099072       // 4103168
517*590d65eeSMartin Matuska 		{ DATA,	 4096 }, { HOLE,  MIN_HOLE + 20070400 },
518f1951fd7SMartin Matuska 		// 24583168      // 24591360
519*590d65eeSMartin Matuska 		{ DATA,	 8192 }, { HOLE, MIN_HOLE + 204390400 },
520f1951fd7SMartin Matuska 		// 229391360     // 229391361
5216c95142eSMartin Matuska 		{ DATA,     1 }, { END,	0 }
5226c95142eSMartin Matuska 	};
5236c95142eSMartin Matuska 	const struct sparse sparse_file1[] = {
524*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE }, { DATA, 1 },
525*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE }, { DATA, 1 },
526*590d65eeSMartin Matuska 		{ HOLE, MIN_HOLE }, { END,  0 }
5276c95142eSMartin Matuska 	};
5286c95142eSMartin Matuska 	const struct sparse sparse_file2[] = {
529*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE }, { DATA, 1024 },
530*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 1 }, { DATA, 1024 },
531*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 2 }, { DATA, 1024 },
532*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 3 }, { DATA, 1024 },
533*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 4 }, { DATA, 1024 },
534*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 5 }, { DATA, 1024 },
535*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 6 }, { DATA, 1024 },
536*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 7 }, { DATA, 1024 },
537*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 8 }, { DATA, 1024 },
538*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 9}, { DATA, 1024 },/* 10 */
539*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE }, { DATA, 1024 * 1 },
540*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 1 }, { DATA, 1024 * 2 },
541*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 2 }, { DATA, 1024 * 3 },
542*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 3 }, { DATA, 1024 * 4 },
543*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 4 }, { DATA, 1024 * 5 },
544*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 5 }, { DATA, 1024 * 6 },
545*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 6 }, { DATA, 1024 * 7 },
546*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 7 }, { DATA, 1024 * 8 },
547*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 8 }, { DATA, 1024 * 9 },
548*590d65eeSMartin Matuska 		{ HOLE,	MIN_HOLE + 409600 * 9}, { DATA, 1024 * 10},/* 20 */
5496c95142eSMartin Matuska 		{ END,	0 }
5506c95142eSMartin Matuska 	};
5516c95142eSMartin Matuska 	const struct sparse sparse_file3[] = {
552cdf63a70SMartin Matuska  		/* This hole size is too small to create a sparse file */
553cdf63a70SMartin Matuska 		{ HOLE,	 1 }, { DATA, 10240 },
554cdf63a70SMartin Matuska 		{ HOLE,	 1 }, { DATA, 10240 },
555cdf63a70SMartin Matuska 		{ HOLE,	 1 }, { DATA, 10240 },
5566c95142eSMartin Matuska 		{ END,	0 }
5576c95142eSMartin Matuska 	};
5586c95142eSMartin Matuska 
5596c95142eSMartin Matuska 	/*
5606c95142eSMartin Matuska 	 * Test for the case that sparse data indicates just the whole file
5616c95142eSMartin Matuska 	 * data.
5626c95142eSMartin Matuska 	 */
5636c95142eSMartin Matuska 	test_sparse_whole_file_data();
5646c95142eSMartin Matuska 
565*590d65eeSMartin Matuska 	skip_sparse_tests = getenv("SKIP_TEST_SPARSE");
566*590d65eeSMartin Matuska 	if (skip_sparse_tests != NULL) {
567*590d65eeSMartin Matuska 		skipping("Skipping sparse tests due to SKIP_TEST_SPARSE "
568*590d65eeSMartin Matuska 		    "environment variable");
569*590d65eeSMartin Matuska 		return;
570*590d65eeSMartin Matuska 	}
571*590d65eeSMartin Matuska 
5726c95142eSMartin Matuska 	/* Check if the filesystem where CWD on can
5736c95142eSMartin Matuska 	 * report the number of the holes of a sparse file. */
5746c95142eSMartin Matuska #ifdef PATH_MAX
5756c95142eSMartin Matuska 	cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */
5766c95142eSMartin Matuska #else
5776c95142eSMartin Matuska 	cwd = getcwd(NULL, 0);
5786c95142eSMartin Matuska #endif
5796c95142eSMartin Matuska 	if (!assert(cwd != NULL))
5806c95142eSMartin Matuska 		return;
5816c95142eSMartin Matuska 	if (!is_sparse_supported(cwd)) {
5826c95142eSMartin Matuska 		free(cwd);
5836c95142eSMartin Matuska 		skipping("This filesystem or platform do not support "
5846c95142eSMartin Matuska 		    "the reporting of the holes of a sparse file through "
5856c95142eSMartin Matuska 		    "API such as lseek(HOLE)");
5866c95142eSMartin Matuska 		return;
5876c95142eSMartin Matuska 	}
5886c95142eSMartin Matuska 
5896c95142eSMartin Matuska 	/*
5906c95142eSMartin Matuska 	 * Get sparse data through directory traversals.
5916c95142eSMartin Matuska 	 */
5926c95142eSMartin Matuska 	assert((a = archive_read_disk_new()) != NULL);
5936c95142eSMartin Matuska 
594cdf63a70SMartin Matuska 	verify_sparse_file(a, "file0", sparse_file0, 4);
595cdf63a70SMartin Matuska 	verify_sparse_file(a, "file1", sparse_file1, 3);
5966c95142eSMartin Matuska 	verify_sparse_file(a, "file2", sparse_file2, 20);
597cdf63a70SMartin Matuska 	/* Encoded non sparse; expect a data block but no sparse entries. */
5986c95142eSMartin Matuska 	verify_sparse_file(a, "file3", sparse_file3, 0);
5996c95142eSMartin Matuska 
6006c95142eSMartin Matuska 	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
6016c95142eSMartin Matuska 
6026c95142eSMartin Matuska 	/*
6036c95142eSMartin Matuska 	 * Get sparse data through archive_read_disk_entry_from_file().
6046c95142eSMartin Matuska 	 */
6056c95142eSMartin Matuska 	assert((a = archive_read_disk_new()) != NULL);
6066c95142eSMartin Matuska 
6076c95142eSMartin Matuska 	verify_sparse_file2(a, "file0", sparse_file0, 5, 0);
6086c95142eSMartin Matuska 	verify_sparse_file2(a, "file0", sparse_file0, 5, 1);
6096c95142eSMartin Matuska 
6106c95142eSMartin Matuska 	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
6116c95142eSMartin Matuska 	free(cwd);
6126c95142eSMartin Matuska }
613cdf63a70SMartin Matuska 
DEFINE_TEST(test_fully_sparse_files)614cdf63a70SMartin Matuska DEFINE_TEST(test_fully_sparse_files)
615cdf63a70SMartin Matuska {
616cdf63a70SMartin Matuska 	char *cwd;
617cdf63a70SMartin Matuska 	struct archive *a;
618*590d65eeSMartin Matuska 	const char *skip_sparse_tests;
619cdf63a70SMartin Matuska 
620cdf63a70SMartin Matuska 	const struct sparse sparse_file[] = {
621*590d65eeSMartin Matuska 		{ HOLE, MIN_HOLE }, { END, 0 }
622cdf63a70SMartin Matuska 	};
623*590d65eeSMartin Matuska 
624*590d65eeSMartin Matuska 	skip_sparse_tests = getenv("SKIP_TEST_SPARSE");
625*590d65eeSMartin Matuska 	if (skip_sparse_tests != NULL) {
626*590d65eeSMartin Matuska 		skipping("Skipping sparse tests due to SKIP_TEST_SPARSE "
627*590d65eeSMartin Matuska 		    "environment variable");
628*590d65eeSMartin Matuska 		return;
629*590d65eeSMartin Matuska 	}
630*590d65eeSMartin Matuska 
631cdf63a70SMartin Matuska 	/* Check if the filesystem where CWD on can
632cdf63a70SMartin Matuska 	 * report the number of the holes of a sparse file. */
633cdf63a70SMartin Matuska #ifdef PATH_MAX
634cdf63a70SMartin Matuska 	cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */
635cdf63a70SMartin Matuska #else
636cdf63a70SMartin Matuska 	cwd = getcwd(NULL, 0);
637cdf63a70SMartin Matuska #endif
638cdf63a70SMartin Matuska 	if (!assert(cwd != NULL))
639cdf63a70SMartin Matuska 		return;
640cdf63a70SMartin Matuska 	if (!is_sparse_supported(cwd)) {
641cdf63a70SMartin Matuska 		free(cwd);
642cdf63a70SMartin Matuska 		skipping("This filesystem or platform do not support "
643cdf63a70SMartin Matuska 		    "the reporting of the holes of a sparse file through "
644cdf63a70SMartin Matuska 		    "API such as lseek(HOLE)");
645cdf63a70SMartin Matuska 		return;
646cdf63a70SMartin Matuska 	}
647cdf63a70SMartin Matuska 
648cdf63a70SMartin Matuska 	assert((a = archive_read_disk_new()) != NULL);
649cdf63a70SMartin Matuska 
650cdf63a70SMartin Matuska 	/* Fully sparse files are encoded with a zero-length "data" block. */
651cdf63a70SMartin Matuska 	verify_sparse_file(a, "file0", sparse_file, 1);
652cdf63a70SMartin Matuska 
653cdf63a70SMartin Matuska 	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
654cdf63a70SMartin Matuska 	free(cwd);
655cdf63a70SMartin Matuska }
656