tar: optional autodetection of gz/bz2 compressed tarballs.

+130 bytes. Closes bug 992.
This commit is contained in:
Denis Vlasenko 2008-02-19 11:26:28 +00:00
parent a37e7134f7
commit 431a7c9c53
3 changed files with 94 additions and 33 deletions

View File

@ -166,6 +166,14 @@ config FEATURE_TAR_CREATE
If you enable this option you'll be able to create If you enable this option you'll be able to create
tar archives using the `-c' option. tar archives using the `-c' option.
config FEATURE_TAR_GZIP
bool "Enable -z option"
default y
depends on TAR
help
If you enable this option tar will be able to call gzip,
when creating or extracting tar gziped archives.
config FEATURE_TAR_BZIP2 config FEATURE_TAR_BZIP2
bool "Enable -j option to handle .tar.bz2 files" bool "Enable -j option to handle .tar.bz2 files"
default n default n
@ -182,22 +190,6 @@ config FEATURE_TAR_LZMA
If you enable this option you'll be able to extract If you enable this option you'll be able to extract
archives compressed with lzma. archives compressed with lzma.
config FEATURE_TAR_FROM
bool "Enable -X (exclude from) and -T (include from) options)"
default n
depends on TAR
help
If you enable this option you'll be able to specify
a list of files to include or exclude from an archive.
config FEATURE_TAR_GZIP
bool "Enable -z option"
default y
depends on TAR
help
If you enable this option tar will be able to call gzip,
when creating or extracting tar gziped archives.
config FEATURE_TAR_COMPRESS config FEATURE_TAR_COMPRESS
bool "Enable -Z option" bool "Enable -Z option"
default n default n
@ -206,6 +198,22 @@ config FEATURE_TAR_COMPRESS
If you enable this option tar will be able to call uncompress, If you enable this option tar will be able to call uncompress,
when extracting .tar.Z archives. when extracting .tar.Z archives.
config FEATURE_TAR_AUTODETECT
bool "Let tar autodetect gz/bz2 compresses tarballs"
default n
depends on FEATURE_TAR_GZIP || FEATURE_TAR_BZIP2
help
With this option tar can automatically detect gzip/bzip2 compressed
tarballs. Currently it works only on seekable streams.
config FEATURE_TAR_FROM
bool "Enable -X (exclude from) and -T (include from) options)"
default n
depends on TAR
help
If you enable this option you'll be able to specify
a list of files to include or exclude from an archive.
config FEATURE_TAR_OLDGNU_COMPATIBILITY config FEATURE_TAR_OLDGNU_COMPATIBILITY
bool "Enable support for old tar header format" bool "Enable support for old tar header format"
default N default N

View File

