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

Attachment: testsuite.log.xz
Description: application/xz

Reply via email to