package.c 22.1 KB
Newer Older
1
2
3
4
5
6
/* package.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
 * Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
 * All rights reserved.
 *
7
 * This program is free software; you can redistribute it and/or modify it
8
9
10
11
 * 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.
 */

12
#include <errno.h>
13
14
15
#include <fcntl.h>
#include <ctype.h>
#include <stdio.h>
16
#include <limits.h>
17
18
19
20
21
22
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

Timo Teräs's avatar
Timo Teräs committed
23
24
#include <openssl/pem.h>

25
26
27
28
29
30
#include "apk_defines.h"
#include "apk_archive.h"
#include "apk_package.h"
#include "apk_database.h"
#include "apk_state.h"

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
void apk_pkg_format_plain(struct apk_package *pkg, apk_blob_t to)
{
	/* pkgname-1.0.apk */
	apk_blob_push_blob(&to, APK_BLOB_STR(pkg->name->name));
	apk_blob_push_blob(&to, APK_BLOB_STR("-"));
	apk_blob_push_blob(&to, APK_BLOB_STR(pkg->version));
	apk_blob_push_blob(&to, APK_BLOB_STR(".apk"));
	apk_blob_push_blob(&to, APK_BLOB_PTR_LEN("", 1));
}

void apk_pkg_format_cache(struct apk_package *pkg, apk_blob_t to)
{
	/* pkgname-1.0_alpha1.12345678.apk */
	apk_blob_push_blob(&to, APK_BLOB_STR(pkg->name->name));
	apk_blob_push_blob(&to, APK_BLOB_STR("-"));
	apk_blob_push_blob(&to, APK_BLOB_STR(pkg->version));
	apk_blob_push_blob(&to, APK_BLOB_STR("."));
	apk_blob_push_hexdump(&to, APK_BLOB_PTR_LEN((char *) pkg->csum.data,
						    APK_CACHE_CSUM_BYTES));
	apk_blob_push_blob(&to, APK_BLOB_STR(".apk"));
	apk_blob_push_blob(&to, APK_BLOB_PTR_LEN("", 1));
}

54
struct apk_package *apk_pkg_new(void)
55
56
57
58
59
60
61
62
63
64
{
	struct apk_package *pkg;

	pkg = calloc(1, sizeof(struct apk_package));
	if (pkg != NULL)
		list_init(&pkg->installed_pkgs_list);

	return pkg;
}

