From: Sergey Poznyakoff Date: Mon, 17 Nov 2003 11:04:16 +0000 (+0000) Subject: (sparse_diff_file): New function X-Git-Url: https://git.brokenzipper.com/gitweb?a=commitdiff_plain;h=a8b2b68c33e6166c8035f094574fc0d1d3459cd8;p=chaz%2Ftar (sparse_diff_file): New function --- diff --git a/src/sparse.c b/src/sparse.c index 4186254..4f936dc 100644 --- a/src/sparse.c +++ b/src/sparse.c @@ -130,8 +130,8 @@ zero_block_p (char *buffer, size_t size) { while (size--) if (*buffer++) - return 0; - return 1; + return false; + return true; } #define clear_block(p) memset (p, 0, BLOCKSIZE); @@ -374,6 +374,117 @@ sparse_extract_file (int fd, struct tar_stat_info *stat, off_t *size) return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short; } + +static char diff_buffer[BLOCKSIZE]; + +static bool +check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end) +{ + if (!lseek_or_error (file, beg, SEEK_SET)) + return false; + + while (beg < end) + { + size_t bytes_read; + size_t rdsize = end - beg; + + if (rdsize > BLOCKSIZE) + rdsize = BLOCKSIZE; + clear_block (diff_buffer); + bytes_read = safe_read (file->fd, diff_buffer, rdsize); + if (bytes_read < 0) + { + read_diag_details (file->stat_info->orig_file_name, + beg, + rdsize); + return false; + } + if (!zero_block_p (diff_buffer, bytes_read)) + { + report_difference (file->stat_info, + _("File fragment at %lu is not a hole"), beg); + return false; + } + + beg += bytes_read; + } + return true; +} + +static bool +check_data_region (struct tar_sparse_file *file, size_t index) +{ + size_t size_left; + + if (!lseek_or_error (file, file->stat_info->sparse_map[index].offset, + SEEK_SET)) + return false; + size_left = file->stat_info->sparse_map[index].numbytes; + while (size_left > 0) + { + size_t bytes_read; + size_t rdsize = (size_left > BLOCKSIZE) ? BLOCKSIZE : size_left; + + union block *blk = find_next_block (); + if (!blk) + { + ERROR ((0, 0, _("Unexpected EOF in archive"))); + return false; + } + set_next_block_after (blk); + bytes_read = safe_read (file->fd, diff_buffer, rdsize); + if (bytes_read < 0) + { + read_diag_details (file->stat_info->orig_file_name, + file->stat_info->sparse_map[index].offset + + file->stat_info->sparse_map[index].numbytes + - size_left, + rdsize); + return false; + } + file->dumped_size += bytes_read; + size_left -= bytes_read; + if (memcmp (blk->buffer, diff_buffer, rdsize)) + { + report_difference (file->stat_info, _("Contents differ")); + return false; + } + } + return true; +} + +bool +sparse_diff_file (int fd, struct tar_stat_info *stat) +{ + bool rc = true; + struct tar_sparse_file file; + size_t i; + off_t offset = 0; + + file.stat_info = stat; + file.fd = fd; + + if (!sparse_select_optab (&file) + || !tar_sparse_init (&file)) + return dump_status_not_implemented; + + rc = tar_sparse_decode_header (&file); + for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++) + { + rc = check_sparse_region (&file, + offset, file.stat_info->sparse_map[i].offset) + && check_data_region (&file, i); + offset = file.stat_info->sparse_map[i].offset + + file.stat_info->sparse_map[i].numbytes; + } + + if (rc) + skip_file (file.stat_info->archive_file_size - file.dumped_size); + + tar_sparse_done (&file); + return rc; +} + /* Old GNU Format. The sparse file information is stored in the oldgnu_header in the following manner: