diff --git a/src/apk_blob.h b/src/apk_blob.h index e22a2f4..c19fb95 100644 --- a/src/apk_blob.h +++ b/src/apk_blob.h @@ -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); diff --git a/src/archive.c b/src/archive.c index 66b4870..6a0774b 100644 --- a/src/archive.c +++ b/src/archive.c @@ -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, diff --git a/src/blob.c b/src/blob.c index fc21467..f1fe221 100644 --- a/src/blob.c +++ b/src/blob.c @@ -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,