X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fsparse.c;h=30bf03157c962c09f51fc8b8be07eb6f287e1000;hb=c7ab8eaba40ef39f7b50cce4cb9228bf4ca09d2a;hp=2f5330df3344d4036f51582dff2e932da6cdd1c9;hpb=15a607fc11d8563fe471924fb6e718ceb9adfce4;p=chaz%2Ftar diff --git a/src/sparse.c b/src/sparse.c index 2f5330d..30bf031 100644 --- a/src/sparse.c +++ b/src/sparse.c @@ -1,6 +1,6 @@ -/* Functions for dealing with sparse files +/* Functions for dealing with sparse files - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the @@ -14,13 +14,15 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "system.h" +#include +#include #include #include "common.h" struct tar_sparse_file; +static bool sparse_select_optab (struct tar_sparse_file *file); enum sparse_scan_state { @@ -39,21 +41,57 @@ struct tar_sparse_optab bool (*decode_header) (struct tar_sparse_file *); bool (*scan_block) (struct tar_sparse_file *, enum sparse_scan_state, void *); - bool (*dump_region) (struct tar_sparse_file *, size_t index); - bool (*extract_region) (struct tar_sparse_file *, size_t index); + bool (*dump_region) (struct tar_sparse_file *, size_t); + bool (*extract_region) (struct tar_sparse_file *, size_t); }; struct tar_sparse_file { int fd; /* File descriptor */ - size_t dumped_size; /* Number of bytes actually written + bool seekable; /* Is fd seekable? */ + off_t offset; /* Current offset in fd if seekable==false. + Otherwise unused */ + 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 *optab; + struct tar_sparse_optab const *optab; /* Operation table */ void *closure; /* Any additional data optab calls might - reqiure */ + require */ }; +/* Dump zeros to file->fd until offset is reached. It is used instead of + lseek if the output file is not seekable */ +static bool +dump_zeros (struct tar_sparse_file *file, off_t offset) +{ + static char const zero_buf[BLOCKSIZE]; + + if (offset < file->offset) + { + errno = EINVAL; + return false; + } + + while (file->offset < offset) + { + size_t size = (BLOCKSIZE < offset - file->offset + ? BLOCKSIZE + : offset - file->offset); + ssize_t wrbytes; + + wrbytes = write (file->fd, zero_buf, size); + if (wrbytes <= 0) + { + if (wrbytes == 0) + errno = EINVAL; + return false; + } + file->offset += wrbytes; + } + + return true; +} + static bool tar_sparse_member_p (struct tar_sparse_file *file) { @@ -65,9 +103,14 @@ tar_sparse_member_p (struct tar_sparse_file *file) static bool tar_sparse_init (struct tar_sparse_file *file) { - file->dumped_size = 0; + memset (file, 0, sizeof *file); + + if (!sparse_select_optab (file)) + return false; + if (file->optab->init) return file->optab->init (file); + return true; } @@ -89,18 +132,18 @@ tar_sparse_scan (struct tar_sparse_file *file, enum sparse_scan_state state, } static bool -tar_sparse_dump_region (struct tar_sparse_file *file, size_t index) +tar_sparse_dump_region (struct tar_sparse_file *file, size_t i) { if (file->optab->dump_region) - return file->optab->dump_region (file, index); + return file->optab->dump_region (file, i); return false; } static bool -tar_sparse_extract_region (struct tar_sparse_file *file, size_t index) +tar_sparse_extract_region (struct tar_sparse_file *file, size_t i) { if (file->optab->extract_region) - return file->optab->extract_region (file, index); + return file->optab->extract_region (file, i); return false; } @@ -130,9 +173,11 @@ tar_sparse_fixup_header (struct tar_sparse_file *file) static bool -lseek_or_error (struct tar_sparse_file *file, off_t offset, int whence) +lseek_or_error (struct tar_sparse_file *file, off_t offset) { - if (lseek (file->fd, offset, whence) < 0) + if (file->seekable + ? lseek (file->fd, offset, SEEK_SET) < 0 + : ! dump_zeros (file, offset)) { seek_diag_details (file->stat_info->orig_file_name, offset); return false; @@ -144,7 +189,7 @@ lseek_or_error (struct tar_sparse_file *file, off_t offset, int whence) it's made *entirely* of zeros, returning a 0 the instant it finds something that is a nonzero, i.e., useful data. */ static bool -zero_block_p (char *buffer, size_t size) +zero_block_p (char const *buffer, size_t size) { while (size--) if (*buffer++) @@ -152,57 +197,46 @@ zero_block_p (char *buffer, size_t size) return true; } -#define clear_block(p) memset (p, 0, BLOCKSIZE); - -#define SPARSES_INIT_COUNT SPARSES_IN_SPARSE_HEADER - static void -sparse_add_map (struct tar_sparse_file *file, struct sp_array *sp) +sparse_add_map (struct tar_stat_info *st, struct sp_array const *sp) { - if (file->stat_info->sparse_map == NULL) - { - file->stat_info->sparse_map = - xmalloc (SPARSES_INIT_COUNT * sizeof file->stat_info->sparse_map[0]); - file->stat_info->sparse_map_size = SPARSES_INIT_COUNT; - } - else if (file->stat_info->sparse_map_avail == file->stat_info->sparse_map_size) - { - file->stat_info->sparse_map_size *= 2; - file->stat_info->sparse_map = - xrealloc (file->stat_info->sparse_map, - file->stat_info->sparse_map_size - * sizeof file->stat_info->sparse_map[0]); - } - file->stat_info->sparse_map[file->stat_info->sparse_map_avail++] = *sp; + struct sp_array *sparse_map = st->sparse_map; + size_t avail = st->sparse_map_avail; + if (avail == st->sparse_map_size) + st->sparse_map = sparse_map = + x2nrealloc (sparse_map, &st->sparse_map_size, sizeof *sparse_map); + sparse_map[avail] = *sp; + st->sparse_map_avail = avail + 1; } /* Scan the sparse file and create its map */ static bool sparse_scan_file (struct tar_sparse_file *file) { - static char buffer[BLOCKSIZE]; + struct tar_stat_info *st = file->stat_info; + int fd = file->fd; + char buffer[BLOCKSIZE]; size_t count; - size_t offset = 0; + off_t offset = 0; struct sp_array sp = {0, 0}; - if (!lseek_or_error (file, 0, SEEK_SET)) + if (!lseek_or_error (file, 0)) return false; - clear_block (buffer); - file->stat_info->sparse_map_size = 0; - file->stat_info->archive_file_size = 0; + st->archive_file_size = 0; if (!tar_sparse_scan (file, scan_begin, NULL)) return false; - while ((count = safe_read (file->fd, buffer, sizeof buffer)) > 0) + while ((count = safe_read (fd, buffer, sizeof buffer)) != 0 + && count != SAFE_READ_ERROR) { - /* Analize the block */ + /* Analyze the block. */ if (zero_block_p (buffer, count)) { if (sp.numbytes) { - sparse_add_map (file, &sp); + sparse_add_map (st, &sp); sp.numbytes = 0; if (!tar_sparse_scan (file, scan_block, NULL)) return false; @@ -213,26 +247,25 @@ sparse_scan_file (struct tar_sparse_file *file) if (sp.numbytes == 0) sp.offset = offset; sp.numbytes += count; - file->stat_info->archive_file_size += count; + st->archive_file_size += count; if (!tar_sparse_scan (file, scan_block, buffer)) return false; } - + offset += count; - clear_block (buffer); } - + if (sp.numbytes == 0) sp.offset = offset; - sparse_add_map (file, &sp); - file->stat_info->archive_file_size += count; + sparse_add_map (st, &sp); + st->archive_file_size += count; return tar_sparse_scan (file, scan_end, NULL); } -static struct tar_sparse_optab oldgnu_optab; -static struct tar_sparse_optab star_optab; -static struct tar_sparse_optab pax_optab; +static struct tar_sparse_optab const oldgnu_optab; +static struct tar_sparse_optab const star_optab; +static struct tar_sparse_optab const pax_optab; static bool sparse_select_optab (struct tar_sparse_file *file) @@ -255,7 +288,7 @@ sparse_select_optab (struct tar_sparse_file *file) case STAR_FORMAT: file->optab = &star_optab; break; - + default: return false; } @@ -263,35 +296,35 @@ sparse_select_optab (struct tar_sparse_file *file) } static bool -sparse_dump_region (struct tar_sparse_file *file, size_t index) +sparse_dump_region (struct tar_sparse_file *file, size_t i) { union block *blk; - off_t bytes_left = file->stat_info->sparse_map[index].numbytes; - - if (!lseek_or_error (file, file->stat_info->sparse_map[index].offset, - SEEK_SET)) + off_t bytes_left = file->stat_info->sparse_map[i].numbytes; + + if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset)) return false; while (bytes_left > 0) { size_t bufsize = (bytes_left > BLOCKSIZE) ? BLOCKSIZE : bytes_left; - off_t bytes_read; - + size_t bytes_read; + blk = find_next_block (); - memset (blk->buffer, 0, BLOCKSIZE); bytes_read = safe_read (file->fd, blk->buffer, bufsize); - if (bytes_read < 0) + if (bytes_read == SAFE_READ_ERROR) { read_diag_details (file->stat_info->orig_file_name, - file->stat_info->sparse_map[index].offset - + file->stat_info->sparse_map[index].numbytes - - bytes_left, - bufsize); + (file->stat_info->sparse_map[i].offset + + file->stat_info->sparse_map[i].numbytes + - bytes_left), + bufsize); return false; } + 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); } @@ -299,20 +332,19 @@ sparse_dump_region (struct tar_sparse_file *file, size_t index) } static bool -sparse_extract_region (struct tar_sparse_file *file, size_t index) +sparse_extract_region (struct tar_sparse_file *file, size_t i) { size_t write_size; - - if (!lseek_or_error (file, file->stat_info->sparse_map[index].offset, - SEEK_SET)) + + if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset)) return false; - write_size = file->stat_info->sparse_map[index].numbytes; + write_size = file->stat_info->sparse_map[i].numbytes; if (write_size == 0) { /* Last block of the file is a hole */ - if (sys_truncate (file->fd)) + if (file->seekable && sys_truncate (file->fd)) truncate_warn (file->stat_info->orig_file_name); } else while (write_size > 0) @@ -329,6 +361,8 @@ sparse_extract_region (struct tar_sparse_file *file, size_t index) 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) { write_error_details (file->stat_info->orig_file_name, @@ -343,18 +377,18 @@ sparse_extract_region (struct tar_sparse_file *file, size_t index) /* Interface functions */ enum dump_status -sparse_dump_file (int fd, struct tar_stat_info *stat) +sparse_dump_file (int fd, struct tar_stat_info *st) { bool rc; struct tar_sparse_file file; - file.stat_info = stat; - 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 dump to work */ + rc = sparse_scan_file (&file); if (rc && file.optab->dump_region) { @@ -364,60 +398,63 @@ sparse_dump_file (int fd, struct tar_stat_info *stat) { 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 (); } } - pad_archive(file.stat_info->archive_file_size - file.dumped_size); + pad_archive (file.stat_info->archive_file_size - file.dumped_size); return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short; } /* Returns true if the file represented by stat is a sparse one */ bool -sparse_file_p (struct tar_stat_info *stat) +sparse_file_p (struct tar_stat_info *st) { - return (ST_NBLOCKS (stat->stat) - < (stat->stat.st_size / ST_NBLOCKSIZE - + (stat->stat.st_size % ST_NBLOCKSIZE != 0))); + return (ST_NBLOCKS (st->stat) + < (st->stat.st_size / ST_NBLOCKSIZE + + (st->stat.st_size % ST_NBLOCKSIZE != 0))); } bool -sparse_member_p (struct tar_stat_info *stat) +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 = stat; + file.stat_info = st; return tar_sparse_member_p (&file); } bool -sparse_fixup_header (struct tar_stat_info *stat) +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 = stat; + file.stat_info = st; return tar_sparse_fixup_header (&file); } enum dump_status -sparse_extract_file (int fd, struct tar_stat_info *stat, off_t *size) +sparse_extract_file (int fd, struct tar_stat_info *st, off_t *size) { bool rc = true; struct tar_sparse_file file; size_t i; - - file.stat_info = stat; - 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 = lseek (fd, 0, SEEK_SET) == 0; + file.offset = 0; + 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); @@ -426,42 +463,37 @@ sparse_extract_file (int fd, struct tar_stat_info *stat, off_t *size) } enum dump_status -sparse_skip_file (struct tar_stat_info *stat) +sparse_skip_file (struct tar_stat_info *st) { bool rc = true; struct tar_sparse_file file; - - file.stat_info = stat; - file.fd = -1; - 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 = -1; + 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; } -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)) + if (!lseek_or_error (file, beg)) return false; - + while (beg < end) { size_t bytes_read; - size_t rdsize = end - beg; - - if (rdsize > BLOCKSIZE) - rdsize = BLOCKSIZE; - clear_block (diff_buffer); + size_t rdsize = BLOCKSIZE < end - beg ? BLOCKSIZE : end - beg; + char diff_buffer[BLOCKSIZE]; + bytes_read = safe_read (file->fd, diff_buffer, rdsize); - if (bytes_read < 0) + if (bytes_read == SAFE_READ_ERROR) { read_diag_details (file->stat_info->orig_file_name, beg, @@ -470,8 +502,10 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end) } if (!zero_block_p (diff_buffer, bytes_read)) { + char begbuf[INT_BUFSIZE_BOUND (off_t)]; report_difference (file->stat_info, - _("File fragment at %lu is not a hole"), beg); + _("File fragment at %s is not a hole"), + offtostr (beg, begbuf)); return false; } @@ -481,19 +515,21 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end) } static bool -check_data_region (struct tar_sparse_file *file, size_t index) +check_data_region (struct tar_sparse_file *file, size_t i) { size_t size_left; - - if (!lseek_or_error (file, file->stat_info->sparse_map[index].offset, - SEEK_SET)) + + if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset)) return false; - size_left = file->stat_info->sparse_map[index].numbytes; + 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; size_t rdsize = (size_left > BLOCKSIZE) ? BLOCKSIZE : size_left; - + char diff_buffer[BLOCKSIZE]; + union block *blk = find_next_block (); if (!blk) { @@ -502,17 +538,18 @@ check_data_region (struct tar_sparse_file *file, size_t index) } set_next_block_after (blk); bytes_read = safe_read (file->fd, diff_buffer, rdsize); - if (bytes_read < 0) + if (bytes_read == SAFE_READ_ERROR) { 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, + (file->stat_info->sparse_map[i].offset + + file->stat_info->sparse_map[i].numbytes + - size_left), rdsize); return false; } 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")); @@ -523,45 +560,47 @@ check_data_region (struct tar_sparse_file *file, size_t index) } bool -sparse_diff_file (int fd, struct tar_stat_info *stat) +sparse_diff_file (int fd, struct tar_stat_info *st) { 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)) + 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; } - + /* Old GNU Format. The sparse file information is stored in the oldgnu_header in the following manner: The header is marked with type 'S'. Its `size' field contains the cumulative size of all non-empty blocks of the file. The actual file size is stored in `realsize' member of oldgnu_header. - + The map of the file is stored in a list of `struct sparse'. Each struct contains offset to the block of data and its size (both as octal numbers). The first file header contains @@ -581,13 +620,13 @@ enum oldgnu_add_status }; static bool -oldgnu_sparse_member_p (struct tar_sparse_file *file_unused) +oldgnu_sparse_member_p (struct tar_sparse_file *file __attribute__ ((unused))) { return current_header->header.typeflag == GNUTYPE_SPARSE; } /* Add a sparse item to the sparse file and its obstack */ -static enum oldgnu_add_status +static enum oldgnu_add_status oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s) { struct sp_array sp; @@ -601,7 +640,7 @@ oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s) || file->stat_info->archive_file_size < 0) return add_fail; - sparse_add_map (file, &sp); + sparse_add_map (file->stat_info, &sp); return add_ok; } @@ -612,7 +651,7 @@ oldgnu_fixup_header (struct tar_sparse_file *file) which actually contains archived size. The following fixes it */ file->stat_info->archive_file_size = file->stat_info->stat.st_size; file->stat_info->stat.st_size = - OFF_FROM_HEADER (current_header->oldgnu_header.realsize); + OFF_FROM_HEADER (current_header->oldgnu_header.realsize); return true; } @@ -623,9 +662,9 @@ oldgnu_get_sparse_info (struct tar_sparse_file *file) size_t i; union block *h = current_header; int ext_p; - static enum oldgnu_add_status rc; - - file->stat_info->sparse_map_size = 0; + enum oldgnu_add_status rc; + + file->stat_info->sparse_map_avail = 0; for (i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++) { rc = oldgnu_add_sparse (file, &h->oldgnu_header.sp[i]); @@ -676,7 +715,7 @@ oldgnu_dump_header (struct tar_sparse_file *file) off_t block_ordinal = current_block_ordinal (); union block *blk; size_t i; - + blk = start_header (file->stat_info); blk->header.typeflag = GNUTYPE_SPARSE; if (file->stat_info->sparse_map_avail > SPARSES_IN_OLDGNU_HEADER) @@ -693,7 +732,7 @@ oldgnu_dump_header (struct tar_sparse_file *file) SPARSES_IN_OLDGNU_HEADER); blk->oldgnu_header.isextended = i < file->stat_info->sparse_map_avail; finish_header (file->stat_info, blk, block_ordinal); - + while (i < file->stat_info->sparse_map_avail) { blk = find_next_block (); @@ -710,7 +749,7 @@ oldgnu_dump_header (struct tar_sparse_file *file) return true; } -static struct tar_sparse_optab oldgnu_optab = { +static struct tar_sparse_optab const oldgnu_optab = { NULL, /* No init function */ NULL, /* No done function */ oldgnu_sparse_member_p, @@ -726,7 +765,7 @@ static struct tar_sparse_optab oldgnu_optab = { /* Star */ static bool -star_sparse_member_p (struct tar_sparse_file *file_unused) +star_sparse_member_p (struct tar_sparse_file *file __attribute__ ((unused))) { return current_header->header.typeflag == GNUTYPE_SPARSE; } @@ -749,9 +788,9 @@ star_get_sparse_info (struct tar_sparse_file *file) size_t i; union block *h = current_header; int ext_p; - static enum oldgnu_add_status rc; - - file->stat_info->sparse_map_size = 0; + enum oldgnu_add_status rc = add_ok; + + file->stat_info->sparse_map_avail = 0; if (h->star_in_header.prefix[0] == '\0' && h->star_in_header.sp[0].offset[10] != '\0') @@ -791,7 +830,7 @@ star_get_sparse_info (struct tar_sparse_file *file) } -static struct tar_sparse_optab star_optab = { +static struct tar_sparse_optab const star_optab = { NULL, /* No init function */ NULL, /* No done function */ star_sparse_member_p, @@ -799,7 +838,7 @@ static struct tar_sparse_optab star_optab = { star_fixup_header, star_get_sparse_info, NULL, /* No scan_block function */ - NULL, /* No dump region function */ + NULL, /* No dump region function */ sparse_extract_region, }; @@ -809,16 +848,34 @@ static struct tar_sparse_optab 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 @@ -827,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); @@ -844,7 +925,7 @@ pax_dump_header (struct tar_sparse_file *file) return true; } -static struct tar_sparse_optab pax_optab = { +static struct tar_sparse_optab const pax_optab = { NULL, /* No init function */ NULL, /* No done function */ pax_sparse_member_p, @@ -855,4 +936,3 @@ static struct tar_sparse_optab pax_optab = { sparse_dump_region, sparse_extract_region, }; -