X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fsparse.c;h=30bf03157c962c09f51fc8b8be07eb6f287e1000;hb=d75890c1b8bfbab65c32eff6a264766579208ba1;hp=ecbbe108842bbd27366b3d16a470fc4e047a474e;hpb=040b5ab5f969f9ee6c69a54bc284e2ff57b86556;p=chaz%2Ftar diff --git a/src/sparse.c b/src/sparse.c index ecbbe10..30bf031 100644 --- a/src/sparse.c +++ b/src/sparse.c @@ -22,6 +22,7 @@ #include "common.h" struct tar_sparse_file; +static bool sparse_select_optab (struct tar_sparse_file *file); enum sparse_scan_state { @@ -53,7 +54,7 @@ struct tar_sparse_file off_t dumped_size; /* Number of bytes actually written to the archive */ struct tar_stat_info *stat_info; /* Information about the file */ - struct tar_sparse_optab const *optab; + struct tar_sparse_optab const *optab; /* Operation table */ void *closure; /* Any additional data optab calls might require */ }; @@ -102,8 +103,14 @@ tar_sparse_member_p (struct tar_sparse_file *file) static bool tar_sparse_init (struct tar_sparse_file *file) { + memset (file, 0, sizeof *file); + + if (!sparse_select_optab (file)) + return false; + if (file->optab->init) return file->optab->init (file); + return true; } @@ -216,6 +223,8 @@ sparse_scan_file (struct tar_sparse_file *file) if (!lseek_or_error (file, 0)) return false; + st->archive_file_size = 0; + if (!tar_sparse_scan (file, scan_begin, NULL)) return false; @@ -315,6 +324,7 @@ sparse_dump_region (struct tar_sparse_file *file, size_t i) memset (blk->buffer + bytes_read, 0, BLOCKSIZE - bytes_read); bytes_left -= bytes_read; file->dumped_size += bytes_read; + mv_size_left (file->stat_info->archive_file_size - file->dumped_size); set_next_block_after (blk); } @@ -351,6 +361,7 @@ sparse_extract_region (struct tar_sparse_file *file, size_t i) count = full_write (file->fd, blk->buffer, wrbytes); write_size -= count; file->dumped_size += count; + mv_size_left (file->stat_info->archive_file_size - file->dumped_size); file->offset += count; if (count != wrbytes) { @@ -369,16 +380,15 @@ enum dump_status sparse_dump_file (int fd, struct tar_stat_info *st) { bool rc; - struct tar_sparse_file file = { 0, }; + struct tar_sparse_file file; + + if (!tar_sparse_init (&file)) + return dump_status_not_implemented; file.stat_info = st; file.fd = fd; file.seekable = true; /* File *must* be seekable for dump to work */ - if (!sparse_select_optab (&file) - || !tar_sparse_init (&file)) - return dump_status_not_implemented; - rc = sparse_scan_file (&file); if (rc && file.optab->dump_region) { @@ -388,8 +398,10 @@ sparse_dump_file (int fd, struct tar_stat_info *st) { size_t i; + mv_begin (file.stat_info); for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++) rc = tar_sparse_dump_region (&file, i); + mv_end (); } } @@ -411,7 +423,7 @@ sparse_member_p (struct tar_stat_info *st) { struct tar_sparse_file file; - if (!sparse_select_optab (&file)) + if (!tar_sparse_init (&file)) return false; file.stat_info = st; return tar_sparse_member_p (&file); @@ -422,7 +434,7 @@ sparse_fixup_header (struct tar_stat_info *st) { struct tar_sparse_file file; - if (!sparse_select_optab (&file)) + if (!tar_sparse_init (&file)) return false; file.stat_info = st; return tar_sparse_fixup_header (&file); @@ -435,15 +447,14 @@ sparse_extract_file (int fd, struct tar_stat_info *st, off_t *size) struct tar_sparse_file file; size_t i; + if (!tar_sparse_init (&file)) + return dump_status_not_implemented; + file.stat_info = st; file.fd = fd; file.seekable = lseek (fd, 0, SEEK_SET) == 0; file.offset = 0; - 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 = tar_sparse_extract_region (&file, i); @@ -457,13 +468,12 @@ sparse_skip_file (struct tar_stat_info *st) bool rc = true; struct tar_sparse_file file; + if (!tar_sparse_init (&file)) + return dump_status_not_implemented; + file.stat_info = st; file.fd = -1; - if (!sparse_select_optab (&file) - || !tar_sparse_init (&file)) - return dump_status_not_implemented; - rc = tar_sparse_decode_header (&file); skip_file (file.stat_info->archive_file_size); return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short; @@ -512,6 +522,8 @@ check_data_region (struct tar_sparse_file *file, size_t i) if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset)) return false; size_left = file->stat_info->sparse_map[i].numbytes; + mv_size_left (file->stat_info->archive_file_size - file->dumped_size); + while (size_left > 0) { size_t bytes_read; @@ -537,6 +549,7 @@ check_data_region (struct tar_sparse_file *file, size_t i) } file->dumped_size += bytes_read; size_left -= bytes_read; + mv_size_left (file->stat_info->archive_file_size - file->dumped_size); if (memcmp (blk->buffer, diff_buffer, rdsize)) { report_difference (file->stat_info, _("Contents differ")); @@ -554,26 +567,28 @@ sparse_diff_file (int fd, struct tar_stat_info *st) size_t i; off_t offset = 0; - file.stat_info = st; - file.fd = fd; - - if (!sparse_select_optab (&file) - || !tar_sparse_init (&file)) + if (!tar_sparse_init (&file)) return dump_status_not_implemented; + file.stat_info = st; + file.fd = fd; + file.seekable = true; /* File *must* be seekable for compare to work */ + rc = tar_sparse_decode_header (&file); + mv_begin (st); 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); + && 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); - + mv_end (); + tar_sparse_done (&file); return rc; } @@ -833,16 +848,34 @@ static struct tar_sparse_optab const star_optab = { GNU.sparse.size Real size of the stored file GNU.sparse.numblocks Number of blocks in the sparse map + GNU.sparse.map Map of non-null data chunks. A string consisting + of comma-separated values "offset,size[,offset,size]..." + + Tar versions 1.14-1.15.1 instead of the latter used: + repeat numblocks time GNU.sparse.offset Offset of the next data block GNU.sparse.numbytes Size of the next data block end repeat + + This has been reported as conflicting with the POSIX specs. The reason is + that offsets and sizes of non-zero data blocks were stored in multiple + instances of GNU.sparse.offset/GNU.sparse.numbytes variables. However, + POSIX requires the latest occurrence of the variable to override all + previous occurrences. + + To avoid this incompatibility new keyword GNU.sparse.map was introduced + in tar 1.15.2. Some people might still need the 1.14 way of handling + sparse files for the compatibility reasons: it can be achieved by + specifying `--pax-option delete=GNU.sparse.map' in the command line. + + See FIXME-1.14-1.15.1-1.20, below. */ static bool pax_sparse_member_p (struct tar_sparse_file *file) { - return file->stat_info->archive_file_size != file->stat_info->stat.st_size; + return file->stat_info->sparse_map_avail > 0; } static bool @@ -851,16 +884,40 @@ pax_dump_header (struct tar_sparse_file *file) off_t block_ordinal = current_block_ordinal (); union block *blk; size_t i; - + char nbuf[UINTMAX_STRSIZE_BOUND]; + struct sp_array *map = file->stat_info->sparse_map; + /* Store the real file size */ xheader_store ("GNU.sparse.size", file->stat_info, NULL); xheader_store ("GNU.sparse.numblocks", file->stat_info, NULL); - for (i = 0; i < file->stat_info->sparse_map_avail; i++) + + /* FIXME-1.14-1.15.1-1.20: See the comment above. + Starting with 1.17 this should display a warning about POSIX-incompatible + keywords being generated. In 1.20, the true branch of the if block below + will be removed and GNU.sparse.map will be marked in xhdr_tab as + protected. */ + + if (xheader_keyword_deleted_p ("GNU.sparse.map")) { - xheader_store ("GNU.sparse.offset", file->stat_info, &i); - xheader_store ("GNU.sparse.numbytes", file->stat_info, &i); + for (i = 0; i < file->stat_info->sparse_map_avail; i++) + { + xheader_store ("GNU.sparse.offset", file->stat_info, &i); + xheader_store ("GNU.sparse.numbytes", file->stat_info, &i); + } + } + else + { + xheader_string_begin (); + for (i = 0; i < file->stat_info->sparse_map_avail; i++) + { + if (i) + xheader_string_add (","); + xheader_string_add (umaxtostr (map[i].offset, nbuf)); + xheader_string_add (","); + xheader_string_add (umaxtostr (map[i].numbytes, nbuf)); + } + xheader_string_end ("GNU.sparse.map"); } - blk = start_header (file->stat_info); /* Store the effective (shrunken) file size */ OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);