Commit 36d5b914 authored by Timo Teräs's avatar Timo Teräs

support extended pax header in tar extractor

parent bb1ac255
......@@ -126,6 +126,7 @@ unsigned int apk_blob_pull_uint(apk_blob_t *b, int radix);
void apk_blob_pull_csum(apk_blob_t *b, struct apk_checksum *csum);
void apk_blob_pull_base64(apk_blob_t *b, apk_blob_t to);
void apk_blob_pull_hexdump(apk_blob_t *b, apk_blob_t to);
int apk_blob_pull_blob_match(apk_blob_t *b, apk_blob_t match);
void apk_atom_init(void);
apk_blob_t *apk_blob_atomize(apk_blob_t blob);
......
......@@ -116,6 +116,53 @@ static void tar_entry_close(void *stream)
{
}
static int blob_realloc(apk_blob_t *b, int newsize)
{
char *tmp;
if (b->len >= newsize) return 0;
tmp = realloc(b->ptr, newsize);
if (!tmp) return -ENOMEM;
b->ptr = tmp;
b->len = newsize;
return 0;
}
static void handle_extended_header(struct apk_file_info *fi, apk_blob_t hdr)
{
apk_blob_t name, value;
while (1) {
char *start = hdr.ptr;
unsigned int len = apk_blob_pull_uint(&hdr, 10);
apk_blob_pull_char(&hdr, ' ');
if (!apk_blob_split(hdr, APK_BLOB_STR("="), &name, &hdr)) break;
len -= hdr.ptr - start + 1;
if (len < 0 || hdr.len < len) break;
value = APK_BLOB_PTR_LEN(hdr.ptr, len);
hdr = APK_BLOB_PTR_LEN(hdr.ptr+len, hdr.len-len);
apk_blob_pull_char(&hdr, '\n');
if (APK_BLOB_IS_NULL(hdr)) break;
value.ptr[value.len] = 0;
if (apk_blob_compare(name, APK_BLOB_STR("path")) == 0) {
fi->name = value.ptr;
} else if (apk_blob_compare(name, APK_BLOB_STR("linkpath")) == 0) {
fi->link_target = value.ptr;
} else if (apk_blob_pull_blob_match(&name, APK_BLOB_STR("APK-TOOLS.checksum."))) {
int type = APK_CHECKSUM_NONE;
if (apk_blob_compare(name, APK_BLOB_STR("SHA1")) == 0)
type = APK_CHECKSUM_SHA1;
else if (apk_blob_compare(name, APK_BLOB_STR("MD5")) == 0)
type = APK_CHECKSUM_MD5;
if (type > fi->csum.type) {
fi->csum.type = type;
apk_blob_pull_hexdump(&value, APK_BLOB_CSUM(fi->csum));
if (APK_BLOB_IS_NULL(value)) fi->csum.type = APK_CHECKSUM_NONE;
}
}
}
}
int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser,
void *ctx, int soft_checksums, struct apk_id_cache *idc)
{
......@@ -126,20 +173,19 @@ int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser,
.tar_is = is,
};
struct tar_header buf;
struct apk_tar_digest_info *odi, *di;
struct apk_tar_digest_info *odi;
unsigned long offset = 0;
int end = 0, r;
size_t toskip;
size_t toskip, paxlen = 0;
apk_blob_t pax = APK_BLOB_NULL, longname = APK_BLOB_NULL;
odi = (struct apk_tar_digest_info *) &buf.linkname[3];
di = (struct apk_tar_digest_info *) &buf.devmajor[0];
EVP_MD_CTX_init(&teis.mdctx);
memset(&entry, 0, sizeof(entry));
while ((r = is->read(is, &buf, 512)) == 512) {
offset += 512;
if (buf.name[0] == '\0') {
if (end)
break;
if (end) break;
end++;
continue;
}
......@@ -150,46 +196,40 @@ int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser,
.gid = apk_resolve_gid(idc, buf.gname, GET_OCTAL(buf.gid)),
.mode = GET_OCTAL(buf.mode) & 07777,
.mtime = GET_OCTAL(buf.mtime),
.name = entry.name,
.name = entry.name ?: buf.name,
.uname = buf.uname,
.gname = buf.gname,
.device = makedev(GET_OCTAL(buf.devmajor),
GET_OCTAL(buf.devminor)),
};
teis.csum = NULL;
buf.mode[0] = 0; /* to nul terminate 100-byte buf.name */
buf.magic[0] = 0; /* to nul terminate 100-byte buf.linkname */
teis.csum = NULL;
if (memcmp(di->id, "APK2", 4) == 0 &&
di->size <= sizeof(entry.csum.data)) {
entry.csum.type = di->size;
memcpy(&entry.csum.data[0], buf.devminor, sizeof(buf.devminor));
memcpy(&entry.csum.data[sizeof(buf.devminor)], buf.padding, sizeof(buf.padding));
}
if (paxlen) handle_extended_header(&entry, APK_BLOB_PTR_LEN(pax.ptr, paxlen));
switch (buf.typeflag) {
case 'L':
if (entry.name != NULL)
free(entry.name);
entry.name = malloc(entry.size+1);
case 'L': /* GNU long name extension */
if (blob_realloc(&longname, entry.size+1)) goto err_nomem;
entry.name = longname.ptr;
is->read(is, entry.name, entry.size);
entry.name[entry.size] = 0;
offset += entry.size;
entry.size = 0;
break;
case 'K': /* GNU long link target extension - ignored */
break;
case '0':
case '7': /* regular file */
entry.mode |= S_IFREG;
if (entry.csum.type == APK_CHECKSUM_NONE) {
if (memcmp(odi->id, "APK2", 4) == 0 &&
odi->size <= sizeof(entry.csum.data)) {
entry.csum.type = odi->size;
memcpy(entry.csum.data, odi->digest,
odi->size);
memcpy(entry.csum.data, odi->digest, odi->size);
} else if (soft_checksums)
teis.csum = &entry.csum;
}
entry.mode |= S_IFREG;
break;
case '1': /* hard link */
entry.mode |= S_IFREG;
......@@ -198,8 +238,7 @@ int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser,
case '2': /* symbolic link */
entry.mode |= S_IFLNK;
entry.link_target = buf.linkname;
if (entry.csum.type == APK_CHECKSUM_NONE &&
soft_checksums) {
if (entry.csum.type == APK_CHECKSUM_NONE && soft_checksums) {
EVP_Digest(buf.linkname, strlen(buf.linkname),
entry.csum.data, NULL,
apk_checksum_default(), NULL);
......@@ -215,26 +254,31 @@ int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser,
case '5': /* directory */
entry.mode |= S_IFDIR;
break;
case 'g': /* global pax header */
break;
case 'x': /* file specific pax header */
paxlen = entry.size;
entry.size = 0;
if (blob_realloc(&pax, (paxlen + 511) & -512)) goto err_nomem;
is->read(is, pax.ptr, paxlen);
offset += paxlen;
break;
default:
break;
}
teis.bytes_left = entry.size;
if (entry.mode & S_IFMT) {
if (entry.name == NULL)
entry.name = strdup(buf.name);
/* callback parser function */
if (teis.csum != NULL)
EVP_DigestInit_ex(&teis.mdctx,
apk_checksum_default(), NULL);
r = parser(ctx, &entry, &teis.is);
free(entry.name);
if (r != 0)
goto err;
if (r != 0) goto err;
entry.name = NULL;
entry.name = buf.name;
paxlen = 0;
}
offset += entry.size - teis.bytes_left;
......@@ -245,26 +289,27 @@ int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser,
if (toskip != 0)
is->read(is, NULL, toskip);
}
EVP_MD_CTX_cleanup(&teis.mdctx);
/* Read remaining end-of-archive records, to ensure we read all of
* the file. The underlying istream is likely doing checksumming. */
if (r == 512) {
while ((r = is->read(is, &buf, 512)) == 512) {
if (buf.name[0] != 0)
return -EBADMSG;
if (buf.name[0] != 0) break;
}
}
/* Check that there was no partial record */
if (r > 0)
r = -EBADMSG;
return r;
/* Check that there was no partial (or non-zero) record */
if (r > 0) r = -EBADMSG;
err:
EVP_MD_CTX_cleanup(&teis.mdctx);
free(pax.ptr);
free(longname.ptr);
return r;
err_nomem:
r = -ENOMEM;
goto err;
}
int apk_tar_write_entry(struct apk_ostream *os, const struct apk_file_info *ae,
......
......@@ -492,6 +492,15 @@ err:
*b = APK_BLOB_NULL;
}
int apk_blob_pull_blob_match(apk_blob_t *b, apk_blob_t match)
{
if (b->len < match.len) return 0;
if (memcmp(b->ptr, match.ptr, match.len) != 0) return 0;
b->ptr += match.len;
b->len -= match.len;
return 1;
}
static unsigned char b64decode[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment