projects/aports·blob v2.23.0_rc1 · refs

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