65
66
67
68
69
70
int apk_pkg_parse_name(apk_blob_t apkname,
		       apk_blob_t *name,
		       apk_blob_t *version)
{
	int i, dash = 0;

71
72
73
	if (APK_BLOB_IS_NULL(apkname))
		return -1;

74
75
76
77
78
79
80
81
	for (i = apkname.len - 2; i >= 0; i--) {
		if (apkname.ptr[i] != '-')
			continue;
		if (isdigit(apkname.ptr[i+1]))
			break;
		if (++dash >= 2)
			return -1;
	}
82
83
84
	if (i < 0)
		return -1;

85
86
87
88
89
90
91
92
93
	if (name != NULL)
		*name = APK_BLOB_PTR_LEN(apkname.ptr, i);
	if (version != NULL)
		*version = APK_BLOB_PTR_PTR(&apkname.ptr[i+1],
					    &apkname.ptr[apkname.len-1]);

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
94
static apk_blob_t trim(apk_blob_t str)
95
96
{
	if (str.ptr == NULL || str.len < 1)
Timo Teräs's avatar
Timo Teräs committed
97
		return str;
98

99
100
	if (str.ptr[str.len-1] == '\n') {
		str.ptr[str.len-1] = 0;
Timo Teräs's avatar
Timo Teräs committed
101
		return APK_BLOB_PTR_LEN(str.ptr, str.len-1);
102
	}
103

Timo Teräs's avatar
Timo Teräs committed
104
	return str;
105
106
107
108
109
110
111
112
113
114
}

int apk_deps_add(struct apk_dependency_array **depends,
		 struct apk_dependency *dep)
{
	struct apk_dependency_array *deps = *depends;
	int i;

	if (deps != NULL) {
		for (i = 0; i < deps->num; i++) {
Timo Teräs's avatar
Timo Teräs committed
115
116
			if (deps->item[i].name == dep->name) {
				deps->item[i] = *dep;
117
				return 0;
Timo Teräs's avatar
Timo Teräs committed
118
			}
119
120
121
122
123
124
		}
	}

	*apk_dependency_array_add(depends) = *dep;
	return 0;
}
Timo Teräs's avatar
Timo Teräs committed
125

126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
void apk_deps_del(struct apk_dependency_array **pdeps,
		  struct apk_name *name)
{
	struct apk_dependency_array *deps = *pdeps;
	int i;

	if (deps == NULL)
		return;

	for (i = 0; i < deps->num; i++) {
		if (deps->item[i].name != name)
			continue;

		deps->item[i] = deps->item[deps->num-1];
		*pdeps = apk_dependency_array_resize(deps, deps->num-1);
		break;
	}
}

145
146
147
148
149
struct parse_depend_ctx {
	struct apk_database *db;
	struct apk_dependency_array **depends;
};

Timo Teräs's avatar
Timo Teräs committed
150
151
int apk_dep_from_blob(struct apk_dependency *dep, struct apk_database *db,
		      apk_blob_t blob)
152
153
{
	struct apk_name *name;
154
155
	apk_blob_t bname, bop, bver = APK_BLOB_NULL;
	int mask = APK_VERSION_LESS | APK_VERSION_EQUAL | APK_VERSION_GREATER;
156

157
158
159
160
161
162
163
164
165
166
	/* [!]name[<,<=,=,>=,>]ver */
	if (blob.ptr[0] == '!') {
		mask = 0;
		blob.ptr++;
		blob.len--;
	}
	if (apk_blob_cspn(blob, "<>=", &bname, &bop)) {
		int i;

		if (mask == 0)
Timo Teräs's avatar
Timo Teräs committed
167
			return -EINVAL;
168
		if (!apk_blob_spn(bop, "<>=", &bop, &bver))
Timo Teräs's avatar
Timo Teräs committed
169
170
171
172
			return -EINVAL;
		mask = 0;
		for (i = 0; i < bop.len; i++) {
			switch (bop.ptr[i]) {
173
174
175
176
177
178
179
180
181
182
183
184
185
			case '<':
				mask |= APK_VERSION_LESS;
				break;
			case '>':
				mask |= APK_VERSION_GREATER;
				break;
			case '=':
				mask |= APK_VERSION_EQUAL;
				break;
			}
		}
		if ((mask & (APK_VERSION_LESS|APK_VERSION_GREATER))
		    == (APK_VERSION_LESS|APK_VERSION_GREATER))
Timo Teräs's avatar
Timo Teräs committed
186
			return -EINVAL;
187
188

		if (!apk_version_validate(bver))
Timo Teräs's avatar
Timo Teräs committed
189
190
191
			return -EINVAL;

		blob = bname;
192
193
	}

Timo Teräs's avatar
Timo Teräs committed
194
	name = apk_db_get_name(db, blob);
195
	if (name == NULL)
Timo Teräs's avatar
Timo Teräs committed
196
		return -ENOENT;
197
198
199

	*dep = (struct apk_dependency){
		.name = name,
200
201
		.version = APK_BLOB_IS_NULL(bver) ? NULL : apk_blob_cstr(bver),
		.result_mask = mask,
202
	};
Timo Teräs's avatar
Timo Teräs committed
203
204
205
206
207
208
209
	return 0;
}

void apk_dep_from_pkg(struct apk_dependency *dep, struct apk_database *db,
		      struct apk_package *pkg)
{
	*dep = (struct apk_dependency) {
210
		.name = pkg->name,
Timo Teräs's avatar
Timo Teräs committed
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
		.version = pkg->version,
		.result_mask = APK_VERSION_EQUAL,
	};
}

static int parse_depend(void *ctx, apk_blob_t blob)
{
	struct parse_depend_ctx *pctx = (struct parse_depend_ctx *) ctx;
	struct apk_dependency *dep, p;

	if (blob.len == 0)
		return 0;

	if (apk_dep_from_blob(&p, pctx->db, blob) < 0)
		return -1;

	dep = apk_dependency_array_add(pctx->depends);
	if (dep == NULL)
		return -1;
	*dep = p;
231
232
233
234

	return 0;
}

235
236
237
238
void apk_deps_parse(struct apk_database *db,
		    struct apk_dependency_array **depends,
		    apk_blob_t blob)
{
239
	struct parse_depend_ctx ctx = { db, depends };
240

241
242
	if (blob.len > 1 && blob.ptr[blob.len-1] == '\n')
		blob.len--;
243

244
	apk_blob_for_each_segment(blob, " ", parse_depend, &ctx);
245
246
}

247
248
int apk_deps_write(struct apk_dependency_array *deps, struct apk_ostream *os)
{
249
	int i, r, n = 0;
250
251
252
253
254
255
256
257
258
259
260

	if (deps == NULL)
		return 0;

	for (i = 0; i < deps->num; i++) {
		if (i) {
			if (os->write(os, " ", 1) != 1)
				return -1;
			n += 1;
		}

261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
		if (deps->item[i].result_mask == APK_DEPMASK_CONFLICT) {
			if (os->write(os, "!", 1) != 1)
				return -1;
			n += 1;
		}

		r = apk_ostream_write_string(os, deps->item[i].name->name);
		if (r < 0)
			return r;
		n += r;

		if (deps->item[i].result_mask != APK_DEPMASK_CONFLICT &&
		    deps->item[i].result_mask != APK_DEPMASK_REQUIRE) {
			r = apk_ostream_write_string(os, apk_version_op_string(deps->item[i].result_mask));
			if (r < 0)
				return r;
			n += r;

			r = apk_ostream_write_string(os, deps->item[i].version);
			if (r < 0)
				return r;
			n += r;
		}
284
285
286
287
288
	}

	return n;
}

Timo Teräs's avatar
Timo Teräs committed
289
const char *apk_script_types[] = {
290
291
292
293
294
295
296
297
298
299
300
301
	[APK_SCRIPT_PRE_INSTALL]	= "pre-install",
	[APK_SCRIPT_POST_INSTALL]	= "post-install",
	[APK_SCRIPT_PRE_DEINSTALL]	= "pre-deinstall",
	[APK_SCRIPT_POST_DEINSTALL]	= "post-deinstall",
	[APK_SCRIPT_PRE_UPGRADE]	= "pre-upgrade",
	[APK_SCRIPT_POST_UPGRADE]	= "post-upgrade",
};

int apk_script_type(const char *name)
{
	int i;

Timo Teräs's avatar
Timo Teräs committed
302
303
304
	for (i = 0; i < ARRAY_SIZE(apk_script_types); i++)
		if (apk_script_types[i] &&
		    strcmp(apk_script_types[i], name) == 0)
305
306
			return i;

307
	return APK_SCRIPT_INVALID;
308
309
}

310
void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action,
311
		       struct apk_checksum *identity, int keys_fd)
