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
struct apk_package *apk_pkg_new(void)
32
33
34
35
36
37
38
39
40
41
{
	struct apk_package *pkg;

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

	return pkg;
}

42
43
44
45
46
47
int apk_pkg_parse_name(apk_blob_t apkname,
		       apk_blob_t *name,
		       apk_blob_t *version)
{
	int i, dash = 0;

48
49
50
	if (APK_BLOB_IS_NULL(apkname))
		return -1;

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
	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;
	}
	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
68
static apk_blob_t trim(apk_blob_t str)
69
70
{
	if (str.ptr == NULL || str.len < 1)
Timo Teräs's avatar
Timo Teräs committed
71
		return str;
72

73
74
	if (str.ptr[str.len-1] == '\n') {
		str.ptr[str.len-1] = 0;
Timo Teräs's avatar
Timo Teräs committed
75
		return APK_BLOB_PTR_LEN(str.ptr, str.len-1);
76
	}
77

Timo Teräs's avatar
Timo Teräs committed
78
	return str;
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
}

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++) {
			if (deps->item[i].name == dep->name)
				return 0;
		}
	}

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

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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;
	}
}

117
118
119
120
121
122
123
124
125
126
struct parse_depend_ctx {
	struct apk_database *db;
	struct apk_dependency_array **depends;
};

static int parse_depend(void *ctx, apk_blob_t blob)
{
	struct parse_depend_ctx *pctx = (struct parse_depend_ctx *) ctx;
	struct apk_dependency *dep;
	struct apk_name *name;
127
128
	apk_blob_t bname, bop, bver = APK_BLOB_NULL;
	int mask = APK_VERSION_LESS | APK_VERSION_EQUAL | APK_VERSION_GREATER;
129
130
131
132

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

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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)
			return -1;
		if (!apk_blob_spn(bop, "<>=", &bop, &bver))
			return -1;
		for (i = 0; i < blob.len; i++) {
			switch (blob.ptr[i]) {
			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))
			return -1;

		if (!apk_version_validate(bver))
			return -1;
	}

167
168
169
170
171
172
173
174
175
176
	name = apk_db_get_name(pctx->db, blob);
	if (name == NULL)
		return -1;

	dep = apk_dependency_array_add(pctx->depends);
	if (dep == NULL)
		return -1;

	*dep = (struct apk_dependency){
		.name = name,
177
178
		.version = APK_BLOB_IS_NULL(bver) ? NULL : apk_blob_cstr(bver),
		.result_mask = mask,
179
180
181
182
183
	};

	return 0;
}

184
185
186
187
void apk_deps_parse(struct apk_database *db,
		    struct apk_dependency_array **depends,
		    apk_blob_t blob)
{
188
	struct parse_depend_ctx ctx = { db, depends };
189

190
191
	if (blob.len > 1 && blob.ptr[blob.len-1] == '\n')
		blob.len--;
192

193
	apk_blob_for_each_segment(blob, " ", parse_depend, &ctx);
194
195
}

196
197
int apk_deps_write(struct apk_dependency_array *deps, struct apk_ostream *os)
{
198
	int i, r, n = 0;
199
200
201
202
203
204
205
206
207
208
209

	if (deps == NULL)
		return 0;

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

210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
		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;
		}
233
234
235
236
237
	}

	return n;
}

Timo Teräs's avatar
Timo Teräs committed
238
const char *apk_script_types[] = {
239
240
241
242
243
244
245
246
247
248
249
250
	[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
251
252
253
	for (i = 0; i < ARRAY_SIZE(apk_script_types); i++)
		if (apk_script_types[i] &&
		    strcmp(apk_script_types[i], name) == 0)
254
255
			return i;

256
	return APK_SCRIPT_INVALID;
257
258
}

259
260
void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action,
		       struct apk_checksum *identity)
