1 /*- 2 * Copyright (c) 2010-2012 Michihiro NAKAJIMA 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 #include "test.h" 26 27 #ifdef HAVE_SYS_IOCTL_H 28 #include <sys/ioctl.h> 29 #endif 30 #ifdef HAVE_SYS_PARAM_H 31 #include <sys/param.h> 32 #endif 33 #ifdef HAVE_FCNTL_H 34 #include <fcntl.h> 35 #endif 36 #ifdef HAVE_LIMITS_H 37 #include <limits.h> 38 #endif 39 #ifdef HAVE_UNISTD_H 40 #include <unistd.h> 41 #endif 42 #ifdef HAVE_LINUX_TYPES_H 43 #include <linux/types.h> 44 #endif 45 #ifdef HAVE_LINUX_FIEMAP_H 46 #include <linux/fiemap.h> 47 #endif 48 #ifdef HAVE_LINUX_FS_H 49 #include <linux/fs.h> 50 #endif 51 52 /* The logic to compare sparse file data read from disk with the 53 * specification is a little involved. Set to 1 to have the progress 54 * dumped. */ 55 #define DEBUG 0 56 57 /* 58 * NOTE: On FreeBSD and Solaris, this test needs ZFS. 59 * You may perform this test as 60 * 'TMPDIR=<a directory on the ZFS> libarchive_test'. 61 */ 62 63 struct sparse { 64 enum { DATA, HOLE, END } type; 65 size_t size; 66 }; 67 68 static void create_sparse_file(const char *, const struct sparse *); 69 70 #if defined(__APPLE__) 71 /* On APFS holes need to be at least 4096x4097 bytes */ 72 #define MIN_HOLE 16781312 73 #else 74 /* Elsewhere we work with 4096*10 bytes */ 75 #define MIN_HOLE 409600 76 #endif 77 78 #if defined(_WIN32) && !defined(__CYGWIN__) 79 #include <winioctl.h> 80 /* 81 * Create a sparse file on Windows. 82 */ 83 84 #if !defined(PATH_MAX) 85 #define PATH_MAX MAX_PATH 86 #endif 87 #if !defined(__BORLANDC__) 88 #define getcwd _getcwd 89 #endif 90 91 static int 92 is_sparse_supported(const char *path) 93 { 94 char root[MAX_PATH+1]; 95 char vol[MAX_PATH+1]; 96 char sys[MAX_PATH+1]; 97 DWORD flags; 98 BOOL r; 99 100 strncpy(root, path, sizeof(root)-1); 101 if (((root[0] >= 'c' && root[0] <= 'z') || 102 (root[0] >= 'C' && root[0] <= 'Z')) && 103 root[1] == ':' && 104 (root[2] == '\\' || root[2] == '/')) 105 root[3] = '\0'; 106 else 107 return (0); 108 assertEqualInt((r = GetVolumeInformation(root, vol, 109 sizeof(vol), NULL, NULL, &flags, sys, sizeof(sys))), 1); 110 return (r != 0 && (flags & FILE_SUPPORTS_SPARSE_FILES) != 0); 111 } 112 113 static void 114 create_sparse_file(const char *path, const struct sparse *s) 115 { 116 char buff[1024]; 117 HANDLE handle; 118 DWORD dmy; 119 120 memset(buff, ' ', sizeof(buff)); 121 122 handle = CreateFileA(path, GENERIC_WRITE, 0, 123 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 124 NULL); 125 assert(handle != INVALID_HANDLE_VALUE); 126 assert(DeviceIoControl(handle, FSCTL_SET_SPARSE, NULL, 0, 127 NULL, 0, &dmy, NULL) != 0); 128 129 uint64_t offsetSoFar = 0; 130 131 while (s->type != END) { 132 if (s->type == HOLE) { 133 LARGE_INTEGER fileOffset, beyondOffset, distanceToMove; 134 fileOffset.QuadPart = offsetSoFar; 135 beyondOffset.QuadPart = offsetSoFar + s->size; 136 distanceToMove.QuadPart = s->size; 137 138 FILE_ZERO_DATA_INFORMATION zeroInformation; 139 zeroInformation.FileOffset = fileOffset; 140 zeroInformation.BeyondFinalZero = beyondOffset; 141 142 DWORD bytesReturned; 143 assert(SetFilePointerEx(handle, distanceToMove, 144 NULL, FILE_CURRENT) != 0); 145 assert(SetEndOfFile(handle) != 0); 146 assert(DeviceIoControl(handle, FSCTL_SET_ZERO_DATA, &zeroInformation, 147 sizeof(FILE_ZERO_DATA_INFORMATION), NULL, 0, &bytesReturned, NULL) != 0); 148 } else { 149 DWORD w, wr; 150 size_t size; 151 152 size = s->size; 153 while (size) { 154 if (size > sizeof(buff)) 155 w = sizeof(buff); 156 else 157 w = (DWORD)size; 158 assert(WriteFile(handle, buff, w, &wr, NULL) != 0); 159 size -= wr; 160 } 161 } 162 offsetSoFar += s->size; 163 s++; 164 } 165 assertEqualInt(CloseHandle(handle), 1); 166 } 167 168 #else 169 170 #if defined(HAVE_LINUX_FIEMAP_H) 171 /* 172 * FIEMAP, which can detect 'hole' of a sparse file, has 173 * been supported from 2.6.28 174 */ 175 176 static int 177 is_sparse_supported_fiemap(const char *path) 178 { 179 const struct sparse sparse_file[] = { 180 /* This hole size is too small to create a sparse 181 * files for almost filesystem. */ 182 { HOLE, 1024 }, { DATA, 10240 }, 183 { END, 0 } 184 }; 185 int fd, r; 186 struct fiemap *fm; 187 char buff[1024]; 188 const char *testfile = "can_sparse"; 189 190 (void)path; /* UNUSED */ 191 memset(buff, 0, sizeof(buff)); 192 create_sparse_file(testfile, sparse_file); 193 fd = open(testfile, O_RDWR); 194 if (fd < 0) 195 return (0); 196 fm = (struct fiemap *)buff; 197 fm->fm_start = 0; 198 fm->fm_length = ~0ULL;; 199 fm->fm_flags = FIEMAP_FLAG_SYNC; 200 fm->fm_extent_count = (sizeof(buff) - sizeof(*fm))/ 201 sizeof(struct fiemap_extent); 202 r = ioctl(fd, FS_IOC_FIEMAP, fm); 203 close(fd); 204 unlink(testfile); 205 return (r >= 0); 206 } 207 208 #if !defined(SEEK_HOLE) || !defined(SEEK_DATA) 209 static int 210 is_sparse_supported(const char *path) 211 { 212 return is_sparse_supported_fiemap(path); 213 } 214 #endif 215 #endif 216 217 #if defined(_PC_MIN_HOLE_SIZE) 218 219 /* 220 * FreeBSD and Solaris can detect 'hole' of a sparse file 221 * through lseek(HOLE) on ZFS. (UFS does not support yet) 222 */ 223 224 static int 225 is_sparse_supported(const char *path) 226 { 227 return (pathconf(path, _PC_MIN_HOLE_SIZE) > 0); 228 } 229 230 #elif defined(SEEK_HOLE) && defined(SEEK_DATA) 231 232 static int 233 is_sparse_supported(const char *path) 234 { 235 const struct sparse sparse_file[] = { 236 /* This hole size is too small to create a sparse 237 * files for almost filesystem. */ 238 { HOLE, 1024 }, { DATA, 10240 }, 239 { END, 0 } 240 }; 241 int fd, r; 242 const char *testfile = "can_sparse"; 243 244 (void)path; /* UNUSED */ 245 create_sparse_file(testfile, sparse_file); 246 fd = open(testfile, O_RDWR); 247 if (fd < 0) 248 return (0); 249 r = lseek(fd, 0, SEEK_HOLE); 250 close(fd); 251 unlink(testfile); 252 #if defined(HAVE_LINUX_FIEMAP_H) 253 if (r < 0) 254 return (is_sparse_supported_fiemap(path)); 255 return (1); 256 #else 257 return (r >= 0); 258 #endif 259 } 260 261 #elif !defined(HAVE_LINUX_FIEMAP_H) 262 263 /* 264 * Other system may do not have the API such as lseek(HOLE), 265 * which detect 'hole' of a sparse file. 266 */ 267 268 static int 269 is_sparse_supported(const char *path) 270 { 271 (void)path; /* UNUSED */ 272 return (0); 273 } 274 275 #endif 276 277 /* 278 * Create a sparse file on POSIX like system. 279 */ 280 281 static void 282 create_sparse_file(const char *path, const struct sparse *s) 283 { 284 char buff[1024]; 285 int fd; 286 uint64_t total_size = 0; 287 const struct sparse *cur = s; 288 289 memset(buff, ' ', sizeof(buff)); 290 assert((fd = open(path, O_CREAT | O_WRONLY, 0600)) != -1); 291 292 /* Handle holes at the end by extending the file */ 293 while (cur->type != END) { 294 total_size += cur->size; 295 ++cur; 296 } 297 assert(ftruncate(fd, total_size) != -1); 298 299 while (s->type != END) { 300 if (s->type == HOLE) { 301 assert(lseek(fd, s->size, SEEK_CUR) != (off_t)-1); 302 } else { 303 size_t w, size; 304 305 size = s->size; 306 while (size) { 307 if (size > sizeof(buff)) 308 w = sizeof(buff); 309 else 310 w = size; 311 assert(write(fd, buff, w) != (ssize_t)-1); 312 size -= w; 313 } 314 } 315 s++; 316 } 317 close(fd); 318 } 319 320 #endif 321 322 /* 323 * Sparse test with directory traversals. 324 */ 325 static void 326 verify_sparse_file(struct archive *a, const char *path, 327 const struct sparse *sparse, int expected_holes) 328 { 329 struct archive_entry *ae; 330 const void *buff; 331 size_t bytes_read; 332 int64_t offset, expected_offset, last_offset; 333 int holes_seen = 0; 334 335 create_sparse_file(path, sparse); 336 assert((ae = archive_entry_new()) != NULL); 337 assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, path)); 338 assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); 339 340 expected_offset = 0; 341 last_offset = 0; 342 while (ARCHIVE_OK == archive_read_data_block(a, &buff, &bytes_read, 343 &offset)) { 344 const char *start = buff; 345 #if DEBUG 346 fprintf(stderr, "%s: bytes_read=%d offset=%d\n", path, (int)bytes_read, (int)offset); 347 #endif 348 if (offset > last_offset) { 349 ++holes_seen; 350 } 351 /* Blocks entirely before the data we just read. */ 352 while (expected_offset + (int64_t)sparse->size < offset) { 353 #if DEBUG 354 fprintf(stderr, " skipping expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); 355 #endif 356 /* Must be holes. */ 357 assert(sparse->type == HOLE); 358 expected_offset += sparse->size; 359 ++sparse; 360 } 361 /* Block that overlaps beginning of data */ 362 if (expected_offset < offset 363 && expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) { 364 const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size; 365 #if DEBUG 366 fprintf(stderr, " overlapping hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); 367 #endif 368 if (sparse->type == HOLE) { 369 assertMemoryFilledWith(start, end - start, '\0'); 370 } else if (assert(sparse->type == DATA)) { 371 assertMemoryFilledWith(start, end - start, ' '); 372 } 373 start = end; 374 expected_offset += sparse->size; 375 ++sparse; 376 } 377 /* Blocks completely contained in data we just read. */ 378 while (expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) { 379 const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size; 380 if (sparse->type == HOLE) { 381 #if DEBUG 382 fprintf(stderr, " contained hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); 383 #endif 384 385 /* verify data corresponding to hole is '\0' */ 386 if (end > (const char *)buff + bytes_read) { 387 end = (const char *)buff + bytes_read; 388 } 389 assertMemoryFilledWith(start, end - start, '\0'); 390 start = end; 391 expected_offset += sparse->size; 392 ++sparse; 393 } else if (sparse->type == DATA) { 394 #if DEBUG 395 fprintf(stderr, " contained data expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); 396 #endif 397 /* verify data corresponding to hole is ' ' */ 398 if (assert(expected_offset + sparse->size <= offset + bytes_read)) { 399 assert(start == (const char *)buff + (size_t)(expected_offset - offset)); 400 assertMemoryFilledWith(start, end - start, ' '); 401 } 402 start = end; 403 expected_offset += sparse->size; 404 ++sparse; 405 } else { 406 break; 407 } 408 } 409 /* Block that overlaps end of data */ 410 if (expected_offset < offset + (int64_t)bytes_read) { 411 const char *end = (const char *)buff + bytes_read; 412 #if DEBUG 413 fprintf(stderr, " trailing overlap expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); 414 #endif 415 if (sparse->type == HOLE) { 416 assertMemoryFilledWith(start, end - start, '\0'); 417 } else if (assert(sparse->type == DATA)) { 418 assertMemoryFilledWith(start, end - start, ' '); 419 } 420 } 421 last_offset = offset + bytes_read; 422 } 423 /* Count a hole at EOF? */ 424 if (last_offset < archive_entry_size(ae)) { 425 ++holes_seen; 426 } 427 428 /* Verify blocks after last read */ 429 while (sparse->type == HOLE) { 430 expected_offset += sparse->size; 431 ++sparse; 432 } 433 assert(sparse->type == END); 434 assertEqualInt(expected_offset, archive_entry_size(ae)); 435 436 failure("%s", path); 437 assertEqualInt(holes_seen, expected_holes); 438 439 assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); 440 archive_entry_free(ae); 441 } 442 443 #if defined(_WIN32) && !defined(__CYGWIN__) 444 #define close _close 445 #define open _open 446 #endif 447 448 /* 449 * Sparse test without directory traversals. 450 */ 451 static void 452 verify_sparse_file2(struct archive *a, const char *path, 453 const struct sparse *sparse, int blocks, int preopen) 454 { 455 struct archive_entry *ae; 456 int fd; 457 458 (void)sparse; /* UNUSED */ 459 assert((ae = archive_entry_new()) != NULL); 460 archive_entry_set_pathname(ae, path); 461 if (preopen) 462 fd = open(path, O_RDONLY | O_BINARY); 463 else 464 fd = -1; 465 assertEqualIntA(a, ARCHIVE_OK, 466 archive_read_disk_entry_from_file(a, ae, fd, NULL)); 467 if (fd >= 0) 468 close(fd); 469 /* Verify the number of holes only, not its offset nor its 470 * length because those alignments are deeply dependence on 471 * its filesystem. */ 472 failure("%s", path); 473 assertEqualInt(blocks, archive_entry_sparse_count(ae)); 474 archive_entry_free(ae); 475 } 476 477 static void 478 test_sparse_whole_file_data(void) 479 { 480 struct archive_entry *ae; 481 int64_t offset; 482 int i; 483 484 assert((ae = archive_entry_new()) != NULL); 485 archive_entry_set_size(ae, 1024*10); 486 487 /* 488 * Add sparse block data up to the file size. 489 */ 490 offset = 0; 491 for (i = 0; i < 10; i++) { 492 archive_entry_sparse_add_entry(ae, offset, 1024); 493 offset += 1024; 494 } 495 496 failure("There should be no sparse"); 497 assertEqualInt(0, archive_entry_sparse_count(ae)); 498 archive_entry_free(ae); 499 } 500 501 DEFINE_TEST(test_sparse_basic) 502 { 503 char *cwd; 504 struct archive *a; 505 const char *skip_sparse_tests; 506 /* 507 * The alignment of the hole of sparse files deeply depends 508 * on filesystem. In my experience, sparse_file2 test with 509 * 204800 bytes hole size did not pass on ZFS and the result 510 * of that test seemed the size was too small, thus you should 511 * keep a hole size more than 409600 bytes to pass this test 512 * on all platform. 513 */ 514 const struct sparse sparse_file0[] = { 515 // 0 // 1024 516 { DATA, 1024 }, { HOLE, MIN_HOLE + 1638400 }, 517 // 2049024 // 2051072 518 { DATA, 2048 }, { HOLE, MIN_HOLE + 1638400 }, 519 // 4099072 // 4103168 520 { DATA, 4096 }, { HOLE, MIN_HOLE + 20070400 }, 521 // 24583168 // 24591360 522 { DATA, 8192 }, { HOLE, MIN_HOLE + 204390400 }, 523 // 229391360 // 229391361 524 { DATA, 1 }, { END, 0 } 525 }; 526 const struct sparse sparse_file1[] = { 527 { HOLE, MIN_HOLE }, { DATA, 1 }, 528 { HOLE, MIN_HOLE }, { DATA, 1 }, 529 { HOLE, MIN_HOLE }, { END, 0 } 530 }; 531 const struct sparse sparse_file2[] = { 532 { HOLE, MIN_HOLE }, { DATA, 1024 }, 533 { HOLE, MIN_HOLE + 409600 * 1 }, { DATA, 1024 }, 534 { HOLE, MIN_HOLE + 409600 * 2 }, { DATA, 1024 }, 535 { HOLE, MIN_HOLE + 409600 * 3 }, { DATA, 1024 }, 536 { HOLE, MIN_HOLE + 409600 * 4 }, { DATA, 1024 }, 537 { HOLE, MIN_HOLE + 409600 * 5 }, { DATA, 1024 }, 538 { HOLE, MIN_HOLE + 409600 * 6 }, { DATA, 1024 }, 539 { HOLE, MIN_HOLE + 409600 * 7 }, { DATA, 1024 }, 540 { HOLE, MIN_HOLE + 409600 * 8 }, { DATA, 1024 }, 541 { HOLE, MIN_HOLE + 409600 * 9}, { DATA, 1024 },/* 10 */ 542 { HOLE, MIN_HOLE }, { DATA, 1024 * 1 }, 543 { HOLE, MIN_HOLE + 409600 * 1 }, { DATA, 1024 * 2 }, 544 { HOLE, MIN_HOLE + 409600 * 2 }, { DATA, 1024 * 3 }, 545 { HOLE, MIN_HOLE + 409600 * 3 }, { DATA, 1024 * 4 }, 546 { HOLE, MIN_HOLE + 409600 * 4 }, { DATA, 1024 * 5 }, 547 { HOLE, MIN_HOLE + 409600 * 5 }, { DATA, 1024 * 6 }, 548 { HOLE, MIN_HOLE + 409600 * 6 }, { DATA, 1024 * 7 }, 549 { HOLE, MIN_HOLE + 409600 * 7 }, { DATA, 1024 * 8 }, 550 { HOLE, MIN_HOLE + 409600 * 8 }, { DATA, 1024 * 9 }, 551 { HOLE, MIN_HOLE + 409600 * 9}, { DATA, 1024 * 10},/* 20 */ 552 { END, 0 } 553 }; 554 const struct sparse sparse_file3[] = { 555 /* This hole size is too small to create a sparse file */ 556 { HOLE, 1 }, { DATA, 10240 }, 557 { HOLE, 1 }, { DATA, 10240 }, 558 { HOLE, 1 }, { DATA, 10240 }, 559 { END, 0 } 560 }; 561 const struct sparse sparse_file4[] = { 562 { DATA, 4096 }, { HOLE, 0xc0000000 }, 563 /* This hole overflows the offset if stored in 32 bits. */ 564 { DATA, 4096 }, { HOLE, 0x50000000 }, 565 { END, 0 } 566 }; 567 568 /* 569 * Test for the case that sparse data indicates just the whole file 570 * data. 571 */ 572 test_sparse_whole_file_data(); 573 574 skip_sparse_tests = getenv("SKIP_TEST_SPARSE"); 575 if (skip_sparse_tests != NULL) { 576 skipping("Skipping sparse tests due to SKIP_TEST_SPARSE " 577 "environment variable"); 578 return; 579 } 580 581 /* Check if the filesystem where CWD on can 582 * report the number of the holes of a sparse file. */ 583 #if defined(PATH_MAX) && !defined(__GLIBC__) 584 cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ 585 #else 586 cwd = getcwd(NULL, 0); 587 #endif 588 if (!assert(cwd != NULL)) 589 return; 590 if (!is_sparse_supported(cwd)) { 591 free(cwd); 592 skipping("This filesystem or platform do not support " 593 "the reporting of the holes of a sparse file through " 594 "API such as lseek(HOLE)"); 595 return; 596 } 597 598 /* 599 * Get sparse data through directory traversals. 600 */ 601 assert((a = archive_read_disk_new()) != NULL); 602 603 verify_sparse_file(a, "file0", sparse_file0, 4); 604 verify_sparse_file(a, "file1", sparse_file1, 3); 605 verify_sparse_file(a, "file2", sparse_file2, 20); 606 /* Encoded non sparse; expect a data block but no sparse entries. */ 607 verify_sparse_file(a, "file3", sparse_file3, 0); 608 verify_sparse_file(a, "file4", sparse_file4, 2); 609 610 assertEqualInt(ARCHIVE_OK, archive_read_free(a)); 611 612 /* 613 * Get sparse data through archive_read_disk_entry_from_file(). 614 */ 615 assert((a = archive_read_disk_new()) != NULL); 616 617 verify_sparse_file2(a, "file0", sparse_file0, 5, 0); 618 verify_sparse_file2(a, "file0", sparse_file0, 5, 1); 619 620 assertEqualInt(ARCHIVE_OK, archive_read_free(a)); 621 622 /* 623 * Test that setting ARCHIVE_READDISK_NO_SPARSE 624 * creates no sparse entries. 625 */ 626 assert((a = archive_read_disk_new()) != NULL); 627 628 assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_behavior(a, 629 ARCHIVE_READDISK_NO_SPARSE)); 630 631 verify_sparse_file(a, "file0", sparse_file0, 0); 632 verify_sparse_file(a, "file1", sparse_file1, 0); 633 verify_sparse_file(a, "file2", sparse_file2, 0); 634 verify_sparse_file(a, "file3", sparse_file3, 0); 635 verify_sparse_file(a, "file4", sparse_file4, 0); 636 637 assertEqualInt(ARCHIVE_OK, archive_read_free(a)); 638 639 assert((a = archive_read_disk_new()) != NULL); 640 641 assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_behavior(a, 642 ARCHIVE_READDISK_NO_SPARSE)); 643 644 verify_sparse_file2(a, "file0", sparse_file0, 0, 0); 645 verify_sparse_file2(a, "file0", sparse_file0, 0, 1); 646 647 assertEqualInt(ARCHIVE_OK, archive_read_free(a)); 648 free(cwd); 649 } 650 651 DEFINE_TEST(test_fully_sparse_files) 652 { 653 char *cwd; 654 struct archive *a; 655 const char *skip_sparse_tests; 656 657 const struct sparse sparse_file[] = { 658 { HOLE, MIN_HOLE }, { END, 0 } 659 }; 660 661 skip_sparse_tests = getenv("SKIP_TEST_SPARSE"); 662 if (skip_sparse_tests != NULL) { 663 skipping("Skipping sparse tests due to SKIP_TEST_SPARSE " 664 "environment variable"); 665 return; 666 } 667 668 /* Check if the filesystem where CWD on can 669 * report the number of the holes of a sparse file. */ 670 #if defined(PATH_MAX) && !defined(__GLIBC__) 671 cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ 672 #else 673 cwd = getcwd(NULL, 0); 674 #endif 675 if (!assert(cwd != NULL)) 676 return; 677 if (!is_sparse_supported(cwd)) { 678 free(cwd); 679 skipping("This filesystem or platform do not support " 680 "the reporting of the holes of a sparse file through " 681 "API such as lseek(HOLE)"); 682 return; 683 } 684 685 assert((a = archive_read_disk_new()) != NULL); 686 687 /* Fully sparse files are encoded with a zero-length "data" block. */ 688 verify_sparse_file(a, "file0", sparse_file, 1); 689 690 assertEqualInt(ARCHIVE_OK, archive_read_free(a)); 691 free(cwd); 692 } 693