Timo Teräs's avatar
Timo Teräs committed
312
313
{
	memset(ctx, 0, sizeof(struct apk_sign_ctx));
314
	ctx->keys_fd = keys_fd;
Timo Teräs's avatar
Timo Teräs committed
315
316
	ctx->action = action;
	switch (action) {
317
318
319
320
321
	case APK_SIGN_NONE:
		ctx->md = EVP_md_null();
		ctx->control_started = 1;
		ctx->data_started = 1;
		break;
Timo Teräs's avatar
Timo Teräs committed
322
323
324
	case APK_SIGN_VERIFY:
		ctx->md = EVP_md_null();
		break;
325
326
327
328
329
330
331
332
333
334
	case APK_SIGN_VERIFY_IDENTITY:
		if (identity->type == APK_CHECKSUM_MD5) {
			ctx->md = EVP_md5();
			ctx->control_started = 1;
			ctx->data_started = 1;
		} else {
			ctx->md = EVP_sha1();
		}
		memcpy(&ctx->identity, identity, sizeof(ctx->identity));
		break;
Timo Teräs's avatar
Timo Teräs committed
335
336
	case APK_SIGN_GENERATE_V1:
		ctx->md = EVP_md5();
Timo Teräs's avatar
Timo Teräs committed
337
338
		ctx->control_started = 1;
		ctx->data_started = 1;
Timo Teräs's avatar
Timo Teräs committed
339
340
		break;
	case APK_SIGN_GENERATE:
341
	case APK_SIGN_VERIFY_AND_GENERATE:
Timo Teräs's avatar
Timo Teräs committed
342
343
344
345
346
	default:
		action = APK_SIGN_GENERATE;
		ctx->md = EVP_sha1();
		break;
	}
347
348
	EVP_MD_CTX_init(&ctx->mdctx);
	EVP_DigestInit_ex(&ctx->mdctx, ctx->md, NULL);
Timo Teräs's avatar
Timo Teräs committed
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
}


void apk_sign_ctx_free(struct apk_sign_ctx *ctx)
{
	if (ctx->signature.data.ptr != NULL)
		free(ctx->signature.data.ptr);
	if (ctx->signature.pkey != NULL)
		EVP_PKEY_free(ctx->signature.pkey);
}

int apk_sign_ctx_process_file(struct apk_sign_ctx *ctx,
			      const struct apk_file_info *fi,
			      struct apk_istream *is)
{
	if (ctx->data_started)
		return 1;

367
	if (fi->name[0] != '.' || strchr(fi->name, '/') != NULL) {
Timo Teräs's avatar
Timo Teräs committed
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
		ctx->data_started = 1;
		ctx->control_started = 1;
		return 1;
	}

