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