Timo Teräs's avatar
Timo Teräs committed
261
262
{
	memset(ctx, 0, sizeof(struct apk_sign_ctx));
Timo Teräs's avatar
Timo Teräs committed
263
264
	ctx->action = action;
	switch (action) {
265
266
267
268
269
	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
270
271
272
	case APK_SIGN_VERIFY:
		ctx->md = EVP_md_null();
		break;
273
274
275
276
277
278
279
280
281
282
	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
283
284
	case APK_SIGN_GENERATE_V1:
		ctx->md = EVP_md5();
Timo Teräs's avatar
Timo Teräs committed
285
286
		ctx->control_started = 1;
		ctx->data_started = 1;
Timo Teräs's avatar
Timo Teräs committed
287
288
289
290
291
292
293
		break;
	case APK_SIGN_GENERATE:
	default:
		action = APK_SIGN_GENERATE;
		ctx->md = EVP_sha1();
		break;
	}
294
295
	EVP_MD_CTX_init(&ctx->mdctx);
	EVP_DigestInit_ex(&ctx->mdctx, ctx->md, NULL);
Timo Teräs's avatar
Timo Teräs committed
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
}


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;

314
	if (fi->name[0] != '.' || strchr(fi->name, '/') != NULL) {
Timo Teräs's avatar
Timo Teräs committed
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
		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 */
332
333
	if (ctx->action != APK_SIGN_VERIFY ||
	    ctx->signature.pkey != NULL)
Timo Teräs's avatar
Timo Teräs committed
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
		return 0;

	if (strncmp(&fi->name[6], "RSA.", 4) == 0 ||
	    strncmp(&fi->name[6], "DSA.", 4) == 0) {
		char file[256];
	        BIO *bio = BIO_new(BIO_s_file());
		snprintf(file, sizeof(file), "/etc/apk/keys/%s", &fi->name[10]);
		if (BIO_read_filename(bio, file) > 0)
			ctx->signature.pkey =
				PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
		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;
}

360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
static int read_datahash(void *ctx, apk_blob_t line)
{
	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);
		apk_blob_for_each_segment(blob, "\n", read_datahash, ctx);
		free(blob.ptr);
	}

	return 0;
}

400
int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data)
Timo Teräs's avatar
Timo Teräs committed
401
402
403
404
405
406
{
	struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx;
	unsigned char calculated[EVP_MAX_MD_SIZE];
	int r;

	switch (part) {
407
408
	case APK_MPART_DATA:
		EVP_DigestUpdate(&sctx->mdctx, data.ptr, data.len);
Timo Teräs's avatar
Timo Teräs committed
409
410
		break;
	case APK_MPART_BOUNDARY:
411
412
		EVP_DigestUpdate(&sctx->mdctx, data.ptr, data.len);

Timo Teräs's avatar
Timo Teräs committed
413
414
415
		/* We are not interested about checksums of signature,
		 * reset checksum if we are still in signatures */
		if (!sctx->control_started) {
416
417
			EVP_DigestFinal_ex(&sctx->mdctx, calculated, NULL);
			EVP_DigestInit_ex(&sctx->mdctx, sctx->md, NULL);
Timo Teräs's avatar
Timo Teräs committed
418
419
420
421
422
423
424
425
426
			return 0;
		}

		/* Are we in control part?. */
		if ((!sctx->control_started) || sctx->data_started)
			return 0;

		/* End of control block, make sure rest is handled as data */
		sctx->data_started = 1;
427
428
		if (!sctx->has_data_checksum)
			return 0;
Timo Teräs's avatar
Timo Teräs committed
429
430
431
432

		/* Verify the signature if we have public key */
		if (sctx->action == APK_SIGN_VERIFY &&
		    sctx->signature.pkey != NULL) {
433
			r = EVP_VerifyFinal(&sctx->mdctx,
Timo Teräs's avatar
Timo Teräs committed
434
435
436
437
					   (unsigned char *) sctx->signature.data.ptr,
					   sctx->signature.data.len,
					   sctx->signature.pkey);
			if (r != 1)
438
				return -1;
Timo Teräs's avatar
Timo Teräs committed
439
440

			sctx->control_verified = 1;
441
			EVP_DigestInit_ex(&sctx->mdctx, sctx->md, NULL);
Timo Teräs's avatar
Timo Teräs committed
442
			return 0;
443
		} else if (sctx->action == APK_SIGN_GENERATE) {
Timo Teräs's avatar
Timo Teräs committed
444
			/* Package identity is checksum of control block */
445
446
			sctx->identity.type = EVP_MD_CTX_size(&sctx->mdctx);
			EVP_DigestFinal_ex(&sctx->mdctx, sctx->identity.data, NULL);
447
			return -1000;
Timo Teräs's avatar
Timo Teräs committed
448
449
		} else {
			/* Reset digest for hashing data */
450
451
			EVP_DigestFinal_ex(&sctx->mdctx, calculated, NULL);
			EVP_DigestInit_ex(&sctx->mdctx, sctx->md, NULL);
452
453
454

			if (sctx->action == APK_SIGN_VERIFY_IDENTITY) {
				if (memcmp(calculated, sctx->identity.data,
455
456
457
					   sctx->identity.type) != 0)
					return -1;
				sctx->control_verified = 1;
458
			}
Timo Teräs's avatar
Timo Teräs committed
459
460
461
		}
		break;
	case APK_MPART_END:
462
463
464
		if (sctx->has_data_checksum) {
			/* Check that data checksum matches */
			EVP_DigestFinal_ex(&sctx->mdctx, calculated, NULL);
465
			if (EVP_MD_CTX_size(&sctx->mdctx) == 0 ||
466
			    memcmp(calculated, sctx->data_checksum,
467
468
469
			           EVP_MD_CTX_size(&sctx->mdctx)) != 0)
				return -1;
			sctx->data_verified = 1;
470
471
		} else if (sctx->action == APK_SIGN_VERIFY) {
			if (sctx->signature.pkey == NULL)
472
				return -1;
473
474
475
476
477
478

			/* Assume that the data is fully signed */
			r = EVP_VerifyFinal(&sctx->mdctx,
				   (unsigned char *) sctx->signature.data.ptr,
				   sctx->signature.data.len,
				   sctx->signature.pkey);
479
480
481
482
483
484
485
486
487
488
489
490
491
			if (r != 1)
				return -1;

			sctx->control_verified = 1;
			sctx->data_verified = 1;
		} else if (sctx->action == APK_SIGN_VERIFY_IDENTITY) {
			EVP_DigestFinal_ex(&sctx->mdctx, calculated, NULL);
			if (EVP_MD_CTX_size(&sctx->mdctx) == 0 ||
			    memcmp(calculated, sctx->identity.data,
			           EVP_MD_CTX_size(&sctx->mdctx)) != 0)
				return -1;
			sctx->control_verified = 1;
			sctx->data_verified = 1;
492
		} else {
Timo Teräs's avatar
Timo Teräs committed
493
			/* Package identity is checksum of all data */
494
495
			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
496
		}
497
		break;
Timo Teräs's avatar
Timo Teräs committed
498
499
500
501
	}
	return 0;
}

502
503
504
struct read_info_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
505
506
	struct apk_sign_ctx *sctx;
	int version;
507
	int has_install : 1;
508
509
};

510
511
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
512
513
514
{
	switch (field) {
	case 'P':
515
		pkg->name = apk_db_get_name(db, value);
Timo Teräs's avatar
Timo Teräs committed
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
		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
533
		apk_blob_pull_csum(&value, &pkg->csum);
Timo Teräs's avatar
Timo Teräs committed
534
535
		break;
	case 'S':
536
		pkg->size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
537
538
		break;
	case 'I':
539
		pkg->installed_size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
540
		break;
541
542
	default:
		return -1;
Timo Teräs's avatar
Timo Teräs committed
543
	}
544
545
	if (APK_BLOB_IS_NULL(value))
		return -1;
Timo Teräs's avatar
Timo Teräs committed
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
	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;

570
	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
Timo Teräs's avatar
Timo Teräs committed
571
572
573
		return 0;

	for (i = 0; i < ARRAY_SIZE(fields); i++) {
574
		if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0) {
575
			apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
576
			return 0;
Timo Teräs's avatar
Timo Teräs committed
577
578
579
		}
	}