	if (ctx->control_started)
		return 1;

	if (strncmp(fi->name, ".SIGN.", 6) != 0) {
		ctx->control_started = 1;
		return 1;
	}

	/* A signature file */
	ctx->num_signatures++;

	/* Found already a trusted key */
385
386
	if (ctx->action != APK_SIGN_VERIFY ||
	    ctx->signature.pkey != NULL)
Timo Teräs's avatar
Timo Teräs committed
387
388
		return 0;

389
390
391
	if (ctx->keys_fd < 0)
		return 0;

Timo Teräs's avatar
Timo Teräs committed
392
393
	if (strncmp(&fi->name[6], "RSA.", 4) == 0 ||
	    strncmp(&fi->name[6], "DSA.", 4) == 0) {
394
395
396
397
398
399
400
401
		int fd = openat(ctx->keys_fd, &fi->name[10], O_RDONLY);
	        BIO *bio;

		if (fd < 0)
			return 0;

		bio  = BIO_new_fp(fdopen(fd, "r"), 0);
		ctx->signature.pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
Timo Teräs's avatar
Timo Teräs committed
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
		if (ctx->signature.pkey != NULL) {
			if (fi->name[6] == 'R')
				ctx->md = EVP_sha1();
			else
				ctx->md = EVP_dss1();
		}
		BIO_free(bio);
	} else
		return 0;

	if (ctx->signature.pkey != NULL)
		ctx->signature.data = apk_blob_from_istream(is, fi->size);

	return 0;
}

418
int apk_sign_ctx_parse_pkginfo_line(void *ctx, apk_blob_t line)
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
{
	struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx;
	apk_blob_t l, r;

	if (line.ptr == NULL || line.len < 1 || line.ptr[0] == '#')
		return 0;

	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
		return 0;

	if (sctx->data_started == 0 &&
	    apk_blob_compare(APK_BLOB_STR("datahash"), l) == 0) {
		sctx->has_data_checksum = 1;
		sctx->md = EVP_sha256();
		apk_blob_pull_hexdump(
			&r, APK_BLOB_PTR_LEN(sctx->data_checksum,
					     EVP_MD_size(sctx->md)));
	}

	return 0;
}

int apk_sign_ctx_verify_tar(void *sctx, const struct apk_file_info *fi,
			    struct apk_istream *is)
{
	struct apk_sign_ctx *ctx = (struct apk_sign_ctx *) sctx;

	if (apk_sign_ctx_process_file(ctx, fi, is) == 0)
		return 0;

	if (strcmp(fi->name, ".PKGINFO") == 0) {
		apk_blob_t blob = apk_blob_from_istream(is, fi->size);
451
452
453
		apk_blob_for_each_segment(
			blob, "\n",
			apk_sign_ctx_parse_pkginfo_line, ctx);
454
455
456
457
458
459
		free(blob.ptr);
	}

	return 0;
}

