summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--abuild-tar.c199
1 files changed, 156 insertions, 43 deletions
diff --git a/abuild-tar.c b/abuild-tar.c
index 4a88882..2f29d30 100644
--- a/abuild-tar.c
+++ b/abuild-tar.c
@@ -1,6 +1,6 @@
/* abuild-tar.c - A TAR mangling utility for .APK packages
*
- * Copyright (C) 2009 Timo Teräs <timo.teras@iki.fi>
+ * Copyright (C) 2009-2014 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
@@ -89,6 +89,19 @@ static void put_octal(char *s, size_t l, size_t value)
*(ptr--) = '0';
}
+static void tarhdr_checksum(struct tar_header *hdr)
+{
+ const unsigned char *src;
+ size_t chksum, i;
+
+ /* Recalculate checksum */
+ memset(hdr->chksum, ' ', sizeof(hdr->chksum));
+ src = (const unsigned char *) hdr;
+ for (i = chksum = 0; i < sizeof(*hdr); i++)
+ chksum += src[i];
+ put_octal(hdr->chksum, sizeof(hdr->chksum)-1, chksum);
+}
+
static int usage(void)
{
fprintf(stderr,
@@ -120,7 +133,7 @@ static ssize_t full_read(int fd, void *buf, size_t count)
} while (1);
if (total == 0 && n < 0)
- return -1;
+ return -errno;
return total;
}
@@ -142,7 +155,7 @@ static ssize_t full_write(int fd, const void *buf, size_t count)
} while (1);
if (total == 0 && n < 0)
- return -1;
+ return -errno;
return total;
}
@@ -163,82 +176,182 @@ static ssize_t full_splice(int from_fd, int to_fd, size_t count)
} while (1);
if (total == 0 && n < 0)
- return -1;
+ return -errno;
return total;
}
-static int do_it(const EVP_MD *md, int cut)
+#define BUF_INITIALIZER {0}
+struct buf {
+ char *ptr;
+ size_t size;
+ size_t alloc;
+};
+
+static void buf_free(struct buf *b)
+{
+ free(b->ptr);
+}
+
+static int buf_resize(struct buf *b, size_t newsize)
{
- struct tar_header hdr;
- size_t size, aligned_size;
void *ptr;
- int dohash = 0, r;
+
+ if (b->alloc >= newsize) return 0;
+ ptr = realloc(b->ptr, newsize);
+ if (!ptr) return -ENOMEM;
+ b->ptr = ptr;
+ b->alloc = newsize;
+ return 0;
+}
+
+static int buf_padto(struct buf *b, size_t alignment)
+{
+ size_t oldsize, newsize;
+ oldsize = b->size;
+ newsize = (oldsize + alignment - 1) & -alignment;
+ if (buf_resize(b, newsize)) return -ENOMEM;
+ b->size = newsize;
+ memset(b->ptr + oldsize, 0, newsize - oldsize);
+ return 0;
+}
+
+static int buf_read_fd(struct buf *b, int fd, size_t size)
+{
+ ssize_t r;
+
+ r = buf_resize(b, size);
+ if (r) return r;
+
+ r = full_read(fd, b->ptr, size);
+ if (r == size) {
+ b->size = r;
+ return 0;
+ }
+ b->size = 0;
+ if (r < 0) return r;
+ return -EIO;
+}
+
+static int buf_write_fd(struct buf *b, int fd)
+{
+ ssize_t r;
+ r = full_write(fd, b->ptr, b->size);
+ if (r == b->size) return 0;
+ if (r < 0) return r;
+ return -ENOSPC;
+}
+
+static int buf_add_ext_header_hexdump(struct buf *b, const char *hdr, const char *value, int valuelen)
+{
+ size_t oldsize = b->size;
+ int i, len;
+
+ /* "%u %s=%s\n" */
+ len = 1 + 1 + strlen(hdr) + 1 + valuelen*2 + 1;
+ for (i = len; i > 9; i /= 10) len++;
+
+ if (buf_resize(b, b->size + len)) return -ENOMEM;
+ b->size += snprintf(&b->ptr[b->size], len, "%u %s=", len, hdr);
+ for (i = 0; i < valuelen; i++)
+ b->size += snprintf(&b->ptr[b->size], 3, "%02x", (int)(unsigned char)value[i]);
+ b->ptr[b->size++] = '\n';
+
+ return 0;
+}
+
+static void add_legacy_checksum(struct tar_header *hdr, const EVP_MD *md, const char *digest)
+{
struct {
char id[4];
uint16_t nid;
uint16_t size;
} mdinfo;
- if (md != NULL) {
- memcpy(mdinfo.id, "APK2", 4);
- mdinfo.nid = EVP_MD_nid(md);
- mdinfo.size = EVP_MD_size(md);
- }
+ memcpy(mdinfo.id, "APK2", 4);
+ mdinfo.nid = EVP_MD_nid(md);
+ mdinfo.size = EVP_MD_size(md);
+ memcpy(&hdr->linkname[3], &mdinfo, sizeof(mdinfo));
+ memcpy(&hdr->linkname[3+sizeof(mdinfo)], digest, mdinfo.size);
+ tarhdr_checksum(hdr);
+}
+
+static int do_it(const EVP_MD *md, int cut)
+{
+ char checksumhdr[32], digest[EVP_MAX_MD_SIZE];
+ struct buf data = BUF_INITIALIZER, pax = BUF_INITIALIZER;
+ struct tar_header hdr, paxhdr;
+ size_t size, aligned_size;
+ int dohash = 0, r, ret = 1;
+
+ if (md) snprintf(checksumhdr, sizeof(checksumhdr), "APK-TOOLS.checksum.%s", EVP_MD_name(md));
do {
if (full_read(STDIN_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
- return 0;
+ goto err;
if (cut && hdr.name[0] == 0)
- return 0;
+ break;
size = GET_OCTAL(hdr.size);
aligned_size = (size + 511) & ~511;
- if (md != NULL)
- dohash = (hdr.typeflag == '0' || hdr.typeflag == '7');
+ if (hdr.typeflag == 'x') {
+ memcpy(&paxhdr, &hdr, sizeof(hdr));
+ if (buf_read_fd(&pax, STDIN_FILENO, aligned_size)) goto err;
+ pax.size = size;
+ continue;
+ }
+
+ dohash = md && (hdr.typeflag == '0' || hdr.typeflag == '7' || hdr.typeflag == '2');
if (dohash) {
- const unsigned char *src;
- int chksum, i;
-
- ptr = malloc(aligned_size);
- if (full_read(STDIN_FILENO, ptr, aligned_size) != aligned_size)
- return 1;
-
- memcpy(&hdr.linkname[3], &mdinfo, sizeof(mdinfo));
- EVP_Digest(ptr, size, &hdr.linkname[3+sizeof(mdinfo)],
- NULL, md, NULL);
-
- /* Recalculate checksum */
- memset(hdr.chksum, ' ', sizeof(hdr.chksum));
- src = (const unsigned char *) &hdr;
- for (i = chksum = 0; i < sizeof(hdr); i++)
- chksum += src[i];
- put_octal(hdr.chksum, sizeof(hdr.chksum)-1, chksum);
+ if (buf_read_fd(&data, STDIN_FILENO, aligned_size)) goto err;
+
+ if (hdr.typeflag != '2') {
+ EVP_Digest(data.ptr, size, digest, NULL, md, NULL);
+ add_legacy_checksum(&hdr, md, digest);
+ } else {
+ EVP_Digest(hdr.linkname, strlen(hdr.linkname), digest, NULL, md, NULL);
+ }
+
+ buf_add_ext_header_hexdump(&pax, checksumhdr, digest, EVP_MD_size(md));
+ PUT_OCTAL(paxhdr.size, pax.size);
+ tarhdr_checksum(&paxhdr);
+ }
+
+ if (pax.size) {
+ /* write pax header + content */
+ if (full_write(STDOUT_FILENO, &paxhdr, sizeof(paxhdr)) != sizeof(paxhdr) ||
+ buf_padto(&pax, 512) ||
+ buf_write_fd(&pax, STDOUT_FILENO)) goto err;
}
- if (full_write(STDOUT_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
- return 2;
+ if (full_write(STDOUT_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr)) goto err;
if (dohash) {
- if (full_write(STDOUT_FILENO, ptr, aligned_size) != aligned_size)
- return 2;
- free(ptr);
+ if (buf_write_fd(&data, STDOUT_FILENO)) goto err;
} else if (aligned_size != 0) {
r = full_splice(STDIN_FILENO, STDOUT_FILENO, aligned_size);
if (r == -1) {
while (aligned_size > 0) {
if (full_read(STDIN_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
- return 1;
+ goto err;
if (full_write(STDOUT_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
- return 2;
+ goto err;
aligned_size -= sizeof(hdr);
}
- } else if (r != aligned_size)
- return 2;
+ } else if (r != aligned_size) goto err;
}
+
+ memset(&paxhdr, 0, sizeof(paxhdr));
+ pax.size = 0;
} while (1);
+
+ ret = 0;
+err:
+ buf_free(&data);
+ buf_free(&pax);
+ return ret;
}
int main(int argc, char **argv)