projects/aports·blob v2.27.2 · refs

398 lines · 8.3 KiB

1/* abuild-tar.c - A TAR mangling utility for .APK packages
2 *
3 * Copyright (C) 2009-2014 Timo Teräs <[email protected]>
4 * All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published
8 * by the Free Software Foundation. See http://www.gnu.org/ for details.
9 */
10
11#define _GNU_SOURCE
12#include <stdio.h>
13#include <fcntl.h>
14#include <errno.h>
15#include <stdint.h>
16#include <getopt.h>
17#include <unistd.h>
18#include <string.h>
19
20#include <openssl/evp.h>
21#include <openssl/engine.h>
22
23#ifndef VERSION
24#define VERSION ""
25#endif
26
27struct tar_header {
28 /* ustar header, Posix 1003.1 */
29 char name[100]; /* 0-99 */
30 char mode[8]; /* 100-107 */
31 char uid[8]; /* 108-115 */
32 char gid[8]; /* 116-123 */
33 char size[12]; /* 124-135 */
34 char mtime[12]; /* 136-147 */
35 char chksum[8]; /* 148-155 */
36 char typeflag; /* 156-156 */
37 char linkname[100]; /* 157-256 */
38 char magic[8]; /* 257-264 */
39 char uname[32]; /* 265-296 */
40 char gname[32]; /* 297-328 */
41 char devmajor[8]; /* 329-336 */
42 char devminor[8]; /* 337-344 */
43 char prefix[155]; /* 345-499 */
44 char padding[12]; /* 500-511 */
45};
46
47#define GET_OCTAL(s) get_octal(s, sizeof(s))
48#define PUT_OCTAL(s,v) put_octal(s, sizeof(s), v)
49
50static inline int dx(int c)
51{
52 if (c >= '0' && c <= '9')
53 return c - '0';
54 if (c >= 'a' && c <= 'f')
55 return c - 'a' + 0xa;
56 if (c >= 'A' && c <= 'F')
57 return c - 'A' + 0xa;
58 return -1;
59}
60
61static int get_octal(char *s, size_t l)
62{
63 unsigned int val;
64 int ch;
65
66 val = 0;
67 while (l && s[0] != 0) {
68 ch = dx(s[0]);
69 if (ch < 0 || ch >= 8)
70 break;
71 val *= 8;
72 val += ch;
73
74 s++;
75 l--;
76 }
77
78 return val;
79}
80
81static void put_octal(char *s, size_t l, size_t value)
82{
83 char *ptr = &s[l - 1];
84
85 *(ptr--) = '\0';
86 while (value != 0 && ptr >= s) {
87 *(ptr--) = '0' + (value % 8);
88 value /= 8;
89 }
90 while (ptr >= s)
91 *(ptr--) = '0';
92}
93
94static void tarhdr_checksum(struct tar_header *hdr)
95{
96 const unsigned char *src;
97 size_t chksum, i;
98
99 /* Recalculate checksum */
100 memset(hdr->chksum, ' ', sizeof(hdr->chksum));
101 src = (const unsigned char *) hdr;
102 for (i = chksum = 0; i < sizeof(*hdr); i++)
103 chksum += src[i];
104 put_octal(hdr->chksum, sizeof(hdr->chksum)-1, chksum);
105}
106
107static int usage(void)
108{
109 fprintf(stderr,
110"abuild-tar " VERSION "\n"
111"\n"
112"usage: abuild-tar [--hash[=<algorithm>]] [--cut]\n"
113"\n"
114"options:\n"
115" --hash[=sha1|md5] Read tar archive from stdin, precalculate hash for \n"
116" regular entries and output tar archive on stdout\n"
117" --cut Remove the end of file tar record\n"
118"\n");
119}
120
121static ssize_t full_read(int fd, void *buf, size_t count)
122{
123 ssize_t total, n;
124
125 total = 0;
126 do {
127 n = read(fd, buf, count);
128 if (n < 0 && errno == EINTR)
129 continue;
130 if (n <= 0)
131 break;
132 buf += n;
133 total += n;
134 count -= n;
135 } while (1);
136
137 if (total == 0 && n < 0)
138 return -errno;
139
140 return total;
141}
142
143static ssize_t full_write(int fd, const void *buf, size_t count)
144{
145 ssize_t total, n;
146
147 total = 0;
148 do {
149 n = write(fd, buf, count);
150 if (n < 0 && errno == EINTR)
151 continue;
152 if (n <= 0)
153 break;
154 buf += n;
155 total += n;
156 count -= n;
157 } while (1);
158
159 if (total == 0 && n < 0)
160 return -errno;
161
162 return total;
163}
164
165#if defined(__linux__)
166static ssize_t full_splice(int from_fd, int to_fd, size_t count)
167{
168 ssize_t total, n;
169
170 total = 0;
171 do {
172 n = splice(from_fd, NULL, to_fd, NULL, count, 0);
173 if (n < 0 && errno == EINTR)
174 continue;
175 if (n <= 0)
176 break;
177 count -= n;
178 total += n;
179 } while (1);
180
181 if (total == 0 && n < 0)
182 return -errno;
183
184 return total;
185}
186#else
187#define full_splice(from_fd, to_fd, count) -1
188#endif
189
190#define BUF_INITIALIZER {0}
191struct buf {
192 char *ptr;
193 size_t size;
194 size_t alloc;
195};
196
197static void buf_free(struct buf *b)
198{
199 free(b->ptr);
200}
201
202static int buf_resize(struct buf *b, size_t newsize)
203{
204 void *ptr;
205
206 if (b->alloc >= newsize) return 0;
207 ptr = realloc(b->ptr, newsize);
208 if (!ptr) return -ENOMEM;
209 b->ptr = ptr;
210 b->alloc = newsize;
211 return 0;
212}
213
214static int buf_padto(struct buf *b, size_t alignment)
215{
216 size_t oldsize, newsize;
217 oldsize = b->size;
218 newsize = (oldsize + alignment - 1) & -alignment;
219 if (buf_resize(b, newsize)) return -ENOMEM;
220 b->size = newsize;
221 memset(b->ptr + oldsize, 0, newsize - oldsize);
222 return 0;
223}
224
225static int buf_read_fd(struct buf *b, int fd, size_t size)
226{
227 ssize_t r;
228
229 r = buf_resize(b, size);
230 if (r) return r;
231
232 r = full_read(fd, b->ptr, size);
233 if (r == size) {
234 b->size = r;
235 return 0;
236 }
237 b->size = 0;
238 if (r < 0) return r;
239 return -EIO;
240}
241
242static int buf_write_fd(struct buf *b, int fd)
243{
244 ssize_t r;
245 r = full_write(fd, b->ptr, b->size);
246 if (r == b->size) return 0;
247 if (r < 0) return r;
248 return -ENOSPC;
249}
250
251static int buf_add_ext_header_hexdump(struct buf *b, const char *hdr, const char *value, int valuelen)
252{
253 size_t oldsize = b->size;
254 int i, len;
255
256 /* "%u %s=%s\n" */
257 len = 1 + 1 + strlen(hdr) + 1 + valuelen*2 + 1;
258 for (i = len; i > 9; i /= 10) len++;
259
260 if (buf_resize(b, b->size + len)) return -ENOMEM;
261 b->size += snprintf(&b->ptr[b->size], len, "%u %s=", len, hdr);
262 for (i = 0; i < valuelen; i++)
263 b->size += snprintf(&b->ptr[b->size], 3, "%02x", (int)(unsigned char)value[i]);
264 b->ptr[b->size++] = '\n';
265
266 return 0;
267}
268
269static void add_legacy_checksum(struct tar_header *hdr, const EVP_MD *md, const char *digest)
270{
271 struct {
272 char id[4];
273 uint16_t nid;
274 uint16_t size;
275 } mdinfo;
276
277 memcpy(mdinfo.id, "APK2", 4);
278 mdinfo.nid = EVP_MD_nid(md);
279 mdinfo.size = EVP_MD_size(md);
280 memcpy(&hdr->linkname[3], &mdinfo, sizeof(mdinfo));
281 memcpy(&hdr->linkname[3+sizeof(mdinfo)], digest, mdinfo.size);
282 tarhdr_checksum(hdr);
283}
284
285static int do_it(const EVP_MD *md, int cut)
286{
287 char checksumhdr[32], digest[EVP_MAX_MD_SIZE];
288 struct buf data = BUF_INITIALIZER, pax = BUF_INITIALIZER;
289 struct tar_header hdr, paxhdr;
290 size_t size, aligned_size;
291 int dohash = 0, r, ret = 1;
292
293 memset(&paxhdr, 0, sizeof(paxhdr));
294
295 if (md) snprintf(checksumhdr, sizeof(checksumhdr), "APK-TOOLS.checksum.%s", EVP_MD_name(md));
296
297 do {
298 if (full_read(STDIN_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
299 goto err;
300
301 if (cut && hdr.name[0] == 0)
302 break;
303
304 size = GET_OCTAL(hdr.size);
305 aligned_size = (size + 511) & ~511;
306
307 if (hdr.typeflag == 'x') {
308 memcpy(&paxhdr, &hdr, sizeof(hdr));
309 if (buf_read_fd(&pax, STDIN_FILENO, aligned_size)) goto err;
310 pax.size = size;
311 continue;
312 }
313
314 dohash = md && (hdr.typeflag == '0' || hdr.typeflag == '7' || hdr.typeflag == '2');
315 if (dohash) {
316 if (buf_read_fd(&data, STDIN_FILENO, aligned_size)) goto err;
317
318 if (hdr.typeflag != '2') {
319 EVP_Digest(data.ptr, size, digest, NULL, md, NULL);
320 add_legacy_checksum(&hdr, md, digest);
321 } else {
322 EVP_Digest(hdr.linkname, strlen(hdr.linkname), digest, NULL, md, NULL);
323 }
324
325 buf_add_ext_header_hexdump(&pax, checksumhdr, digest, EVP_MD_size(md));
326 PUT_OCTAL(paxhdr.size, pax.size);
327 tarhdr_checksum(&paxhdr);
328 }
329
330 if (pax.size) {
331 /* write pax header + content */
332 if (full_write(STDOUT_FILENO, &paxhdr, sizeof(paxhdr)) != sizeof(paxhdr) ||
333 buf_padto(&pax, 512) ||
334 buf_write_fd(&pax, STDOUT_FILENO)) goto err;
335 }
336
337 if (full_write(STDOUT_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr)) goto err;
338
339 if (dohash) {
340 if (buf_write_fd(&data, STDOUT_FILENO)) goto err;
341 } else if (aligned_size != 0) {
342 r = full_splice(STDIN_FILENO, STDOUT_FILENO, aligned_size);
343 if (r == -1) {
344 while (aligned_size > 0) {
345 if (full_read(STDIN_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
346 goto err;
347 if (full_write(STDOUT_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
348 goto err;
349 aligned_size -= sizeof(hdr);
350 }
351 } else if (r != aligned_size) goto err;
352 }
353
354 memset(&paxhdr, 0, sizeof(paxhdr));
355 pax.size = 0;
356 } while (1);
357
358 ret = 0;
359err:
360 buf_free(&data);
361 buf_free(&pax);
362 return ret;
363}
364
365int main(int argc, char **argv)
366{
367 static int cut = 0;
368 static const struct option options[] = {
369 { "hash", optional_argument },
370 { "cut", no_argument, &cut, 1 },
371 { NULL }
372 };
373 const EVP_MD *md = NULL;
374 char *digest = NULL;
375 int ndx;
376
377 OpenSSL_add_all_algorithms();
378 ENGINE_load_builtin_engines();
379 ENGINE_register_all_complete();
380
381 while (getopt_long(argc, argv, "", options, &ndx) != -1) {
382 if (ndx == 0)
383 digest = optarg ? optarg : "sha1";
384 }
385
386 if (digest == NULL && cut == 0)
387 return usage();
388 if (isatty(STDIN_FILENO))
389 return usage();
390
391 if (digest != NULL) {
392 md = EVP_get_digestbyname(digest);
393 if (md == NULL)
394 return usage();
395 }
396
397 return do_it(md, cut);
398}