460
int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data)
Timo Teräs's avatar
Timo Teräs committed
461
462
463
{
	struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx;
	unsigned char calculated[EVP_MAX_MD_SIZE];
Timo Teräs's avatar
Timo Teräs committed
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
	int r, end_of_control;

	if ((part == APK_MPART_DATA) ||
	    (part == APK_MPART_BOUNDARY && sctx->data_started))
		goto update_digest;

	/* Still in signature blocks? */
	if (!sctx->control_started)
		goto reset_digest;

	/* Grab state and mark all remaining block as data */
	end_of_control = (sctx->data_started == 0);
	sctx->data_started = 1;

	/* End of control-block and control does not have data checksum? */
	if (sctx->has_data_checksum == 0 && end_of_control)
		goto update_digest;

	/* Drool in the remaining of the digest block now, we will finish
	 * it on all cases */
	EVP_DigestUpdate(&sctx->mdctx, data.ptr, data.len);

	/* End of control-block and checking control hash/signature or
	 * end of data-block and checking its hash/signature */
	if (sctx->has_data_checksum && !end_of_control) {
		/* End of control-block and check it's hash */
		EVP_DigestFinal_ex(&sctx->mdctx, calculated, NULL);
		if (EVP_MD_CTX_size(&sctx->mdctx) == 0 ||
		    memcmp(calculated, sctx->data_checksum,
		           EVP_MD_CTX_size(&sctx->mdctx)) != 0)
			return -EKEYREJECTED;
		sctx->data_verified = 1;
		if (!(apk_flags & APK_ALLOW_UNTRUSTED) &&
		    !sctx->control_verified)
			return -ENOKEY;
		return 0;
	}
Timo Teräs's avatar
Timo Teräs committed
501

Timo Teräs's avatar
Timo Teräs committed
502
503
	switch (sctx->action) {
	case APK_SIGN_VERIFY:
504
	case APK_SIGN_VERIFY_AND_GENERATE:
Timo Teräs's avatar
Timo Teräs committed
505
506
507
508
		if (sctx->signature.pkey == NULL) {
			if (apk_flags & APK_ALLOW_UNTRUSTED)
				break;
			return -ENOKEY;
Timo Teräs's avatar
Timo Teräs committed
509
510
		}

Timo Teräs's avatar
Timo Teräs committed
511
512
513
514
515
516
517
518
		r = EVP_VerifyFinal(&sctx->mdctx,
			(unsigned char *) sctx->signature.data.ptr,
			sctx->signature.data.len,
			sctx->signature.pkey);
		if (r != 1)
			return -EKEYREJECTED;
		sctx->control_verified = 1;
		if (!sctx->has_data_checksum && part == APK_MPART_END)
519
			sctx->data_verified = 1;
Timo Teräs's avatar
Timo Teräs committed
520
521
522
523
524
525
526
527
528
		break;
	case APK_SIGN_VERIFY_IDENTITY:
		/* Reset digest for hashing data */
		EVP_DigestFinal_ex(&sctx->mdctx, calculated, NULL);
		if (memcmp(calculated, sctx->identity.data,
			   sctx->identity.type) != 0)
			return -EKEYREJECTED;
		sctx->control_verified = 1;
		if (!sctx->has_data_checksum && part == APK_MPART_END)
529
			sctx->data_verified = 1;
Timo Teräs's avatar
Timo Teräs committed
530
531
532
533
534
535
536
537
538
		break;
	case APK_SIGN_GENERATE:
	case APK_SIGN_GENERATE_V1:
		/* Package identity is the checksum */
		sctx->identity.type = EVP_MD_CTX_size(&sctx->mdctx);
		EVP_DigestFinal_ex(&sctx->mdctx, sctx->identity.data, NULL);
		if (sctx->action == APK_SIGN_GENERATE &&
		    sctx->has_data_checksum)
			return -ECANCELED;
539
		break;
Timo Teräs's avatar
Timo Teräs committed
540
	}
541
542
543
544
	if (sctx->action == APK_SIGN_VERIFY_AND_GENERATE) {
		sctx->identity.type = EVP_MD_CTX_size(&sctx->mdctx);
		EVP_DigestFinal_ex(&sctx->mdctx, sctx->identity.data, NULL);
	}
Timo Teräs's avatar
Timo Teräs committed
545
546
547
548
549
550
551
552
553

reset_digest:
	EVP_DigestInit_ex(&sctx->mdctx, sctx->md, NULL);
	EVP_MD_CTX_set_flags(&sctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
	return 0;

update_digest:
	EVP_MD_CTX_clear_flags(&sctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
	EVP_DigestUpdate(&sctx->mdctx, data.ptr, data.len);
Timo Teräs's avatar
Timo Teräs committed
554
555
556
	return 0;
}

557
558
559
struct read_info_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
560
561
	struct apk_sign_ctx *sctx;
	int version;
562
563
};

564
565
int apk_pkg_add_info(struct apk_database *db, struct apk_package *pkg,
		     char field, apk_blob_t value)
Timo Teräs's avatar
Timo Teräs committed
566
567
568
{
	switch (field) {
	case 'P':
569
		pkg->name = apk_db_get_name(db, value);
Timo Teräs's avatar
Timo Teräs committed
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
		break;
	case 'V':
		pkg->version = apk_blob_cstr(value);
		break;
	case 'T':
		pkg->description = apk_blob_cstr(value);
		break;
	case 'U':
		pkg->url = apk_blob_cstr(value);
		break;
	case 'L':
		pkg->license = apk_blob_cstr(value);
		break;
	case 'D':
		apk_deps_parse(db, &pkg->depends, value);
		break;
	case 'C':
Timo Teräs's avatar
Timo Teräs committed
587
		apk_blob_pull_csum(&value, &pkg->csum);
Timo Teräs's avatar
Timo Teräs committed
588
589
		break;
	case 'S':
590
		pkg->size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
591
592
		break;
	case 'I':
593
		pkg->installed_size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
594
		break;
595
596
	default:
		return -1;
Timo Teräs's avatar
Timo Teräs committed
597
	}
598
599
	if (APK_BLOB_IS_NULL(value))
		return -1;
Timo Teräs's avatar
Timo Teräs committed
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
	return 0;
}

static int read_info_line(void *ctx, apk_blob_t line)
{
	static struct {
		const char *str;
		char field;
	} fields[] = {
		{ "pkgname", 'P' },
		{ "pkgver",  'V' },
		{ "pkgdesc", 'T' },
		{ "url",     'U' },
		{ "size",    'I' },
		{ "license", 'L' },
		{ "depend",  'D' },
	};
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
	apk_blob_t l, r;
	int i;

	if (line.ptr == NULL || line.len < 1 || line.ptr[0] == '#')
		return 0;

624
	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
Timo Teräs's avatar
Timo Teräs committed
625
626
627
		return 0;

	for (i = 0; i < ARRAY_SIZE(fields); i++) {
628
		if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0) {
629
			apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
630
			return 0;
Timo Teräs's avatar
Timo Teräs committed
631
632
		}
	}
633
	apk_sign_ctx_parse_pkginfo_line(ri->sctx, line);
634

Timo Teräs's avatar
Timo Teräs committed
635
636
637
	return 0;
}

