header_verbose_list: show off_t size, not cast to (unsigned)

get_header_tar: support GNU tar's "base256" encoding

function                                             old     new   delta
get_header_tar                                      1536    1600     +64
header_verbose_list                                  242     257     +15
This commit is contained in:
Denis Vlasenko 2008-12-12 02:14:35 +00:00
parent 12abcb36c8
commit ab6b446e95
3 changed files with 76 additions and 4 deletions

View File

@ -14,6 +14,74 @@
#include "libbb.h" #include "libbb.h"
#include "unarchive.h" #include "unarchive.h"
/*
* GNU tar uses "base-256 encoding" for very large numbers (>8 billion).
* Encoding is binary, with highest bit always set as a marker
* and sign in next-highest bit:
* 80 00 .. 00 - zero
* bf ff .. ff - largest positive number
* ff ff .. ff - minus 1
* c0 00 .. 00 - smallest negative number
*
* We expect it only in size field, where negative numbers don't make sense.
*/
static off_t getBase256_len12(const char *str)
{
off_t value;
int len;
/* if (*str & 0x40) error; - caller prevents this */
if (sizeof(off_t) >= 12) {
/* Probably 128-bit (16 byte) off_t. Can be optimized. */
len = 12;
value = *str++ & 0x3f;
while (--len)
value = (value << 8) + (unsigned char) *str++;
return value;
}
#ifdef CHECK_FOR_OVERFLOW
/* Can be optimized to eat 32-bit chunks */
char c = *str++ & 0x3f;
len = 12;
while (1) {
if (c)
bb_error_msg_and_die("overflow in base-256 encoded file size");
if (--len == sizeof(off_t))
break;
c = *str++;
}
#else
str += (12 - sizeof(off_t));
#endif
/* Now str points to sizeof(off_t) least significant bytes.
*
* Example of tar file with 8914993153 (0x213600001) byte file.
* Field starts at offset 7c:
* 00070 30 30 30 00 30 30 30 30 30 30 30 00 80 00 00 00 |000.0000000.....|
* 00080 00 00 00 02 13 60 00 01 31 31 31 32 30 33 33 36 |.....`..11120336|
*
* str is at offset 80 or 84 now (64-bit or 32-bit off_t).
* We (ab)use the fact that value happens to be aligned,
* and fetch it in one go:
*/
if (sizeof(off_t) == 8) {
value = *(off_t*)str;
value = SWAP_BE64(value);
} else if (sizeof(off_t) == 4) {
value = *(off_t*)str;
value = SWAP_BE32(value);
} else {
value = 0;
len = sizeof(off_t);
while (--len)
value = (value << 8) + (unsigned char) *str++;
}
return value;
}
/* NB: _DESTROYS_ str[len] character! */ /* NB: _DESTROYS_ str[len] character! */
static unsigned long long getOctal(char *str, int len) static unsigned long long getOctal(char *str, int len)
{ {
@ -234,7 +302,10 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
file_header->gname = tar.gname[0] ? xstrndup(tar.gname, sizeof(tar.gname)) : NULL; file_header->gname = tar.gname[0] ? xstrndup(tar.gname, sizeof(tar.gname)) : NULL;
#endif #endif
file_header->mtime = GET_OCTAL(tar.mtime); file_header->mtime = GET_OCTAL(tar.mtime);
file_header->size = GET_OCTAL(tar.size); /* Size field: handle GNU tar's "base256 encoding" */
file_header->size = (*tar.size & 0xc0) == 0x80 /* positive base256? */
? getBase256_len12(tar.size)
: GET_OCTAL(tar.size);
file_header->gid = GET_OCTAL(tar.gid); file_header->gid = GET_OCTAL(tar.gid);
file_header->uid = GET_OCTAL(tar.uid); file_header->uid = GET_OCTAL(tar.uid);
/* Set bits 0-11 of the files mode */ /* Set bits 0-11 of the files mode */

View File

@ -24,11 +24,11 @@ void FAST_FUNC header_verbose_list(const file_header_t *file_header)
snprintf(gid, sizeof(gid), "%u", (unsigned)file_header->gid); snprintf(gid, sizeof(gid), "%u", (unsigned)file_header->gid);
group = gid; group = gid;
} }
printf("%s %s/%s %9u %4u-%02u-%02u %02u:%02u:%02u %s", printf("%s %s/%s %9"OFF_FMT"u %4u-%02u-%02u %02u:%02u:%02u %s",
bb_mode_string(file_header->mode), bb_mode_string(file_header->mode),
user, user,
group, group,
(unsigned int) file_header->size, file_header->size,
1900 + mtime->tm_year, 1900 + mtime->tm_year,
1 + mtime->tm_mon, 1 + mtime->tm_mon,
mtime->tm_mday, mtime->tm_mday,

View File

@ -357,7 +357,8 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
if (tbInfo->verboseFlag) { if (tbInfo->verboseFlag) {
FILE *vbFd = stdout; FILE *vbFd = stdout;
if (tbInfo->tarFd == STDOUT_FILENO) /* If the archive goes to stdout, verbose to stderr */ /* If archive goes to stdout, verbose goes to stderr */
if (tbInfo->tarFd == STDOUT_FILENO)
vbFd = stderr; vbFd = stderr;
/* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */ /* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */
/* We don't have such excesses here: for us "v" == "vv" */ /* We don't have such excesses here: for us "v" == "vv" */