Timo Teräs's avatar
Timo Teräs committed
580
581
582
583
584
585
586
587
	if (ri->sctx->data_started == 0 &&
	    apk_blob_compare(APK_BLOB_STR("datahash"), l) == 0) {
		ri->sctx->has_data_checksum = 1;
		ri->sctx->md = EVP_sha256();
		apk_blob_pull_hexdump(
			&r, APK_BLOB_PTR_LEN(ri->sctx->data_checksum,
					     EVP_MD_size(ri->sctx->md)));
	}
588

Timo Teräs's avatar
Timo Teräs committed
589
590
591
	return 0;
}

592
static int read_info_entry(void *ctx, const struct apk_file_info *ae,
593
			   struct apk_istream *is)
594
{
Timo Teräs's avatar
Timo Teräs committed
595
596
597
598
599
	static struct {
		const char *str;
		char field;
	} fields[] = {
		{ "DESC",	'T' },
600
		{ "WWW",	'U' },
Timo Teräs's avatar
Timo Teräs committed
601
602
603
		{ "LICENSE",	'L' },
		{ "DEPEND", 	'D' },
	};
604
605
606
607
	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;
608
	char *slash;
Timo Teräs's avatar
Timo Teräs committed
609
	int i;
610

Timo Teräs's avatar
Timo Teräs committed
611
	/* Meta info and scripts */
Timo Teräs's avatar
Timo Teräs committed
612
613
	if (apk_sign_ctx_process_file(ri->sctx, ae, is) == 0)
		return 0;
614

Timo Teräs's avatar
Timo Teräs committed
615
	if (ae->name[0] == '.') {
Timo Teräs's avatar
Timo Teräs committed
616
617
		/* APK 2.0 format */
		if (strcmp(ae->name, ".PKGINFO") == 0) {
618
			apk_blob_t blob = apk_blob_from_istream(is, ae->size);
Timo Teräs's avatar
Timo Teräs committed
619
620
			apk_blob_for_each_segment(blob, "\n", read_info_line, ctx);
			free(blob.ptr);
621
622
			ri->version = 2;
		} else if (strcmp(ae->name, ".INSTALL") == 0) {
623
624
			apk_warning("Package '%s-%s' contains deprecated .INSTALL",
				    pkg->name->name, pkg->version);
Timo Teräs's avatar
Timo Teräs committed
625
		}
626
627
628
629
		return 0;
	}

	if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
Timo Teräs's avatar
Timo Teräs committed
630
		/* APK 1.0 format */
631
		ri->version = 1;
Timo Teräs's avatar
Timo Teräs committed
632
633
		if (!S_ISREG(ae->mode))
			return 0;
634

Timo Teräs's avatar
Timo Teräs committed
635
636
637
		slash = strchr(&ae->name[11], '/');
		if (slash == NULL)
			return 0;
638

Timo Teräs's avatar
Timo Teräs committed
639
640
641
		if (apk_pkg_parse_name(APK_BLOB_PTR_PTR(&ae->name[11], slash-1),
				       &name, &version) < 0)
			return -1;
642

643
644
		if (pkg->name == NULL)
			pkg->name = apk_db_get_name(db, name);
Timo Teräs's avatar
Timo Teräs committed
645
646
647
648
649
		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) {
650
				apk_blob_t blob = apk_blob_from_istream(is, ae->size);
651
652
				apk_pkg_add_info(ri->db, ri->pkg, fields[i].field,
						 trim(blob));
Timo Teräs's avatar
Timo Teräs committed
653
654
655
				free(blob.ptr);
				break;
			}
656
		}
Timo Teräs's avatar
Timo Teräs committed
657
658
659
		if (apk_script_type(slash+1) == APK_SCRIPT_POST_INSTALL ||
		    apk_script_type(slash+1) == APK_SCRIPT_PRE_INSTALL)
			ri->has_install = 1;
660
	} else if (ri->version < 2) {
661
662
		/* Version 1.x packages do not contain installed size
		 * in metadata, so we calculate it here */
663
		pkg->installed_size += apk_calc_installed_size(ae->size);
Timo Teräs's avatar
Timo Teräs committed
664
	}
665
666
667
668

	return 0;
}

669
struct apk_package *apk_pkg_read(struct apk_database *db, const char *file,
Timo Teräs's avatar
Timo Teräs committed
670
				 struct apk_sign_ctx *sctx)