638
static int read_info_entry(void *ctx, const struct apk_file_info *ae,
639
			   struct apk_istream *is)
640
{
Timo Teräs's avatar
Timo Teräs committed
641
642
643
644
645
	static struct {
		const char *str;
		char field;
	} fields[] = {
		{ "DESC",	'T' },
646
		{ "WWW",	'U' },
Timo Teräs's avatar
Timo Teräs committed
647
648
649
		{ "LICENSE",	'L' },
		{ "DEPEND", 	'D' },
	};
650
651
652
653
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
	struct apk_database *db = ri->db;
	struct apk_package *pkg = ri->pkg;
	apk_blob_t name, version;
654
	char *slash;
Timo Teräs's avatar
Timo Teräs committed
655
	int i;
656

Timo Teräs's avatar
Timo Teräs committed
657
	/* Meta info and scripts */
Timo Teräs's avatar
Timo Teräs committed
658
659
	if (apk_sign_ctx_process_file(ri->sctx, ae, is) == 0)
		return 0;
660

Timo Teräs's avatar
Timo Teräs committed
661
	if (ae->name[0] == '.') {
Timo Teräs's avatar
Timo Teräs committed
662
663
		/* APK 2.0 format */
		if (strcmp(ae->name, ".PKGINFO") == 0) {
664
			apk_blob_t blob = apk_blob_from_istream(is, ae->size);
Timo Teräs's avatar
Timo Teräs committed
665
666
			apk_blob_for_each_segment(blob, "\n", read_info_line, ctx);
			free(blob.ptr);
667
668
			ri->version = 2;
		} else if (strcmp(ae->name, ".INSTALL") == 0) {
669
670
			apk_warning("Package '%s-%s' contains deprecated .INSTALL",
				    pkg->name->name, pkg->version);
Timo Teräs's avatar
Timo Teräs committed
671
		}
672
673
674
675
		return 0;
	}

	if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
Timo Teräs's avatar
Timo Teräs committed
676
		/* APK 1.0 format */
677
		ri->version = 1;
Timo Teräs's avatar
Timo Teräs committed
678
679
		if (!S_ISREG(ae->mode))
			return 0;
680

Timo Teräs's avatar
Timo Teräs committed
681
682
683
		slash = strchr(&ae->name[11], '/');
		if (slash == NULL)
			return 0;
684

Timo Teräs's avatar
Timo Teräs committed
685
686
687
		if (apk_pkg_parse_name(APK_BLOB_PTR_PTR(&ae->name[11], slash-1),
				       &name, &version) < 0)
			return -1;
688

689
690
		if (pkg->name == NULL)
			pkg->name = apk_db_get_name(db, name);
Timo Teräs's avatar
Timo Teräs committed
691
692
693
694
695
		if (pkg->version == NULL)
			pkg->version = apk_blob_cstr(version);

		for (i = 0; i < ARRAY_SIZE(fields); i++) {
			if (strcmp(fields[i].str, slash+1) == 0) {
696
				apk_blob_t blob = apk_blob_from_istream(is, ae->size);
697
698
				apk_pkg_add_info(ri->db, ri->pkg, fields[i].field,
						 trim(blob));
Timo Teräs's avatar
Timo Teräs committed
699
700
701
				free(blob.ptr);
				break;
			}
702
		}
703
	} else if (ri->version < 2) {
704
705
		/* Version 1.x packages do not contain installed size
		 * in metadata, so we calculate it here */
706
		pkg->installed_size += apk_calc_installed_size(ae->size);
Timo Teräs's avatar
Timo Teräs committed
707
	}
