projects/aports·blob v2.25.0_rc1 · refs

394 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
165static ssize_t full_splice(int from_fd, int to_fd, size_t count)
166{
167 ssize_t total, n;
168
169 total = 0;
170 do {
171 n = splice(from_fd, NULL, to_fd, NULL, count, 0);
172 if (n < 0 && errno == EINTR)
173 continue;
174 if (n <= 0)
175 break;
176 count -= n;
177 total += n;
178 } while (1);
179
180 if (total == 0 && n < 0)
181 return -errno;
182
183 return total;
184}
185
186#define BUF_INITIALIZER {0}
187struct buf {
188 char *ptr;
189 size_t size;
190 size_t alloc;
191};
192
193static void buf_free(struct buf *b)
194{
195 free(b->ptr);
196}
197
198static int buf_resize(struct buf *b, size_t newsize)
199{
200 void *ptr;
201
202 if (b->alloc >= newsize) return 0;
203 ptr = realloc(b->ptr, newsize);
204 if (!ptr) return -ENOMEM;
205 b->ptr = ptr;
206 b->alloc = newsize;
207 return 0;
208}
209
210static int buf_padto(struct buf *b, size_t alignment)
211{
212 size_t oldsize, newsize;
213 oldsize = b->size;
214 newsize = (oldsize + alignment - 1) & -alignment;
215 if (buf_resize(b, newsize)) return -ENOMEM;
216 b->size = newsize;
217 memset(b->ptr + oldsize, 0, newsize - oldsize);
218 return 0;
219}
220
221static int buf_read_fd(struct buf *b, int fd, size_t size)
222{
223 ssize_t r;
224
225 r = buf_resize(b, size);
226 if (r) return r;
227
228 r = full_read(fd, b->ptr, size);
229 if (r == size) {
230 b->size = r;
231 return 0;
232 }
233 b->size = 0;
234 if (r < 0) return r;
235 return -EIO;
236}
237
238static int buf_write_fd(struct buf *b, int fd)
239{
240 ssize_t r;
241 r = full_write(fd, b->ptr, b->size);
242 if (r == b->size) return 0;
243 if (r < 0) return r;
244 return -ENOSPC;
245}
246
247static int buf_add_ext_header_hexdump(struct buf *b, const char *hdr, const char *value, int valuelen)
248{
249 size_t oldsize = b->size;
250 int i, len;
251
252 /* "%u %s=%s\n" */
253 len = 1 + 1 + strlen(hdr) + 1 + valuelen*2 + 1;
254 for (i = len; i > 9; i /= 10) len++;
255
256 if (buf_resize(b, b->size + len)) return -ENOMEM;
257 b->size += snprintf(&b->ptr[b->size], len, "%u %s=", len, hdr);
258 for (i = 0; i < valuelen; i++)
259 b->size += snprintf(&b->ptr[b->size], 3, "%02x", (int)(unsigned char)value[i]);
260 b->ptr[b->size++] = '\n';
261
262 return 0;
263}
264
265static void add_legacy_checksum(struct tar_header *hdr, const EVP_MD *md, const char *digest)
266{
267 struct {
268 char id[4];
269 uint16_t nid;
270 uint16_t size;
271 } mdinfo;
272
273 memcpy(mdinfo.id, "APK2", 4);
274 mdinfo.nid = EVP_MD_nid(md);
275 mdinfo.size = EVP_MD_size(md);
276 memcpy(&hdr->linkname[3], &mdinfo, sizeof(mdinfo));
277 memcpy(&hdr->linkname[3+sizeof(mdinfo)], digest, mdinfo.size);
278 tarhdr_checksum(hdr);
279}
280
281static int do_it(const EVP_MD *md, int cut)
282{
283 char checksumhdr[32], digest[EVP_MAX_MD_SIZE];
284 struct buf data = BUF_INITIALIZER, pax = BUF_INITIALIZER;
285 struct tar_header hdr, paxhdr;
286 size_t size, aligned_size;
287 int dohash = 0, r, ret = 1;
288
289 memset(&paxhdr, 0, sizeof(paxhdr));
290
291 if (md) snprintf(checksumhdr, sizeof(checksumhdr), "APK-TOOLS.checksum.%s", EVP_MD_name(md));
292
293 do {
294 if (full_read(STDIN_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
295 goto err;
296
297 if (cut && hdr.name[0] == 0)
298 break;
299
300 size = GET_OCTAL(hdr.size);
301 aligned_size = (size + 511) & ~511;
302
303 if (hdr.typeflag == 'x') {
304 memcpy(&paxhdr, &hdr, sizeof(hdr));
305 if (buf_read_fd(&pax, STDIN_FILENO, aligned_size)) goto err;
306 pax.size = size;
307 continue;
308 }
309
310 dohash = md && (hdr.typeflag == '0' || hdr.typeflag == '7' || hdr.typeflag == '2');
311 if (dohash) {
312 if (buf_read_fd(&data, STDIN_FILENO, aligned_size)) goto err;
313
314 if (hdr.typeflag != '2') {
315 EVP_Digest(data.ptr, size, digest, NULL, md, NULL);
316 add_legacy_checksum(&hdr, md, digest);
317 } else {
318 EVP_Digest(hdr.linkname, strlen(hdr.linkname), digest, NULL, md, NULL);
319 }
320
321 buf_add_ext_header_hexdump(&pax, checksumhdr, digest, EVP_MD_size(md));
322 PUT_OCTAL(paxhdr.size, pax.size);
323 tarhdr_checksum(&paxhdr);
324 }
325
326 if (pax.size) {
327 /* write pax header + content */
328 if (full_write(STDOUT_FILENO, &paxhdr, sizeof(paxhdr)) != sizeof(paxhdr) ||
329 buf_padto(&pax, 512) ||
330 buf_write_fd(&pax, STDOUT_FILENO)) goto err;
331 }
332
333 if (full_write(STDOUT_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr)) goto err;
334
335 if (dohash) {
336 if (buf_write_fd(&data, STDOUT_FILENO)) goto err;
337 } else if (aligned_size != 0) {
338 r = full_splice(STDIN_FILENO, STDOUT_FILENO, aligned_size);
339 if (r == -1) {
340 while (aligned_size > 0) {
341 if (full_read(STDIN_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
342 goto err;
343 if (full_write(STDOUT_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
344 goto err;
345 aligned_size -= sizeof(hdr);
346 }
347 } else if (r != aligned_size) goto err;
348 }
349
350 memset(&paxhdr, 0, sizeof(paxhdr));
351 pax.size = 0;
352 } while (1);
353
354 ret = 0;
355err:
356 buf_free(&data);
357 buf_free(&pax);
358 return ret;
359}
360
361int main(int argc, char **argv)
362{
363 static int cut = 0;
364 static const struct option options[] = {
365 { "hash", optional_argument },
366 { "cut", no_argument, &cut, 1 },
367 { NULL }
368 };
369 const EVP_MD *md = NULL;
370 char *digest = NULL;
371 int ndx;
372
373 OpenSSL_add_all_algorithms();
374 ENGINE_load_builtin_engines();
375 ENGINE_register_all_complete();
376
377 while (getopt_long(argc, argv, "", options, &ndx) != -1) {
378 if (ndx == 0)
379 digest = optarg ? optarg : "sha1";
380 }
381
382 if (digest == NULL && cut == 0)
383 return usage();
384 if (isatty(STDIN_FILENO))
385 return usage();
386
387 if (digest != NULL) {
388 md = EVP_get_digestbyname(digest);
389 if (md == NULL)
390 return usage();
391 }
392
393 return do_it(md, cut);
394}