abuild-tar.c 5.52 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/* abuild-tar.c - A TAR mangling utility for .APK packages
 *
 * Copyright (C) 2009 Timo Teräs <timo.teras@iki.fi>
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation. See http://www.gnu.org/ for details.
 */

#include <stdio.h>
#include <fcntl.h>
13 14
#include <errno.h>
#include <stdint.h>
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
#include <getopt.h>
#include <unistd.h>
#include <string.h>

#include <openssl/evp.h>

#ifndef VERSION
#define VERSION ""
#endif

struct tar_header {
	/* ustar header, Posix 1003.1 */
	char name[100];     /*   0-99 */
	char mode[8];       /* 100-107 */
	char uid[8];        /* 108-115 */
	char gid[8];        /* 116-123 */
	char size[12];      /* 124-135 */
	char mtime[12];     /* 136-147 */
	char chksum[8];     /* 148-155 */
	char typeflag;      /* 156-156 */
	char linkname[100]; /* 157-256 */
	char magic[8];      /* 257-264 */
	char uname[32];     /* 265-296 */
	char gname[32];     /* 297-328 */
	char devmajor[8];   /* 329-336 */
	char devminor[8];   /* 337-344 */
	char prefix[155];   /* 345-499 */
	char padding[12];   /* 500-512 */
};

#define GET_OCTAL(s)	get_octal(s, sizeof(s))
#define PUT_OCTAL(s,v)	put_octal(s, sizeof(s), v)

static inline int dx(int c)
{
	if (c >= '0' && c <= '9')
		return c - '0';
	if (c >= 'a' && c <= 'f')
		return c - 'a' + 0xa;
	if (c >= 'A' && c <= 'F')
		return c - 'A' + 0xa;
	return -1;
}

static int get_octal(char *s, size_t l)
{
	unsigned int val;
	int ch;

	val = 0;
	while (l && s[0] != 0) {
		ch = dx(s[0]);
		if (ch < 0 || ch >= 8)
			break;
		val *= 8;
		val += ch;

		s++;
		l--;
	}

	return val;
}

static void put_octal(char *s, size_t l, size_t value)
{
	char *ptr = &s[l - 1];

	*(ptr--) = '\0';
	while (value != 0 && ptr >= s) {
		*(ptr--) = '0' + (value % 8);
		value /= 8;
	}
	while (ptr >= s)
		*(ptr--) = '0';
}

static int usage(void)
{
	fprintf(stderr,
"abuild-tar " VERSION "\n"
"\n"
Timo Teräs's avatar
Timo Teräs committed
97
"usage: abuild-tar [--hash [<algorithm>]] [--cut]\n"
98 99 100 101
"\n"
"options:\n"
"  --hash [sha1|md5]	Read tar archive from stdin, precalculate hash for \n"
"			regular	entries and output tar archive on stdout\n"
Timo Teräs's avatar
Timo Teräs committed
102
"  --cut		Remove the end of file tar record\n"
103 104 105
"\n");
}

106 107 108 109 110 111 112 113 114 115 116 117
static ssize_t full_read(int fd, void *buf, size_t count)
{
	ssize_t total, n;

	total = 0;
	do {
		n = read(fd, buf, count);
		if (n < 0 && errno == EINTR)
			continue;
		if (n <= 0)
			break;
		buf += n;
118
		total += n;
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
		count -= n;
	} while (1);

	if (total == 0 && n < 0)
		return -1;

	return total;
}

static ssize_t full_write(int fd, const void *buf, size_t count)
{
	ssize_t total, n;

	total = 0;
	do {
		n = write(fd, buf, count);
		if (n < 0 && errno == EINTR)
			continue;
		if (n <= 0)
			break;
		buf += n;
140
		total += n;
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
		count -= n;
	} while (1);

	if (total == 0 && n < 0)
		return -1;

	return total;
}

static ssize_t full_splice(int from_fd, int to_fd, size_t count)
{
	ssize_t total, n;

	total = 0;
	do {
156
		n = splice(from_fd, NULL, to_fd, NULL, count, 0);
157 158 159 160 161
		if (n < 0 && errno == EINTR)
			continue;
		if (n <= 0)
			break;
		count -= n;
162
		total += n;
163 164 165 166 167 168 169 170
	} while (1);

	if (total == 0 && n < 0)
		return -1;

	return total;
}

171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
static int do_it(const EVP_MD *md, int cut)
{
	struct tar_header hdr;
	size_t size, aligned_size;
	void *ptr;
	int dohash = 0, r;
	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);
	}

	do {
190
		if (full_read(STDIN_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
			return 0;

		if (cut && hdr.name[0] == 0)
			return 0;

		size = GET_OCTAL(hdr.size);
		aligned_size = (size + 511) & ~511;

		if (md != NULL)
			dohash = (hdr.typeflag == '0' || hdr.typeflag == '7');
		if (dohash) {
			const unsigned char *src;
			int chksum, i;

			ptr = malloc(aligned_size);
206
			if (full_read(STDIN_FILENO, ptr, aligned_size) != aligned_size)
207 208 209 210 211 212 213 214 215 216 217 218 219 220
				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);
		}

221
		if (full_write(STDOUT_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
222 223 224
			return 2;

		if (dohash) {
225
			if (full_write(STDOUT_FILENO, ptr, aligned_size) != aligned_size)
226 227 228
				return 2;
			free(ptr);
		} else if (aligned_size != 0) {
229
			r = full_splice(STDIN_FILENO, STDOUT_FILENO, aligned_size);
230 231
			if (r == -1) {
				while (aligned_size > 0) {
232
					if (full_read(STDIN_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
233
						return 1;
234
					if (full_write(STDOUT_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
235 236 237 238 239 240 241 242 243 244 245 246 247 248
						return 2;
					aligned_size -= sizeof(hdr);
				}
			} else if (r != aligned_size)
				return 2;
		}
	} while (1);
}

int main(int argc, char **argv)
{
	static int cut = 0;
	static const struct option options[] = {
		{ "hash", optional_argument },
249
		{ "cut", no_argument, &cut, 1 },
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
		{ NULL }
	};
	const EVP_MD *md = NULL;
	char *digest = NULL;
	int ndx;

	OpenSSL_add_all_algorithms();
	ENGINE_load_builtin_engines();
	ENGINE_register_all_complete();

	while (getopt_long(argc, argv, "", options, &ndx) != -1) {
		if (ndx == 0)
			digest = optarg ? optarg : "sha1";
	}

	if (digest == NULL && cut == 0)
		return usage();
	if (isatty(STDIN_FILENO))
		return usage();

	if (digest != NULL) {
		md = EVP_get_digestbyname(digest);
		if (md == NULL)
			return usage();
	}

	return do_it(md, cut);
}