X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;ds=sidebyside;f=src%2Fsparse.c;h=a661ab18d3a47b3ed6cf5446130fc1dc957d2a76;hb=d46025b23c5bbe31902ba4e902312ce8fe05ed63;hp=39be72db56f964ad5046553859c2fe3934b5664c;hpb=8d6e47cc7fcfb6781b82ba60b8eb823c7fdc7013;p=chaz%2Ftar diff --git a/src/sparse.c b/src/sparse.c index 39be72d..a661ab1 100644 --- a/src/sparse.c +++ b/src/sparse.c @@ -205,10 +205,8 @@ sparse_scan_file (struct tar_sparse_file *file) } if (sp.numbytes == 0) - { - sp.offset = offset - 1; - sp.numbytes = 1; - } + sp.offset = offset; + sparse_add_map (file, &sp); file->stat_info->archive_file_size += count; return tar_sparse_scan (file, scan_end, NULL); @@ -216,6 +214,7 @@ sparse_scan_file (struct tar_sparse_file *file) static struct tar_sparse_optab oldgnu_optab; static struct tar_sparse_optab star_optab; +static struct tar_sparse_optab pax_optab; static bool sparse_select_optab (struct tar_sparse_file *file) @@ -232,8 +231,8 @@ sparse_select_optab (struct tar_sparse_file *file) break; case POSIX_FORMAT: - /* FIXME: Add method */ - return false; + file->optab = &pax_optab; + break; case STAR_FORMAT: file->optab = &star_optab; @@ -255,7 +254,7 @@ sparse_dump_region (struct tar_sparse_file *file, size_t index) SEEK_SET)) return false; - do + while (bytes_left > 0) { size_t bufsize = (bytes_left > BLOCKSIZE) ? BLOCKSIZE : bytes_left; off_t bytes_read; @@ -277,7 +276,7 @@ sparse_dump_region (struct tar_sparse_file *file, size_t index) file->dumped_size += bytes_read; set_next_block_after (blk); } - while (bytes_left > 0); + return true; } @@ -289,8 +288,16 @@ sparse_extract_region (struct tar_sparse_file *file, size_t index) if (!lseek_or_error (file, file->stat_info->sparse_map[index].offset, SEEK_SET)) return false; + write_size = file->stat_info->sparse_map[index].numbytes; - while (write_size > 0) + + if (write_size == 0) + { + /* Last block of the file is a hole */ + if (sys_truncate (file->fd)) + truncate_warn (file->stat_info->orig_file_name); + } + else while (write_size > 0) { size_t count; size_t wrbytes = (write_size > BLOCKSIZE) ? BLOCKSIZE : write_size; @@ -482,7 +489,7 @@ sparse_diff_file (int fd, struct tar_stat_info *stat) + file.stat_info->sparse_map[i].numbytes; } - if (rc) + if (!rc) skip_file (file.stat_info->archive_file_size - file.dumped_size); tar_sparse_done (&file); @@ -713,3 +720,59 @@ static struct tar_sparse_optab star_optab = { NULL, /* No dump region function */ sparse_extract_region, }; + + +/* GNU PAX sparse file format. The sparse file map is stored in + x header: + + GNU.sparse.size Real size of the stored file + GNU.sparse.numblocks Number of blocks in the sparse map + repeat numblocks time + GNU.sparse.offset Offset of the next data block + GNU.sparse.numbytes Size of the next data block + end repeat +*/ + +static bool +pax_dump_header (struct tar_sparse_file *file) +{ + off_t block_ordinal = current_block_ordinal (); + union block *blk; + size_t i; + + /* 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++) + { + xheader_store ("GNU.sparse.offset", file->stat_info, &i); + xheader_store ("GNU.sparse.numbytes", file->stat_info, &i); + } + + blk = start_header (file->stat_info); + /* Store the effective (shrunken) file size */ + OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size); + finish_header (file->stat_info, blk, block_ordinal); + return true; +} + +static bool +pax_decode_header (struct tar_sparse_file *file) +{ + /* Restore actual size */ + size_t s = file->stat_info->archive_file_size; + file->stat_info->archive_file_size = file->stat_info->stat.st_size; + file->stat_info->stat.st_size = s; + return true; +} + +static struct tar_sparse_optab pax_optab = { + NULL, /* No init function */ + NULL, /* No done function */ + pax_dump_header, + pax_decode_header, + NULL, /* No scan_block function */ + sparse_dump_region, + sparse_extract_region, +}; +