@ -46,6 +46,9 @@ void BUG_tar_header_size(void);
char get_header_tar(archive_handle_t *archive_handle) char get_header_tar(archive_handle_t *archive_handle)
{ {
static smallint end; static smallint end;
#if ENABLE_FEATURE_TAR_AUTODETECT
static smallint not_first;
#endif
file_header_t *file_header = archive_handle->file_header; file_header_t *file_header = archive_handle->file_header;
struct { struct {
@ -115,7 +118,7 @@ char get_header_tar(archive_handle_t *archive_handle)
* Read until the end to empty the pipe from gz or bz2 * Read until the end to empty the pipe from gz or bz2
*/ */
while (full_read(archive_handle->src_fd, &tar, 512) == 512) while (full_read(archive_handle->src_fd, &tar, 512) == 512)
/* repeat */; continue;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
end = 1; end = 1;
@ -123,16 +126,49 @@ char get_header_tar(archive_handle_t *archive_handle)
} }
end = 0; end = 0;
/* Check header has valid magic, "ustar" is for the proper tar /* Check header has valid magic, "ustar" is for the proper tar,
* 0's are for the old tar format * five NULs are for the old tar format */
*/ if (strncmp(tar.magic, "ustar", 5) != 0
if (strncmp(tar.magic, "ustar", 5) != 0) { && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY || memcmp(tar.magic, "\0\0\0\0", 5) != 0)
if (memcmp(tar.magic, "\0\0\0\0", 5) != 0) ) {
#if ENABLE_FEATURE_TAR_AUTODETECT
char (*get_header_ptr)(archive_handle_t *);
/* tar gz/bz autodetect: check for gz/bz2 magic.
* If it is the very first block, and we see the magic,
* we can switch to get_header_tar_gz/bz2/lzma().
* Needs seekable fd. I wish recv(MSG_PEEK) would work
* on any fd... */
if (not_first)
goto err;
#if ENABLE_FEATURE_TAR_GZIP
if (tar.name[0] == 0x1f && tar.name[1] == 0x8b) { /* gzip */
get_header_ptr = get_header_tar_gz;
} else
#endif #endif
#if ENABLE_FEATURE_TAR_BZIP2
if (tar.name[0] == 'B' && tar.name[1] == 'Z'
&& tar.name[2] == 'h' && isdigit(tar.name[3])
) { /* bzip2 */
get_header_ptr = get_header_tar_bz2;
} else
#endif
goto err;
if (lseek(archive_handle->src_fd, -512, SEEK_CUR) != 0)
goto err;
while (get_header_ptr(archive_handle) == EXIT_SUCCESS)
continue;
return EXIT_FAILURE;
err:
#endif /* FEATURE_TAR_AUTODETECT */
bb_error_msg_and_die("invalid tar magic"); bb_error_msg_and_die("invalid tar magic");
} }
#if ENABLE_FEATURE_TAR_AUTODETECT
not_first = 1;
#endif
/* Do checksum on headers. /* Do checksum on headers.
* POSIX says that checksum is done on unsigned bytes, but * POSIX says that checksum is done on unsigned bytes, but
* Sun and HP-UX gets it wrong... more details in * Sun and HP-UX gets it wrong... more details in

View File

@ -33,8 +33,17 @@
#define FNM_LEADING_DIR 0 #define FNM_LEADING_DIR 0
#endif #endif
#define block_buf bb_common_bufsiz1 #define block_buf bb_common_bufsiz1
#if !ENABLE_FEATURE_TAR_GZIP && !ENABLE_FEATURE_TAR_BZIP2
/* Do not pass gzip flag to writeTarFile() */
#define writeTarFile(tar_fd, verboseFlag, dereferenceFlag, include, exclude, gzip) \
writeTarFile(tar_fd, verboseFlag, dereferenceFlag, include, exclude)
#endif
#if ENABLE_FEATURE_TAR_CREATE #if ENABLE_FEATURE_TAR_CREATE
/* Tar file constants */ /* Tar file constants */
@ -514,18 +523,23 @@ static int writeTarFile(const int tar_fd, const int verboseFlag,
if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0) if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0)
bb_perror_msg_and_die("cannot stat tar file"); bb_perror_msg_and_die("cannot stat tar file");
if ((ENABLE_FEATURE_TAR_GZIP || ENABLE_FEATURE_TAR_BZIP2) && gzip) { #if ENABLE_FEATURE_TAR_GZIP || ENABLE_FEATURE_TAR_BZIP2
// On Linux, vfork never unpauses parent early, although standard if (gzip) {
// allows for that. Do we want to waste bytes checking for it? #if ENABLE_FEATURE_TAR_GZIP && ENABLE_FEATURE_TAR_BZIP2
const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2";
#elif ENABLE_FEATURE_TAR_GZIP
const char *zip_exec = "gzip";
#else /* only ENABLE_FEATURE_TAR_BZIP2 */
const char *zip_exec = "bzip2";
#endif
// On Linux, vfork never unpauses parent early, although standard
// allows for that. Do we want to waste bytes checking for it?
#define WAIT_FOR_CHILD 0 #define WAIT_FOR_CHILD 0
volatile int vfork_exec_errno = 0; volatile int vfork_exec_errno = 0;
#if WAIT_FOR_CHILD #if WAIT_FOR_CHILD
struct fd_pair gzipStatusPipe; struct fd_pair gzipStatusPipe;
#endif #endif
struct fd_pair gzipDataPipe; struct fd_pair gzipDataPipe;
const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2";
xpiped_pair(gzipDataPipe); xpiped_pair(gzipDataPipe);
#if WAIT_FOR_CHILD #if WAIT_FOR_CHILD
xpiped_pair(gzipStatusPipe); xpiped_pair(gzipStatusPipe);
@ -584,6 +598,7 @@ static int writeTarFile(const int tar_fd, const int verboseFlag,
bb_perror_msg_and_die("cannot exec %s", zip_exec); bb_perror_msg_and_die("cannot exec %s", zip_exec);
} }
} }
#endif
tbInfo.excludeList = exclude; tbInfo.excludeList = exclude;
@ -934,11 +949,13 @@ int tar_main(int argc, char **argv)
/* create an archive */ /* create an archive */
if (opt & OPT_CREATE) { if (opt & OPT_CREATE) {
#if ENABLE_FEATURE_TAR_GZIP || ENABLE_FEATURE_TAR_BZIP2
int zipMode = 0; int zipMode = 0;
if (ENABLE_FEATURE_TAR_GZIP && get_header_ptr == get_header_tar_gz) if (ENABLE_FEATURE_TAR_GZIP && (opt & OPT_GZIP))
zipMode = 1; zipMode = 1;
if (ENABLE_FEATURE_TAR_BZIP2 && get_header_ptr == get_header_tar_bz2) if (ENABLE_FEATURE_TAR_BZIP2 && (opt & OPT_BZIP2))
zipMode = 2; zipMode = 2;
#endif
/* NB: writeTarFile() closes tar_handle->src_fd */ /* NB: writeTarFile() closes tar_handle->src_fd */
return writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE, return writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE,
tar_handle->accept, tar_handle->accept,
@ -946,7 +963,7 @@ int tar_main(int argc, char **argv)
} }
while (get_header_ptr(tar_handle) == EXIT_SUCCESS) while (get_header_ptr(tar_handle) == EXIT_SUCCESS)
/* nothing */; continue;
/* Check that every file that should have been extracted was */ /* Check that every file that should have been extracted was */
while (tar_handle->accept) { while (tar_handle->accept) {