#include "common.h"
struct tar_sparse_file;
+static bool sparse_select_optab (struct tar_sparse_file *file);
enum sparse_scan_state
{
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 */
};
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;
}
if (!lseek_or_error (file, 0))
return false;
+ st->archive_file_size = 0;
+
if (!tar_sparse_scan (file, scan_begin, NULL))
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);
}
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)
{
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)
{
{
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 ();
}
}
{
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);
{
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);
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);
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;
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;
}
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"));
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;
}
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
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);