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. 60313570Smm * You may perform 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 71348607Smm#if defined(__APPLE__) 72348607Smm/* On APFS holes need to be at least 4096x4097 bytes */ 73348607Smm#define MIN_HOLE 16781312 74348607Smm#else 75348607Smm/* Elsewhere we work with 4096*10 bytes */ 76348607Smm#define MIN_HOLE 409600 77348607Smm#endif 78348607Smm 79231200Smm#if defined(_WIN32) && !defined(__CYGWIN__) 80231200Smm#include <winioctl.h> 81231200Smm/* 82231200Smm * Create a sparse file on Windows. 83231200Smm */ 84231200Smm 85231200Smm#if !defined(PATH_MAX) 86231200Smm#define PATH_MAX MAX_PATH 87231200Smm#endif 88231200Smm#if !defined(__BORLANDC__) 89231200Smm#define getcwd _getcwd 90231200Smm#endif 91231200Smm 92231200Smmstatic int 93231200Smmis_sparse_supported(const char *path) 94231200Smm{ 95231200Smm char root[MAX_PATH+1]; 96231200Smm char vol[MAX_PATH+1]; 97231200Smm char sys[MAX_PATH+1]; 98231200Smm DWORD flags; 99231200Smm BOOL r; 100231200Smm 101231200Smm strncpy(root, path, sizeof(root)-1); 102231200Smm if (((root[0] >= 'c' && root[0] <= 'z') || 103231200Smm (root[0] >= 'C' && root[0] <= 'Z')) && 104231200Smm root[1] == ':' && 105231200Smm (root[2] == '\\' || root[2] == '/')) 106231200Smm root[3] = '\0'; 107231200Smm else 108231200Smm return (0); 109231200Smm assertEqualInt((r = GetVolumeInformation(root, vol, 110231200Smm sizeof(vol), NULL, NULL, &flags, sys, sizeof(sys))), 1); 111231200Smm return (r != 0 && (flags & FILE_SUPPORTS_SPARSE_FILES) != 0); 112231200Smm} 113231200Smm 114231200Smmstatic void 115231200Smmcreate_sparse_file(const char *path, const struct sparse *s) 116231200Smm{ 117231200Smm char buff[1024]; 118231200Smm HANDLE handle; 119231200Smm DWORD dmy; 120231200Smm 121231200Smm memset(buff, ' ', sizeof(buff)); 122231200Smm 123231200Smm handle = CreateFileA(path, GENERIC_WRITE, 0, 124231200Smm NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 125231200Smm NULL); 126231200Smm assert(handle != INVALID_HANDLE_VALUE); 127231200Smm assert(DeviceIoControl(handle, FSCTL_SET_SPARSE, NULL, 0, 128231200Smm NULL, 0, &dmy, NULL) != 0); 129337351Smm 130358088Smm uint64_t offsetSoFar = 0; 131337351Smm 132231200Smm while (s->type != END) { 133231200Smm if (s->type == HOLE) { 134337351Smm LARGE_INTEGER fileOffset, beyondOffset, distanceToMove; 135337351Smm fileOffset.QuadPart = offsetSoFar; 136337351Smm beyondOffset.QuadPart = offsetSoFar + s->size; 137337351Smm distanceToMove.QuadPart = s->size; 138231200Smm 139337351Smm FILE_ZERO_DATA_INFORMATION zeroInformation; 140337351Smm zeroInformation.FileOffset = fileOffset; 141337351Smm zeroInformation.BeyondFinalZero = beyondOffset; 142337351Smm 143337351Smm DWORD bytesReturned; 144337351Smm assert(SetFilePointerEx(handle, distanceToMove, 145337351Smm NULL, FILE_CURRENT) != 0); 146337351Smm assert(SetEndOfFile(handle) != 0); 147337351Smm assert(DeviceIoControl(handle, FSCTL_SET_ZERO_DATA, &zeroInformation, 148337351Smm sizeof(FILE_ZERO_DATA_INFORMATION), NULL, 0, &bytesReturned, NULL) != 0); 149231200Smm } else { 150248616Smm DWORD w, wr; 151248616Smm size_t size; 152231200Smm 153231200Smm size = s->size; 154231200Smm while (size) { 155231200Smm if (size > sizeof(buff)) 156231200Smm w = sizeof(buff); 157231200Smm else 158248616Smm w = (DWORD)size; 159231200Smm assert(WriteFile(handle, buff, w, &wr, NULL) != 0); 160231200Smm size -= wr; 161231200Smm } 162231200Smm } 163337351Smm offsetSoFar += s->size; 164231200Smm s++; 165231200Smm } 166231200Smm assertEqualInt(CloseHandle(handle), 1); 167231200Smm} 168231200Smm 169231200Smm#else 170231200Smm 171311041Smm#if defined(HAVE_LINUX_FIEMAP_H) 172231200Smm/* 173231200Smm * FIEMAP, which can detect 'hole' of a sparse file, has 174231200Smm * been supported from 2.6.28 175231200Smm */ 176231200Smm 177231200Smmstatic int 178311041Smmis_sparse_supported_fiemap(const char *path) 179231200Smm{ 180231200Smm const struct sparse sparse_file[] = { 181231200Smm /* This hole size is too small to create a sparse 182231200Smm * files for almost filesystem. */ 183231200Smm { HOLE, 1024 }, { DATA, 10240 }, 184231200Smm { END, 0 } 185231200Smm }; 186231200Smm int fd, r; 187231200Smm struct fiemap *fm; 188231200Smm char buff[1024]; 189231200Smm const char *testfile = "can_sparse"; 190231200Smm 191232153Smm (void)path; /* UNUSED */ 192248616Smm memset(buff, 0, sizeof(buff)); 193231200Smm create_sparse_file(testfile, sparse_file); 194231200Smm fd = open(testfile, O_RDWR); 195231200Smm if (fd < 0) 196231200Smm return (0); 197231200Smm fm = (struct fiemap *)buff; 198231200Smm fm->fm_start = 0; 199231200Smm fm->fm_length = ~0ULL;; 200231200Smm fm->fm_flags = FIEMAP_FLAG_SYNC; 201231200Smm fm->fm_extent_count = (sizeof(buff) - sizeof(*fm))/ 202231200Smm sizeof(struct fiemap_extent); 203231200Smm r = ioctl(fd, FS_IOC_FIEMAP, fm); 204231200Smm close(fd); 205231200Smm unlink(testfile); 206238856Smm return (r >= 0); 207231200Smm} 208231200Smm 209311041Smm#if !defined(SEEK_HOLE) || !defined(SEEK_DATA) 210311041Smmstatic int 211311041Smmis_sparse_supported(const char *path) 212311041Smm{ 213311041Smm return is_sparse_supported_fiemap(path); 214311041Smm} 215311041Smm#endif 216311041Smm#endif 217231200Smm 218311041Smm#if defined(_PC_MIN_HOLE_SIZE) 219311041Smm 220231200Smm/* 221311041Smm * FreeBSD and Solaris can detect 'hole' of a sparse file 222311041Smm * through lseek(HOLE) on ZFS. (UFS does not support yet) 223311041Smm */ 224311041Smm 225311041Smmstatic int 226311041Smmis_sparse_supported(const char *path) 227311041Smm{ 228311041Smm return (pathconf(path, _PC_MIN_HOLE_SIZE) > 0); 229311041Smm} 230311041Smm 231311041Smm#elif defined(SEEK_HOLE) && defined(SEEK_DATA) 232311041Smm 233311041Smmstatic int 234311041Smmis_sparse_supported(const char *path) 235311041Smm{ 236311041Smm const struct sparse sparse_file[] = { 237311041Smm /* This hole size is too small to create a sparse 238311041Smm * files for almost filesystem. */ 239311041Smm { HOLE, 1024 }, { DATA, 10240 }, 240311041Smm { END, 0 } 241311041Smm }; 242311041Smm int fd, r; 243311041Smm const char *testfile = "can_sparse"; 244311041Smm 245311041Smm (void)path; /* UNUSED */ 246311041Smm create_sparse_file(testfile, sparse_file); 247311041Smm fd = open(testfile, O_RDWR); 248311041Smm if (fd < 0) 249311041Smm return (0); 250311041Smm r = lseek(fd, 0, SEEK_HOLE); 251311041Smm close(fd); 252311041Smm unlink(testfile); 253311041Smm#if defined(HAVE_LINUX_FIEMAP_H) 254311041Smm if (r < 0) 255311041Smm return (is_sparse_supported_fiemap(path)); 256311041Smm#endif 257311041Smm return (r >= 0); 258311041Smm} 259311041Smm 260311041Smm#elif !defined(HAVE_LINUX_FIEMAP_H) 261311041Smm 262311041Smm/* 263231200Smm * Other system may do not have the API such as lseek(HOLE), 264231200Smm * which detect 'hole' of a sparse file. 265231200Smm */ 266231200Smm 267231200Smmstatic int 268231200Smmis_sparse_supported(const char *path) 269231200Smm{ 270232153Smm (void)path; /* UNUSED */ 271231200Smm return (0); 272231200Smm} 273231200Smm 274231200Smm#endif 275231200Smm 276231200Smm/* 277231200Smm * Create a sparse file on POSIX like system. 278231200Smm */ 279231200Smm 280231200Smmstatic void 281231200Smmcreate_sparse_file(const char *path, const struct sparse *s) 282231200Smm{ 283231200Smm char buff[1024]; 284231200Smm int fd; 285358088Smm uint64_t total_size = 0; 286299529Smm const struct sparse *cur = s; 287231200Smm 288231200Smm memset(buff, ' ', sizeof(buff)); 289231200Smm assert((fd = open(path, O_CREAT | O_WRONLY, 0600)) != -1); 290299529Smm 291299529Smm /* Handle holes at the end by extending the file */ 292299529Smm while (cur->type != END) { 293299529Smm total_size += cur->size; 294299529Smm ++cur; 295299529Smm } 296299529Smm assert(ftruncate(fd, total_size) != -1); 297299529Smm 298231200Smm while (s->type != END) { 299231200Smm if (s->type == HOLE) { 300231200Smm assert(lseek(fd, s->size, SEEK_CUR) != (off_t)-1); 301231200Smm } else { 302231200Smm size_t w, size; 303231200Smm 304231200Smm size = s->size; 305231200Smm while (size) { 306231200Smm if (size > sizeof(buff)) 307231200Smm w = sizeof(buff); 308231200Smm else 309231200Smm w = size; 310231200Smm assert(write(fd, buff, w) != (ssize_t)-1); 311231200Smm size -= w; 312231200Smm } 313231200Smm } 314231200Smm s++; 315231200Smm } 316231200Smm close(fd); 317231200Smm} 318231200Smm 319231200Smm#endif 320231200Smm 321231200Smm/* 322231200Smm * Sparse test with directory traversals. 323231200Smm */ 324231200Smmstatic void 325231200Smmverify_sparse_file(struct archive *a, const char *path, 326299529Smm const struct sparse *sparse, int expected_holes) 327231200Smm{ 328231200Smm struct archive_entry *ae; 329231200Smm const void *buff; 330231200Smm size_t bytes_read; 331299529Smm int64_t offset, expected_offset, last_offset; 332299529Smm int holes_seen = 0; 333231200Smm 334231200Smm create_sparse_file(path, sparse); 335231200Smm assert((ae = archive_entry_new()) != NULL); 336231200Smm assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, path)); 337231200Smm assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); 338299529Smm 339299529Smm expected_offset = 0; 340299529Smm last_offset = 0; 341231200Smm while (ARCHIVE_OK == archive_read_data_block(a, &buff, &bytes_read, 342231200Smm &offset)) { 343299529Smm const char *start = buff; 344299529Smm#if DEBUG 345299529Smm fprintf(stderr, "%s: bytes_read=%d offset=%d\n", path, (int)bytes_read, (int)offset); 346299529Smm#endif 347299529Smm if (offset > last_offset) { 348299529Smm ++holes_seen; 349231200Smm } 350299529Smm /* Blocks entirely before the data we just read. */ 351299529Smm while (expected_offset + (int64_t)sparse->size < offset) { 352299529Smm#if DEBUG 353299529Smm fprintf(stderr, " skipping expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); 354299529Smm#endif 355299529Smm /* Must be holes. */ 356299529Smm assert(sparse->type == HOLE); 357299529Smm expected_offset += sparse->size; 358299529Smm ++sparse; 359299529Smm } 360299529Smm /* Block that overlaps beginning of data */ 361299529Smm if (expected_offset < offset 362299529Smm && expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) { 363299529Smm const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size; 364299529Smm#if DEBUG 365299529Smm fprintf(stderr, " overlapping hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); 366299529Smm#endif 367299529Smm /* Must be a hole, overlap must be filled with '\0' */ 368299529Smm if (assert(sparse->type == HOLE)) { 369299529Smm assertMemoryFilledWith(start, end - start, '\0'); 370299529Smm } 371299529Smm start = end; 372299529Smm expected_offset += sparse->size; 373299529Smm ++sparse; 374299529Smm } 375299529Smm /* Blocks completely contained in data we just read. */ 376299529Smm while (expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) { 377299529Smm const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size; 378299529Smm if (sparse->type == HOLE) { 379299529Smm#if DEBUG 380299529Smm fprintf(stderr, " contained hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); 381299529Smm#endif 382299529Smm 383299529Smm /* verify data corresponding to hole is '\0' */ 384299529Smm if (end > (const char *)buff + bytes_read) { 385299529Smm end = (const char *)buff + bytes_read; 386299529Smm } 387299529Smm assertMemoryFilledWith(start, end - start, '\0'); 388299529Smm start = end; 389299529Smm expected_offset += sparse->size; 390299529Smm ++sparse; 391299529Smm } else if (sparse->type == DATA) { 392299529Smm#if DEBUG 393299529Smm fprintf(stderr, " contained data expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); 394299529Smm#endif 395299529Smm /* verify data corresponding to hole is ' ' */ 396299529Smm if (assert(expected_offset + sparse->size <= offset + bytes_read)) { 397299529Smm assert(start == (const char *)buff + (size_t)(expected_offset - offset)); 398299529Smm assertMemoryFilledWith(start, end - start, ' '); 399299529Smm } 400299529Smm start = end; 401299529Smm expected_offset += sparse->size; 402299529Smm ++sparse; 403299529Smm } else { 404299529Smm break; 405299529Smm } 406299529Smm } 407299529Smm /* Block that overlaps end of data */ 408299529Smm if (expected_offset < offset + (int64_t)bytes_read) { 409299529Smm const char *end = (const char *)buff + bytes_read; 410299529Smm#if DEBUG 411299529Smm fprintf(stderr, " trailing overlap expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); 412299529Smm#endif 413299529Smm /* Must be a hole, overlap must be filled with '\0' */ 414299529Smm if (assert(sparse->type == HOLE)) { 415299529Smm assertMemoryFilledWith(start, end - start, '\0'); 416299529Smm } 417299529Smm } 418299529Smm last_offset = offset + bytes_read; 419231200Smm } 420299529Smm /* Count a hole at EOF? */ 421299529Smm if (last_offset < archive_entry_size(ae)) { 422299529Smm ++holes_seen; 423299529Smm } 424231200Smm 425299529Smm /* Verify blocks after last read */ 426299529Smm while (sparse->type == HOLE) { 427299529Smm expected_offset += sparse->size; 428299529Smm ++sparse; 429299529Smm } 430299529Smm assert(sparse->type == END); 431299529Smm assertEqualInt(expected_offset, archive_entry_size(ae)); 432299529Smm 433358088Smm failure("%s", path); 434299529Smm assertEqualInt(holes_seen, expected_holes); 435299529Smm 436231200Smm assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); 437231200Smm archive_entry_free(ae); 438231200Smm} 439231200Smm 440231200Smm#if defined(_WIN32) && !defined(__CYGWIN__) 441231200Smm#define close _close 442231200Smm#define open _open 443231200Smm#endif 444231200Smm 445231200Smm/* 446231200Smm * Sparse test without directory traversals. 447231200Smm */ 448231200Smmstatic void 449231200Smmverify_sparse_file2(struct archive *a, const char *path, 450231200Smm const struct sparse *sparse, int blocks, int preopen) 451231200Smm{ 452231200Smm struct archive_entry *ae; 453231200Smm int fd; 454231200Smm 455231200Smm (void)sparse; /* UNUSED */ 456231200Smm assert((ae = archive_entry_new()) != NULL); 457231200Smm archive_entry_set_pathname(ae, path); 458231200Smm if (preopen) 459231200Smm fd = open(path, O_RDONLY | O_BINARY); 460231200Smm else 461231200Smm fd = -1; 462231200Smm assertEqualIntA(a, ARCHIVE_OK, 463231200Smm archive_read_disk_entry_from_file(a, ae, fd, NULL)); 464231200Smm if (fd >= 0) 465231200Smm close(fd); 466231200Smm /* Verify the number of holes only, not its offset nor its 467231200Smm * length because those alignments are deeply dependence on 468231200Smm * its filesystem. */ 469358088Smm failure("%s", path); 470231200Smm assertEqualInt(blocks, archive_entry_sparse_count(ae)); 471231200Smm archive_entry_free(ae); 472231200Smm} 473231200Smm 474231200Smmstatic void 475358088Smmtest_sparse_whole_file_data(void) 476231200Smm{ 477231200Smm struct archive_entry *ae; 478231200Smm int64_t offset; 479231200Smm int i; 480231200Smm 481231200Smm assert((ae = archive_entry_new()) != NULL); 482231200Smm archive_entry_set_size(ae, 1024*10); 483231200Smm 484231200Smm /* 485231200Smm * Add sparse block data up to the file size. 486231200Smm */ 487231200Smm offset = 0; 488231200Smm for (i = 0; i < 10; i++) { 489231200Smm archive_entry_sparse_add_entry(ae, offset, 1024); 490231200Smm offset += 1024; 491231200Smm } 492231200Smm 493231200Smm failure("There should be no sparse"); 494231200Smm assertEqualInt(0, archive_entry_sparse_count(ae)); 495231200Smm archive_entry_free(ae); 496231200Smm} 497231200Smm 498231200SmmDEFINE_TEST(test_sparse_basic) 499231200Smm{ 500231200Smm char *cwd; 501231200Smm struct archive *a; 502348607Smm const char *skip_sparse_tests; 503231200Smm /* 504231200Smm * The alignment of the hole of sparse files deeply depends 505231200Smm * on filesystem. In my experience, sparse_file2 test with 506231200Smm * 204800 bytes hole size did not pass on ZFS and the result 507231200Smm * of that test seemed the size was too small, thus you should 508231200Smm * keep a hole size more than 409600 bytes to pass this test 509231200Smm * on all platform. 510231200Smm */ 511231200Smm const struct sparse sparse_file0[] = { 512337351Smm // 0 // 1024 513348607Smm { DATA, 1024 }, { HOLE, MIN_HOLE + 1638400 }, 514337351Smm // 2049024 // 2051072 515348607Smm { DATA, 2048 }, { HOLE, MIN_HOLE + 1638400 }, 516337351Smm // 4099072 // 4103168 517348607Smm { DATA, 4096 }, { HOLE, MIN_HOLE + 20070400 }, 518337351Smm // 24583168 // 24591360 519348607Smm { DATA, 8192 }, { HOLE, MIN_HOLE + 204390400 }, 520337351Smm // 229391360 // 229391361 521231200Smm { DATA, 1 }, { END, 0 } 522231200Smm }; 523231200Smm const struct sparse sparse_file1[] = { 524348607Smm { HOLE, MIN_HOLE }, { DATA, 1 }, 525348607Smm { HOLE, MIN_HOLE }, { DATA, 1 }, 526348607Smm { HOLE, MIN_HOLE }, { END, 0 } 527231200Smm }; 528231200Smm const struct sparse sparse_file2[] = { 529348607Smm { HOLE, MIN_HOLE }, { DATA, 1024 }, 530348607Smm { HOLE, MIN_HOLE + 409600 * 1 }, { DATA, 1024 }, 531348607Smm { HOLE, MIN_HOLE + 409600 * 2 }, { DATA, 1024 }, 532348607Smm { HOLE, MIN_HOLE + 409600 * 3 }, { DATA, 1024 }, 533348607Smm { HOLE, MIN_HOLE + 409600 * 4 }, { DATA, 1024 }, 534348607Smm { HOLE, MIN_HOLE + 409600 * 5 }, { DATA, 1024 }, 535348607Smm { HOLE, MIN_HOLE + 409600 * 6 }, { DATA, 1024 }, 536348607Smm { HOLE, MIN_HOLE + 409600 * 7 }, { DATA, 1024 }, 537348607Smm { HOLE, MIN_HOLE + 409600 * 8 }, { DATA, 1024 }, 538348607Smm { HOLE, MIN_HOLE + 409600 * 9}, { DATA, 1024 },/* 10 */ 539348607Smm { HOLE, MIN_HOLE }, { DATA, 1024 * 1 }, 540348607Smm { HOLE, MIN_HOLE + 409600 * 1 }, { DATA, 1024 * 2 }, 541348607Smm { HOLE, MIN_HOLE + 409600 * 2 }, { DATA, 1024 * 3 }, 542348607Smm { HOLE, MIN_HOLE + 409600 * 3 }, { DATA, 1024 * 4 }, 543348607Smm { HOLE, MIN_HOLE + 409600 * 4 }, { DATA, 1024 * 5 }, 544348607Smm { HOLE, MIN_HOLE + 409600 * 5 }, { DATA, 1024 * 6 }, 545348607Smm { HOLE, MIN_HOLE + 409600 * 6 }, { DATA, 1024 * 7 }, 546348607Smm { HOLE, MIN_HOLE + 409600 * 7 }, { DATA, 1024 * 8 }, 547348607Smm { HOLE, MIN_HOLE + 409600 * 8 }, { DATA, 1024 * 9 }, 548348607Smm { HOLE, MIN_HOLE + 409600 * 9}, { DATA, 1024 * 10},/* 20 */ 549231200Smm { END, 0 } 550231200Smm }; 551231200Smm const struct sparse sparse_file3[] = { 552299529Smm /* This hole size is too small to create a sparse file */ 553299529Smm { HOLE, 1 }, { DATA, 10240 }, 554299529Smm { HOLE, 1 }, { DATA, 10240 }, 555299529Smm { HOLE, 1 }, { DATA, 10240 }, 556231200Smm { END, 0 } 557231200Smm }; 558358088Smm const struct sparse sparse_file4[] = { 559358088Smm { DATA, 4096 }, { HOLE, 0xc0000000 }, 560358088Smm /* This hole overflows the offset if stored in 32 bits. */ 561358088Smm { DATA, 4096 }, { HOLE, 0x50000000 }, 562358088Smm { END, 0 } 563358088Smm }; 564231200Smm 565231200Smm /* 566231200Smm * Test for the case that sparse data indicates just the whole file 567231200Smm * data. 568231200Smm */ 569231200Smm test_sparse_whole_file_data(); 570231200Smm 571348607Smm skip_sparse_tests = getenv("SKIP_TEST_SPARSE"); 572348607Smm if (skip_sparse_tests != NULL) { 573348607Smm skipping("Skipping sparse tests due to SKIP_TEST_SPARSE " 574348607Smm "environment variable"); 575348607Smm return; 576348607Smm } 577348607Smm 578231200Smm /* Check if the filesystem where CWD on can 579231200Smm * report the number of the holes of a sparse file. */ 580370535Sgit2svn#if defined(PATH_MAX) && !defined(__GLIBC__) 581231200Smm cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ 582231200Smm#else 583231200Smm cwd = getcwd(NULL, 0); 584231200Smm#endif 585231200Smm if (!assert(cwd != NULL)) 586231200Smm return; 587231200Smm if (!is_sparse_supported(cwd)) { 588231200Smm free(cwd); 589231200Smm skipping("This filesystem or platform do not support " 590231200Smm "the reporting of the holes of a sparse file through " 591231200Smm "API such as lseek(HOLE)"); 592231200Smm return; 593231200Smm } 594231200Smm 595231200Smm /* 596231200Smm * Get sparse data through directory traversals. 597231200Smm */ 598231200Smm assert((a = archive_read_disk_new()) != NULL); 599231200Smm 600299529Smm verify_sparse_file(a, "file0", sparse_file0, 4); 601299529Smm verify_sparse_file(a, "file1", sparse_file1, 3); 602231200Smm verify_sparse_file(a, "file2", sparse_file2, 20); 603299529Smm /* Encoded non sparse; expect a data block but no sparse entries. */ 604231200Smm verify_sparse_file(a, "file3", sparse_file3, 0); 605358088Smm verify_sparse_file(a, "file4", sparse_file4, 2); 606231200Smm 607231200Smm assertEqualInt(ARCHIVE_OK, archive_read_free(a)); 608231200Smm 609231200Smm /* 610231200Smm * Get sparse data through archive_read_disk_entry_from_file(). 611231200Smm */ 612231200Smm assert((a = archive_read_disk_new()) != NULL); 613231200Smm 614231200Smm verify_sparse_file2(a, "file0", sparse_file0, 5, 0); 615231200Smm verify_sparse_file2(a, "file0", sparse_file0, 5, 1); 616231200Smm 617231200Smm assertEqualInt(ARCHIVE_OK, archive_read_free(a)); 618231200Smm free(cwd); 619231200Smm} 620299529Smm 621299529SmmDEFINE_TEST(test_fully_sparse_files) 622299529Smm{ 623299529Smm char *cwd; 624299529Smm struct archive *a; 625348607Smm const char *skip_sparse_tests; 626299529Smm 627299529Smm const struct sparse sparse_file[] = { 628348607Smm { HOLE, MIN_HOLE }, { END, 0 } 629299529Smm }; 630348607Smm 631348607Smm skip_sparse_tests = getenv("SKIP_TEST_SPARSE"); 632348607Smm if (skip_sparse_tests != NULL) { 633348607Smm skipping("Skipping sparse tests due to SKIP_TEST_SPARSE " 634348607Smm "environment variable"); 635348607Smm return; 636348607Smm } 637348607Smm 638299529Smm /* Check if the filesystem where CWD on can 639299529Smm * report the number of the holes of a sparse file. */ 640370535Sgit2svn#if defined(PATH_MAX) && !defined(__GLIBC__) 641299529Smm cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ 642299529Smm#else 643299529Smm cwd = getcwd(NULL, 0); 644299529Smm#endif 645299529Smm if (!assert(cwd != NULL)) 646299529Smm return; 647299529Smm if (!is_sparse_supported(cwd)) { 648299529Smm free(cwd); 649299529Smm skipping("This filesystem or platform do not support " 650299529Smm "the reporting of the holes of a sparse file through " 651299529Smm "API such as lseek(HOLE)"); 652299529Smm return; 653299529Smm } 654299529Smm 655299529Smm assert((a = archive_read_disk_new()) != NULL); 656299529Smm 657299529Smm /* Fully sparse files are encoded with a zero-length "data" block. */ 658299529Smm verify_sparse_file(a, "file0", sparse_file, 1); 659299529Smm 660299529Smm assertEqualInt(ARCHIVE_OK, archive_read_free(a)); 661299529Smm free(cwd); 662299529Smm} 663