List the archives containing sparse files of >8GB (real) data correctly (print correct real size) and do not fail.
Reproducer (just as an example, this could take too long): $ dd if=/dev/urandom of=bigfile bs=512M count=17 seek=1 $ tar --posix -cSf archive bigfile $ echo $? 0 $ tar -tf archive bigfile tar: Skipping to next header tar: Exiting with failure status due to previous errors Or just archive such file with --verify/-W: $ tar --posix -cWSf archive bigfile bigfile: Size differs tar: VERIFY FAILURE: 16842752 invalid headers detected tar: Exiting with failure status due to previous errors Original bugreport thread: http://www.mail-archive.com/bug-tar%40gnu.org/msg03905.html Pavel
>From c8c86adf182260182c4ad6cd1991c8808ecf7dbb Mon Sep 17 00:00:00 2001 From: Pavel Raiskup <prais...@redhat.com> Date: Tue, 1 Apr 2014 13:09:20 +0200 Subject: [PATCH] sparse: fix bug in decode_header List the archives containing sparse files of >8GB (real) data correctly (print correct real size) and do not fail. Reproducer (just as an example, this could take too long): $ dd if=/dev/urandom of=bigfile bs=512M count=17 seek=1 $ tar --posix -cSf archive bigfile $ echo $? 0 $ tar -tf archive bigfile tar: Skipping to next header tar: Exiting with failure status due to previous errors Or just archive such file with --verify/-W: $ tar --posix -cWSf archive bigfile bigfile: Size differs tar: VERIFY FAILURE: 16842752 invalid headers detected tar: Exiting with failure status due to previous errors Original bugreport thread: http://www.mail-archive.com/bug-tar%40gnu.org/msg03905.html * src/list.c (decode_header): Remove redundant assignment. * src/sparse.c (sparse 1.0 docs): Fix typos. * src/tar.h (tar_stat_info): Add new 'real_size' and 'real_size_set' fields into struct. * src/xheader.c (xheader_fixup_file_size): New function. (xheader_decode): Call xheader_fixup_file_size. (sparse_size_decoder): Instead of touching stat->st_size directly, just adjust real_size_set & real_size. --- src/list.c | 1 - src/sparse.c | 4 ++-- src/tar.h | 4 ++++ src/xheader.c | 28 +++++++++++++++++++++++++++- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/list.c b/src/list.c index b4277e0..22bec01 100644 --- a/src/list.c +++ b/src/list.c @@ -689,7 +689,6 @@ decode_header (union block *header, struct tar_stat_info *stat_info, } } - stat_info->archive_file_size = stat_info->stat.st_size; xheader_decode (stat_info); if (sparse_member_p (stat_info)) diff --git a/src/sparse.c b/src/sparse.c index 6a97676..de7f22d 100644 --- a/src/sparse.c +++ b/src/sparse.c @@ -876,7 +876,7 @@ static struct tar_sparse_optab const star_optab = { * 1.0 Starting from this version, the exact sparse format version is specified - explicitely in the header using the following variables: + explicitly in the header using the following variables: GNU.sparse.major Major version GNU.sparse.minor Minor version @@ -905,7 +905,7 @@ static struct tar_sparse_optab const star_optab = { directory. Then, using a simple program it would be possible to expand the file to its original form even without GNU tar. - Bu default, v.1.0 archives are created. To use other formats, + By default, v.1.0 archives are created. To use other formats, --sparse-version option is provided. Additionally, v.0.0 can be obtained by deleting GNU.sparse.map from 0.1 format: --sparse-version 0.1 --pax-option delete=GNU.sparse.map diff --git a/src/tar.h b/src/tar.h index 3d69399..73cd11e 100644 --- a/src/tar.h +++ b/src/tar.h @@ -327,6 +327,10 @@ struct tar_stat_info size_t sparse_map_size; /* Size of the sparse map */ struct sp_array *sparse_map; + off_t real_size; /* The real size of sparse file */ + int real_size_set; /* True when GNU.sparse.realsize is set in + archived file */ + size_t xattr_map_size; /* Size of the xattr map */ struct xattr_array *xattr_map; diff --git a/src/xheader.c b/src/xheader.c index c94c6d3..0be83a4 100644 --- a/src/xheader.c +++ b/src/xheader.c @@ -742,6 +742,20 @@ decx (void *data, char const *keyword, char const *value, size_t size) keyword)); } +static void +xheader_fixup_file_size (struct tar_stat_info *st) +{ + /* The effective size is in st_size, regardless of whether the file is sparse + * or not. */ + st->archive_file_size = st->stat.st_size; + + /* For non-sparse files we are done (effective size == real size). But for + sparse files we must take the GNU.sparse.realsize header into account (if + present, its content was parsed by sparse_size_decoder already. */ + if (st->real_size_set) + st->stat.st_size = st->real_size; +} + void xheader_decode (struct tar_stat_info *st) { @@ -755,6 +769,11 @@ xheader_decode (struct tar_stat_info *st) continue; } run_override_list (keyword_override_list, st); + + /* Here we know for sure that we have "effective" size stored into + st->stat->st_size (this may be set by read_header directly from regular pax + header, or overwritten by size_decoder() from extended pax header). */ + xheader_fixup_file_size (st); } static void @@ -1360,7 +1379,14 @@ sparse_size_decoder (struct tar_stat_info *st, { uintmax_t u; if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword)) - st->stat.st_size = u; + { + /* do not set the st->stat.st_size here immediately because there may be + the 'size' extended header following 'GNU.sparse.realsize' which would + override our setup. We must handle size (and real size) of sparse + files once whole xheader is read */ + st->real_size_set = 1; + st->real_size = u; + } } static void -- 1.9.0
testsuite.log.xz
Description: application/xz