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