708
709
710
711

	return 0;
}

712
713
int apk_pkg_read(struct apk_database *db, const char *file,
	         struct apk_sign_ctx *sctx, struct apk_package **pkg)
714
715
{
	struct read_info_ctx ctx;
716
	struct apk_file_info fi;
717
	struct apk_bstream *bs;
718
	struct apk_istream *tar;
719
	int r;
720

721
	r = apk_file_get_info(AT_FDCWD, file, APK_CHECKSUM_NONE, &fi);
722
723
	if (r != 0)
		return r;
724

725
	memset(&ctx, 0, sizeof(ctx));
Timo Teräs's avatar
Timo Teräs committed
726
	ctx.sctx = sctx;
727
	ctx.pkg = apk_pkg_new();
728
	r = -ENOMEM;
729
	if (ctx.pkg == NULL)
730
		goto err;
731
	bs = apk_bstream_from_file(AT_FDCWD, file);
Timo Teräs's avatar
Timo Teräs committed
732
	if (bs == NULL)
733
734
735
		goto err;

	ctx.db = db;
736
	ctx.pkg->size = fi.size;
737

Timo Teräs's avatar
Timo Teräs committed
738
	tar = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, sctx);
739
	r = apk_tar_parse(tar, read_info_entry, &ctx, FALSE);
740
	tar->close(tar);
Timo Teräs's avatar
Timo Teräs committed
741
	if (r < 0 && r != -ECANCELED)
742
		goto err;
743
	if (ctx.pkg->name == NULL) {
744
		r = -ENOMSG;
745
		goto err;
746
	}
Timo Teräs's avatar
Timo Teräs committed
747
748
	if (sctx->action != APK_SIGN_VERIFY)
		ctx.pkg->csum = sctx->identity;
749
	ctx.pkg->filename = strdup(file);
750

751
752
753
	ctx.pkg = apk_db_pkg_add(db, ctx.pkg);
	if (pkg != NULL)
		*pkg = ctx.pkg;
754
	return 0;
755
756
err:
	apk_pkg_free(ctx.pkg);
757
	return r;
758
759
760
761
762
763
764
765
766
767
768
769
770
}

void apk_pkg_free(struct apk_package *pkg)
{
	struct apk_script *script;
	struct hlist_node *c, *n;

	if (pkg == NULL)
		return;

	hlist_for_each_entry_safe(script, c, n, &pkg->scripts, script_list)
		free(script);

771
772
	if (pkg->depends)
		free(pkg->depends);
773
774
775
776
777
778
779
780
781
782
783
784
785
	if (pkg->version)
		free(pkg->version);
	if (pkg->url)
		free(pkg->url);
	if (pkg->description)
		free(pkg->description);
	if (pkg->license)
		free(pkg->license);
	free(pkg);
}

int apk_pkg_get_state(struct apk_package *pkg)
{
786
	if (list_hashed(&pkg->installed_pkgs_list))
787
788
		return APK_PKG_INSTALLED;
	return APK_PKG_NOT_INSTALLED;
789
790
}

791
792
793
void apk_pkg_set_state(struct apk_database *db, struct apk_package *pkg, int state)
{
	switch (state) {
794
	case APK_PKG_INSTALLED:
795
796
797
798
799
800
		if (!list_hashed(&pkg->installed_pkgs_list)) {
			db->installed.stats.packages++;
			list_add_tail(&pkg->installed_pkgs_list,
				      &db->installed.packages);
		}
		break;
801
	case APK_PKG_NOT_INSTALLED:
802
803
804
805
806
807
808
809
		if (list_hashed(&pkg->installed_pkgs_list)) {
			db->installed.stats.packages--;
			list_del(&pkg->installed_pkgs_list);
		}
		break;
	}
}

810
int apk_pkg_add_script(struct apk_package *pkg, struct apk_istream *is,
811
812
813
814
815
816
817
818
		       unsigned int type, unsigned int size)
{
	struct apk_script *script;
	int r;

	script = malloc(sizeof(struct apk_script) + size);
	script->type = type;
	script->size = size;
819
	r = is->read(is, script->script, size);
820
821
822
823
824
825
826
827
828
	if (r < 0) {
		free(script);
		return r;
	}

	hlist_add_head(&script->script_list, &pkg->scripts);
	return r;
}

