Hi, I'm attaching patch for bug in tar for --posix && --sparse options.
Could you please consider applying?
Reproduce:
1. Create sparse file of "shrunken" size (real data) >= 8^11 bytes.
2. tar --posix --sparse -cf /dev/null ThatBigFile
3. Tar fails with:
tar: value [BIG_NUMBER] out of off_t range 0..8589934591
tar: Exiting with failure status due to previous errors
Simple test:
$ cat script
#!/bin/bash
COUNT=1 # 1000 * 2 * 10M ~> ~ 20GB
if ! test -z "$2"; then
COUNT=$2
fi
for i in `seq 0 $(( $COUNT - 1))`; do
dd if=/dev/urandom of=$1 bs=4K count=$(( 256 * 10 )) \
seek=$(( $i * 2 * 256 * 10 )) 2> /dev/null
echo $i
done
$ ./script BIGFILE 1000
Sorry that I haven't written test. I was unable to use genfile utility to
produce such files. Is the 'dd' utility used as above ^^ ok for portability
reasons?
Pavel
>From ce21d1046792a3a918ef85037a28216350511a06 Mon Sep 17 00:00:00 2001
From: Pavel Raiskup <[email protected]>
Date: Fri, 25 Jan 2013 13:24:23 +0100
Subject: [PATCH] sparse: Fix bug in POSIX_FORMAT
Archive the file size correctly (and do not fail) when the shrunken size of
file is >= 8^11 (8589934592) bytes. In that case use the 'size' posix
extended header instead of using the size into header field which has not
enough capacity.
* src/common.h: Generalize macros from create.c.
* src/create.c: Move macros to common.h.
* src/sparse.c: Use "size" field when shrunken size >= 8^11 bytes.
---
src/common.h | 11 +++++++++++
src/create.c | 11 -----------
src/sparse.c | 11 ++++++++++-
3 files changed, 21 insertions(+), 12 deletions(-)
diff --git a/src/common.h b/src/common.h
index 4f7c19f..d8d7638 100644
--- a/src/common.h
+++ b/src/common.h
@@ -493,6 +493,17 @@ void exclusion_tag_warning (const char *dirname, const char *tagname,
enum exclusion_tag_type check_exclusion_tags (struct tar_stat_info const *st,
const char **tag_file_name);
+/* The maximum uintmax_t value that can be represented with DIGITS digits,
+ assuming that each digit is BITS_PER_DIGIT wide. */
+#define MAX_VAL_WITH_DIGITS(digits, bits_per_digit) \
+ ((digits) * (bits_per_digit) < sizeof (uintmax_t) * CHAR_BIT \
+ ? ((uintmax_t) 1 << ((digits) * (bits_per_digit))) - 1 \
+ : (uintmax_t) -1)
+
+/* The maximum uintmax_t value that can be represented with octal
+ - digits and a trailing NUL in BUFFER. */
+#define MAX_OCTAL_VAL(buffer) MAX_VAL_WITH_DIGITS (sizeof (buffer) - 1, LG_8)
+
#define OFF_TO_CHARS(val, where) off_to_chars (val, where, sizeof (where))
#define TIME_TO_CHARS(val, where) time_to_chars (val, where, sizeof (where))
diff --git a/src/create.c b/src/create.c
index e14e13d..ad9db70 100644
--- a/src/create.c
+++ b/src/create.c
@@ -121,17 +121,6 @@ cachedir_file_p (int fd)
}
-/* The maximum uintmax_t value that can be represented with DIGITS digits,
- assuming that each digit is BITS_PER_DIGIT wide. */
-#define MAX_VAL_WITH_DIGITS(digits, bits_per_digit) \
- ((digits) * (bits_per_digit) < sizeof (uintmax_t) * CHAR_BIT \
- ? ((uintmax_t) 1 << ((digits) * (bits_per_digit))) - 1 \
- : (uintmax_t) -1)
-
-/* The maximum uintmax_t value that can be represented with octal
- digits and a trailing NUL in BUFFER. */
-#define MAX_OCTAL_VAL(buffer) MAX_VAL_WITH_DIGITS (sizeof (buffer) - 1, LG_8)
-
/* Convert VALUE to an octal representation suitable for tar headers.
Output to buffer WHERE with size SIZE.
The result is undefined if SIZE is 0 or if VALUE is too large to fit. */
diff --git a/src/sparse.c b/src/sparse.c
index b93cdc2..1f3b0b5 100644
--- a/src/sparse.c
+++ b/src/sparse.c
@@ -987,6 +987,7 @@ pax_dump_header_1 (struct tar_sparse_file *file)
size_t i;
char nbuf[UINTMAX_STRSIZE_BOUND];
off_t size = 0;
+ off_t shrunken_size = 0;
struct sp_array *map = file->stat_info->sparse_map;
char *save_file_name = file->stat_info->file_name;
@@ -1034,8 +1035,16 @@ pax_dump_header_1 (struct tar_sparse_file *file)
file->stat_info->file_name[NAME_FIELD_SIZE] = 0;
blk = start_header (file->stat_info);
+
/* Store the effective (shrunken) file size */
- OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
+ shrunken_size = file->stat_info->archive_file_size;
+ if (MAX_OCTAL_VAL (blk->header.size) < shrunken_size)
+ {
+ xheader_store ("size", file->stat_info, NULL);
+ shrunken_size = 0;
+ }
+ OFF_TO_CHARS (shrunken_size, blk->header.size);
+
finish_header (file->stat_info, blk, block_ordinal);
free (file->stat_info->file_name);
file->stat_info->file_name = save_file_name;
--
1.7.11.7