671
672
{
	struct read_info_ctx ctx;
673
	struct apk_file_info fi;
674
	struct apk_bstream *bs;
675
	struct apk_istream *tar;
676
	char realfile[PATH_MAX];
677
	int r;
678
679
680

	if (realpath(file, realfile) < 0)
		return NULL;
681
682
	if (apk_file_get_info(realfile, APK_CHECKSUM_NONE, &fi) < 0)
		return NULL;
683

684
	memset(&ctx, 0, sizeof(ctx));
Timo Teräs's avatar
Timo Teräs committed
685
	ctx.sctx = sctx;
686
	ctx.pkg = apk_pkg_new();
687
688
689
	if (ctx.pkg == NULL)
		return NULL;

690
	bs = apk_bstream_from_file(realfile);
Timo Teräs's avatar
Timo Teräs committed
691
	if (bs == NULL)
692
693
694
695
		goto err;

	ctx.db = db;
	ctx.has_install = 0;
696
	ctx.pkg->size = fi.size;
697

Timo Teräs's avatar
Timo Teräs committed
698
	tar = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, sctx);
699
700
	r = apk_tar_parse(tar, read_info_entry, &ctx);
	tar->close(tar);
701
	if (r < 0 && r != -1000)
702
		goto err;
Timo Teräs's avatar
Timo Teräs committed
703
	if (ctx.pkg->name == NULL)
704
		goto err;
Timo Teräs's avatar
Timo Teräs committed
705
706
	if (sctx->action == APK_SIGN_VERIFY && !sctx->data_verified &&
	    !(apk_flags & APK_FORCE))
707
		goto err;
Timo Teräs's avatar
Timo Teräs committed
708
709
	if (sctx->action != APK_SIGN_VERIFY)
		ctx.pkg->csum = sctx->identity;
710
711
712
713

	/* Add implicit busybox dependency if there is scripts */
	if (ctx.has_install) {
		struct apk_dependency dep = {
714
			.name = apk_db_get_name(db, APK_BLOB_STR("busybox")),
715
716
717
		};
		apk_deps_add(&ctx.pkg->depends, &dep);
	}
718
	ctx.pkg->filename = strdup(realfile);
719

720
	return apk_db_pkg_add(db, ctx.pkg);
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
err:
	apk_pkg_free(ctx.pkg);
	return NULL;
}

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);

737
738
	if (pkg->depends)
		free(pkg->depends);
739
740
741
742
743
744
745
746
747
748
749
750
751
	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)
{
752
	if (list_hashed(&pkg->installed_pkgs_list))
753
754
		return APK_PKG_INSTALLED;
	return APK_PKG_NOT_INSTALLED;
755
756
}

757
758
759
void apk_pkg_set_state(struct apk_database *db, struct apk_package *pkg, int state)
{
	switch (state) {
760
	case APK_PKG_INSTALLED:
761
762
763
764
765
766
		if (!list_hashed(&pkg->installed_pkgs_list)) {
			db->installed.stats.packages++;
			list_add_tail(&pkg->installed_pkgs_list,
				      &db->installed.packages);
		}
		break;
767
	case APK_PKG_NOT_INSTALLED:
768
769
770
771
772
773
774
775
		if (list_hashed(&pkg->installed_pkgs_list)) {
			db->installed.stats.packages--;
			list_del(&pkg->installed_pkgs_list);
		}
		break;
	}
}

