+ NULL, /* No dump region function */
+ sparse_extract_region,
+};
+
+\f
+/* 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
+ 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->sparse_map_avail > 0;
+}
+
+static bool
+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);
+
+ /* 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"))
+ {
+ 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);
+ finish_header (file->stat_info, blk, block_ordinal);
+ return true;
+}
+
+static struct tar_sparse_optab const pax_optab = {
+ NULL, /* No init function */
+ NULL, /* No done function */
+ pax_sparse_member_p,
+ pax_dump_header,
+ NULL, /* No decode_header function */
+ NULL, /* No fixup_header function */
+ NULL, /* No scan_block function */
+ sparse_dump_region,