static int is_sparse_supported(const char *path) { const struct sparse sparse_file[] = { /* This hole size is too small to create a sparse * files for almost filesystem. */ { HOLE, 1024 }, { DATA, 10240 }, { END, 0 } }; int fd, r; const char *testfile = "can_sparse"; (void)path; /* UNUSED */ create_sparse_file(testfile, sparse_file); fd = open(testfile, O_RDWR); if (fd < 0) return (0); r = lseek(fd, 0, SEEK_HOLE); close(fd); unlink(testfile); #if defined(HAVE_LINUX_FIEMAP_H) if (r < 0) return (is_sparse_supported_fiemap(path)); #endif return (r >= 0); }
static int is_sparse_supported_fiemap(const char *path) { const struct sparse sparse_file[] = { /* This hole size is too small to create a sparse * files for almost filesystem. */ { HOLE, 1024 }, { DATA, 10240 }, { END, 0 } }; int fd, r; struct fiemap *fm; char buff[1024]; const char *testfile = "can_sparse"; (void)path; /* UNUSED */ memset(buff, 0, sizeof(buff)); create_sparse_file(testfile, sparse_file); fd = open(testfile, O_RDWR); if (fd < 0) return (0); fm = (struct fiemap *)buff; fm->fm_start = 0; fm->fm_length = ~0ULL;; fm->fm_flags = FIEMAP_FLAG_SYNC; fm->fm_extent_count = (sizeof(buff) - sizeof(*fm))/ sizeof(struct fiemap_extent); r = ioctl(fd, FS_IOC_FIEMAP, fm); close(fd); unlink(testfile); return (r >= 0); }
static int is_sparse_supported(const char *path) { const struct sparse sparse_file[] = { /* This hole size is too small to create a sparse * files for almost filesystem. */ { HOLE, 1024 }, { DATA, 10240 }, { END, 0 } }; struct utsname ut; char *p, *e; long d; int fd, r; struct fiemap *fm; char buff[1024]; const char *testfile = "can_sparse"; memset(&ut, 0, sizeof(ut)); assertEqualInt(uname(&ut), 0); p = ut.release; d = strtol(p, &e, 10); if (d < 2 || *e != '.') return (0); p = e + 1; d = strtol(p, &e, 10); if (d < 6 || *e != '.') return (0); p = e + 1; d = strtol(p, NULL, 10); if (d < 28) return (0); create_sparse_file(testfile, sparse_file); fd = open(testfile, O_RDWR); if (fd < 0) return (0); fm = (struct fiemap *)buff; fm->fm_start = 0; fm->fm_length = ~0ULL;; fm->fm_flags = FIEMAP_FLAG_SYNC; fm->fm_extent_count = (sizeof(buff) - sizeof(*fm))/ sizeof(struct fiemap_extent); r = ioctl(fd, FS_IOC_FIEMAP, fm); if (r < 0 && (errno == ENOTTY || errno == EOPNOTSUPP)) return (0);/* Not supported. */ close(fd); unlink(testfile); return (1); }
int main(int argc, const char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: %s filename len\n", argv[0]); exit(1); } int i = 1; while (i < argc && argv[i][0] == '-') { switch (argv[i][1]) { case 'v': Opt_verbose = 1; break; case 's': Opt_sparse = 1; break; case 'f': Opt_force = 1; break; default: out_err("Unknown option: \'%c\'.", argv[i][1]); exit(2); } ++i; } const char *filename = argv[i]; long long len = atoll(argv[i + 1]); if (len < 0) { out_err("Invalid file length: %lld.\n", len); exit(3); } if (create_sparse_file(filename, len) < 0) { out_err("File creation failed."); exit(4); } if (Opt_verbose) print_file_size(filename); return 0; }
/* * Sparse test with directory traversals. */ static void verify_sparse_file(struct archive *a, const char *path, const struct sparse *sparse, int blocks, int expected_data_blocks) { struct archive_entry *ae; const void *buff; size_t bytes_read; int64_t offset; int64_t total; int data_blocks, hole; create_sparse_file(path, sparse); assert((ae = archive_entry_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, path)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); /* Verify the number of holes only, not its offset nor its * length because those alignments are deeply dependence on * its filesystem. */ assertEqualInt(blocks, archive_entry_sparse_count(ae)); total = 0; data_blocks = 0; hole = 0; while (ARCHIVE_OK == archive_read_data_block(a, &buff, &bytes_read, &offset)) { if (offset > total || offset == 0) { if (offset > total) hole = 1; data_blocks++; } total = offset + bytes_read; } assertEqualInt(expected_data_blocks, data_blocks); if (blocks > 1) assert(hole); /* There must be a hole if > 1 blocks were encoded */ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); archive_entry_free(ae); }
/* * Sparse test with directory traversals. */ static void verify_sparse_file(struct archive *a, const char *path, const struct sparse *sparse, int expected_holes) { struct archive_entry *ae; const void *buff; size_t bytes_read; int64_t offset, expected_offset, last_offset; int holes_seen = 0; create_sparse_file(path, sparse); assert((ae = archive_entry_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, path)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); expected_offset = 0; last_offset = 0; while (ARCHIVE_OK == archive_read_data_block(a, &buff, &bytes_read, &offset)) { const char *start = buff; #if DEBUG fprintf(stderr, "%s: bytes_read=%d offset=%d\n", path, (int)bytes_read, (int)offset); #endif if (offset > last_offset) { ++holes_seen; } /* Blocks entirely before the data we just read. */ while (expected_offset + (int64_t)sparse->size < offset) { #if DEBUG fprintf(stderr, " skipping expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); #endif /* Must be holes. */ assert(sparse->type == HOLE); expected_offset += sparse->size; ++sparse; } /* Block that overlaps beginning of data */ if (expected_offset < offset && expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) { const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size; #if DEBUG fprintf(stderr, " overlapping hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); #endif /* Must be a hole, overlap must be filled with '\0' */ if (assert(sparse->type == HOLE)) { assertMemoryFilledWith(start, end - start, '\0'); } start = end; expected_offset += sparse->size; ++sparse; } /* Blocks completely contained in data we just read. */ while (expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) { const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size; if (sparse->type == HOLE) { #if DEBUG fprintf(stderr, " contained hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); #endif /* verify data corresponding to hole is '\0' */ if (end > (const char *)buff + bytes_read) { end = (const char *)buff + bytes_read; } assertMemoryFilledWith(start, end - start, '\0'); start = end; expected_offset += sparse->size; ++sparse; } else if (sparse->type == DATA) { #if DEBUG fprintf(stderr, " contained data expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); #endif /* verify data corresponding to hole is ' ' */ if (assert(expected_offset + sparse->size <= offset + bytes_read)) { assert(start == (const char *)buff + (size_t)(expected_offset - offset)); assertMemoryFilledWith(start, end - start, ' '); } start = end; expected_offset += sparse->size; ++sparse; } else { break; } } /* Block that overlaps end of data */ if (expected_offset < offset + (int64_t)bytes_read) { const char *end = (const char *)buff + bytes_read; #if DEBUG fprintf(stderr, " trailing overlap expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); #endif /* Must be a hole, overlap must be filled with '\0' */ if (assert(sparse->type == HOLE)) { assertMemoryFilledWith(start, end - start, '\0'); } } last_offset = offset + bytes_read; } /* Count a hole at EOF? */ if (last_offset < archive_entry_size(ae)) { ++holes_seen; } /* Verify blocks after last read */ while (sparse->type == HOLE) { expected_offset += sparse->size; ++sparse; } assert(sparse->type == END); assertEqualInt(expected_offset, archive_entry_size(ae)); assertEqualInt(holes_seen, expected_holes); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); archive_entry_free(ae); }