776
int apk_pkg_add_script(struct apk_package *pkg, struct apk_istream *is,
777
778
779
780
781
782
783
784
		       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;
785
	r = is->read(is, script->script, size);
786
787
788
789
790
791
792
793
794
	if (r < 0) {
		free(script);
		return r;
	}

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

795
int apk_pkg_run_script(struct apk_package *pkg, int root_fd,
796
797
		       unsigned int type)
{
798
799
800
801
	static const char * const environment[] = {
		"PATH=/usr/sbin:/usr/bin:/sbin:/bin",
		NULL
	};
802
803
804
805
806
807
	struct apk_script *script;
	struct hlist_node *c;
	int fd, status;
	pid_t pid;
	char fn[1024];

808
	fchdir(root_fd);
809
	hlist_for_each_entry(script, c, &pkg->scripts, script_list) {
810
		if (script->type != type)
811
812
813
814
815
			continue;

		snprintf(fn, sizeof(fn),
			"tmp/%s-%s.%s",
			pkg->name->name, pkg->version,
Timo Teräs's avatar
Timo Teräs committed
816
			apk_script_types[type]);
817
818
819
820
821
822
823
824
825
826
827
828
		fd = creat(fn, 0777);
		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) {
829
830
			if (chroot(".") < 0) {
				apk_error("chroot: %s", strerror(errno));
Timo Teräs's avatar
Timo Teräs committed
831
			} else {
Timo Teräs's avatar
Timo Teräs committed
832
				execle(fn, apk_script_types[type],
833
				       pkg->version, "", NULL, environment);
Timo Teräs's avatar
Timo Teräs committed
834
			}
835
836
837
			exit(1);
		}
		waitpid(pid, &status, 0);
Timo Teräs's avatar
Timo Teräs committed
838
		unlink(fn);
839
840
841
842
843
844
845
846
		if (WIFEXITED(status))
			return WEXITSTATUS(status);
		return -1;
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
847
static int parse_index_line(void *ctx, apk_blob_t line)
848
{
Timo Teräs's avatar
Timo Teräs committed
849
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
850

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

854
	apk_pkg_add_info(ri->db, ri->pkg, line.ptr[0], APK_BLOB_PTR_LEN(line.ptr+2, line.len-2));
855
856
857
858
859
	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
860
	struct read_info_ctx ctx;
861

862
	ctx.pkg = apk_pkg_new();
Timo Teräs's avatar
Timo Teräs committed
863
	if (ctx.pkg == NULL)
864
865
		return NULL;

Timo Teräs's avatar
Timo Teräs committed
866
	ctx.db = db;
867
	ctx.version = 0;
Timo Teräs's avatar
Timo Teräs committed
868
	ctx.has_install = 0;
869

Timo Teräs's avatar
Timo Teräs committed
870
871
872
873
	apk_blob_for_each_segment(blob, "\n", parse_index_line, &ctx);

	if (ctx.pkg->name == NULL) {
		apk_pkg_free(ctx.pkg);
874
875
		apk_error("Failed to parse index entry: %.*s",
			  blob.len, blob.ptr);
Timo Teräs's avatar
Timo Teräs committed
876
		ctx.pkg = NULL;
877
878
	}

Timo Teräs's avatar
Timo Teräs committed
879
	return ctx.pkg;
880
881
}

882
883
int apk_pkg_write_index_entry(struct apk_package *info,
			      struct apk_ostream *os)
884
{
885
	char buf[512];
886
887
888
889
	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
890
	apk_blob_push_csum(&bbuf, &info->csum);
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
	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
907
908
909
	if (APK_BLOB_IS_NULL(bbuf))
		return -1;

910
	if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
911
		return -1;
912
913

	if (info->depends != NULL) {
914
915
916
917
918
919
920
		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;
921
922
	}

923
	return 0;
924
}
925

926
927
928
929
930
int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b)
{
	return apk_version_compare(a->version, b->version);
}

931
932
933
934
935
936
937
938
939
940
941
942
943
struct apk_dependency apk_dep_from_str(struct apk_database *db,
				       char *str)
{
	apk_blob_t name = APK_BLOB_STR(str);
	char *v = str;
	int mask = APK_DEPMASK_REQUIRE;

	v = strpbrk(str, "<>=");
	if (v != NULL) {
		name.len = v - str;
		mask = apk_version_result_mask(v++);
		if (*v == '=')
			v++;
944
	}
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
	return (struct apk_dependency) {
		.name = apk_db_get_name(db, name),
		.version = v,
		.result_mask = mask,
	};
}

struct apk_dependency apk_dep_from_pkg(struct apk_database *db,
				       struct apk_package *pkg)
{
	return (struct apk_dependency) {
		.name = apk_db_get_name(db, APK_BLOB_STR(pkg->name->name)),
		.version = pkg->version,
		.result_mask = APK_VERSION_EQUAL,
	};
}