829
int apk_pkg_run_script(struct apk_package *pkg, int root_fd,
830
831
		       unsigned int type)
{
832
833
834
835
	static const char * const environment[] = {
		"PATH=/usr/sbin:/usr/bin:/sbin:/bin",
		NULL
	};
836
837
838
839
	struct apk_script *script;
	struct hlist_node *c;
	int fd, status;
	pid_t pid;
840
	char fn[PATH_MAX];
841
842

	hlist_for_each_entry(script, c, &pkg->scripts, script_list) {
843
		if (script->type != type)
844
845
			continue;

846
		snprintf(fn, sizeof(fn), "tmp/%s-%s.%s",
847
			pkg->name->name, pkg->version,
Timo Teräs's avatar
Timo Teräs committed
848
			apk_script_types[type]);
849
850

		fd = openat(root_fd, fn, O_CREAT|O_RDWR|O_TRUNC, 0777);
851
852
853
854
855
856
857
858
859
860
861
		if (fd < 0)
			return fd;
		write(fd, script->script, script->size);
		close(fd);

		apk_message("Executing %s", &fn[4]);

		pid = fork();
		if (pid == -1)
			return -1;
		if (pid == 0) {
862
			fchdir(root_fd);
863
864
			if (chroot(".") < 0) {
				apk_error("chroot: %s", strerror(errno));
Timo Teräs's avatar
Timo Teräs committed
865
			} else {
Timo Teräs's avatar
Timo Teräs committed
866
				execle(fn, apk_script_types[type],
867
				       pkg->version, "", NULL, environment);
Timo Teräs's avatar
Timo Teräs committed
868
			}
869
870
871
			exit(1);
		}
		waitpid(pid, &status, 0);
872
		unlinkat(root_fd, fn, 0);
873
874
875
876
877
878
879
880
		if (WIFEXITED(status))
			return WEXITSTATUS(status);
		return -1;
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
881
static int parse_index_line(void *ctx, apk_blob_t line)
882
{
Timo Teräs's avatar
Timo Teräs committed
883
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
884

Timo Teräs's avatar
Timo Teräs committed
885
886
	if (line.len < 3 || line.ptr[1] != ':')
		return 0;
887

888
	apk_pkg_add_info(ri->db, ri->pkg, line.ptr[0], APK_BLOB_PTR_LEN(line.ptr+2, line.len-2));
889
890
891
892
893
	return 0;
}

struct apk_package *apk_pkg_parse_index_entry(struct apk_database *db, apk_blob_t blob)
{
Timo Teräs's avatar
Timo Teräs committed
894
	struct read_info_ctx ctx;
895

896
	ctx.pkg = apk_pkg_new();
Timo Teräs's avatar
Timo Teräs committed
897
	if (ctx.pkg == NULL)
898
899
		return NULL;

Timo Teräs's avatar
Timo Teräs committed
900
	ctx.db = db;
901
	ctx.version = 0;
902

Timo Teräs's avatar
Timo Teräs committed
903
904
905
906
	apk_blob_for_each_segment(blob, "\n", parse_index_line, &ctx);

	if (ctx.pkg->name == NULL) {
		apk_pkg_free(ctx.pkg);
907
908
		apk_error("Failed to parse index entry: %.*s",
			  blob.len, blob.ptr);
Timo Teräs's avatar
Timo Teräs committed
909
		ctx.pkg = NULL;
910
911
	}

Timo Teräs's avatar
Timo Teräs committed
912
	return ctx.pkg;
913
914
}

915
916
int apk_pkg_write_index_entry(struct apk_package *info,
			      struct apk_ostream *os)
917
{
918
	char buf[512];
919
920
921
922
	apk_blob_t bbuf = APK_BLOB_BUF(buf);
	int r;

	apk_blob_push_blob(&bbuf, APK_BLOB_STR("C:"));
Timo Teräs's avatar
Timo Teräs committed
923
	apk_blob_push_csum(&bbuf, &info->csum);
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nP:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->name->name));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nV:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->version));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nS:"));
	apk_blob_push_uint(&bbuf, info->size, 10);
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nI:"));
	apk_blob_push_uint(&bbuf, info->installed_size, 10);
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nT:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->description));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nU:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->url));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nL:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->license));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));

Timo Teräs's avatar
Timo Teräs committed
940
941
942
	if (APK_BLOB_IS_NULL(bbuf))
		return -1;

943
	if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
944
		return -1;
945
946

	if (info->depends != NULL) {
947
948
949
950
951
952
953
		if (os->write(os, "D:", 2) != 2)
			return -1;
		r = apk_deps_write(info->depends, os);
		if (r < 0)
			return r;
		if (os->write(os, "\n", 1) != 1)
			return -1;
954
955
	}

956
	return 0;
957
}
958

959
960
961
962
int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b)
{
	return apk_version_compare(a->version, b->version);
}