package.c 19.4 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
}

Timo Teräs's avatar
Timo Teräs committed
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
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
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
400
401
402
403
404
405
406
407
408
void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action)
{
	memset(ctx, 0, sizeof(struct apk_sign_ctx));
	switch (ctx->action) {
	case APK_SIGN_VERIFY:
		ctx->md = EVP_md_null();
		break;
	case APK_SIGN_GENERATE_V1:
		ctx->md = EVP_md5();
		break;
	case APK_SIGN_GENERATE:
	default:
		action = APK_SIGN_GENERATE;
		ctx->md = EVP_sha1();
		break;
	}
	ctx->action = action;
}


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;

	if (fi->name[0] != '.') {
		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 */
	if (ctx->signature.pkey != NULL)
		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;
}

int apk_sign_ctx_mpart_cb(void *ctx, EVP_MD_CTX *mdctx, int part)
{
	struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx;
	unsigned char calculated[EVP_MAX_MD_SIZE];
	int r;

	switch (part) {
	case APK_MPART_BEGIN:
		EVP_DigestInit_ex(mdctx, sctx->md, NULL);
		break;
	case APK_MPART_BOUNDARY:
		/* We are not interested about checksums of signature,
		 * reset checksum if we are still in signatures */
		if (!sctx->control_started) {
			EVP_DigestFinal_ex(mdctx, calculated, NULL);
			EVP_DigestInit_ex(mdctx, sctx->md, NULL);
			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;

		/* Verify the signature if we have public key */
		if (sctx->action == APK_SIGN_VERIFY &&
		    sctx->signature.pkey != NULL) {
			r = EVP_VerifyFinal(mdctx,
					   (unsigned char *) sctx->signature.data.ptr,
					   sctx->signature.data.len,
					   sctx->signature.pkey);
			if (r != 1)
				return 1;

			sctx->control_verified = 1;
			EVP_DigestInit_ex(mdctx, sctx->md, NULL);
			return 0;
		} else if (sctx->action == APK_SIGN_GENERATE &&
			   sctx->has_data_checksum) {
			/* Package identity is checksum of control block */
			sctx->identity.type = EVP_MD_CTX_size(mdctx);
			EVP_DigestFinal_ex(mdctx, sctx->identity.data, NULL);
			return 1;
		} else {
			/* Reset digest for hashing data */
			EVP_DigestFinal_ex(mdctx, calculated, NULL);
			EVP_DigestInit_ex(mdctx, sctx->md, NULL);
		}
		break;
	case APK_MPART_END:
		if (sctx->action == APK_SIGN_VERIFY) {
			/* Check that data checksum matches */
			EVP_DigestFinal_ex(mdctx, calculated, NULL);
			if (sctx->has_data_checksum &&
			    EVP_MD_CTX_size(mdctx) != 0 &&
			    memcmp(calculated, sctx->data_checksum,
				   EVP_MD_CTX_size(mdctx)) == 0)
				sctx->data_verified = 1;
		} else {
			/* Package identity is checksum of all data */
			sctx->identity.type = EVP_MD_CTX_size(mdctx);
			EVP_DigestFinal_ex(mdctx, sctx->identity.data, NULL);
		}
		return 1;
	}
	return 0;
}

409
410
411
struct read_info_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
412
413
	struct apk_sign_ctx *sctx;
	int version;
414
	int has_install : 1;
415
416
};

417
418
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
419
420
421
{
	switch (field) {
	case 'P':
422
		pkg->name = apk_db_get_name(db, value);
Timo Teräs's avatar
Timo Teräs committed
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
		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
440
		apk_blob_pull_csum(&value, &pkg->csum);
Timo Teräs's avatar
Timo Teräs committed
441
442
		break;
	case 'S':
443
		pkg->size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
444
445
		break;
	case 'I':
446
		pkg->installed_size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
447
		break;
448
449
	default:
		return -1;
Timo Teräs's avatar
Timo Teräs committed
450
	}
451
452
	if (APK_BLOB_IS_NULL(value))
		return -1;
Timo Teräs's avatar
Timo Teräs committed
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
	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;

477
	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
Timo Teräs's avatar
Timo Teräs committed
478
479
480
		return 0;

	for (i = 0; i < ARRAY_SIZE(fields); i++) {
481
		if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0) {
482
			apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
483
			return 0;
Timo Teräs's avatar
Timo Teräs committed
484
485
486
		}
	}

Timo Teräs's avatar
Timo Teräs committed
487
488
489
490
491
492
493
494
	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)));
	}
495

Timo Teräs's avatar
Timo Teräs committed
496
497
498
	return 0;
}

499
static int read_info_entry(void *ctx, const struct apk_file_info *ae,
500
			   struct apk_istream *is)
501
{
Timo Teräs's avatar
Timo Teräs committed
502
503
504
505
506
	static struct {
		const char *str;
		char field;
	} fields[] = {
		{ "DESC",	'T' },
507
		{ "WWW",	'U' },
Timo Teräs's avatar
Timo Teräs committed
508
509
510
		{ "LICENSE",	'L' },
		{ "DEPEND", 	'D' },
	};
511
512
513
514
	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;
515
	char *slash;
Timo Teräs's avatar
Timo Teräs committed
516
	int i;
517

Timo Teräs's avatar
Timo Teräs committed
518
	/* Meta info and scripts */
Timo Teräs's avatar
Timo Teräs committed
519
520
	if (apk_sign_ctx_process_file(ri->sctx, ae, is) == 0)
		return 0;
521

Timo Teräs's avatar
Timo Teräs committed
522
	if (ri->sctx->data_started == 0 && ae->name[0] == '.') {
Timo Teräs's avatar
Timo Teräs committed
523
524
		/* APK 2.0 format */
		if (strcmp(ae->name, ".PKGINFO") == 0) {
525
			apk_blob_t blob = apk_blob_from_istream(is, ae->size);
Timo Teräs's avatar
Timo Teräs committed
526
527
			apk_blob_for_each_segment(blob, "\n", read_info_line, ctx);
			free(blob.ptr);
528
529
			ri->version = 2;
		} else if (strcmp(ae->name, ".INSTALL") == 0) {
530
531
			apk_warning("Package '%s-%s' contains deprecated .INSTALL",
				    pkg->name->name, pkg->version);
Timo Teräs's avatar
Timo Teräs committed
532
		}
533
534
535
536
		return 0;
	}

	if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
Timo Teräs's avatar
Timo Teräs committed
537
		/* APK 1.0 format */
538
		ri->version = 1;
Timo Teräs's avatar
Timo Teräs committed
539
540
		if (!S_ISREG(ae->mode))
			return 0;
541

Timo Teräs's avatar
Timo Teräs committed
542
543
544
		slash = strchr(&ae->name[11], '/');
		if (slash == NULL)
			return 0;
545

Timo Teräs's avatar
Timo Teräs committed
546
547
548
		if (apk_pkg_parse_name(APK_BLOB_PTR_PTR(&ae->name[11], slash-1),
				       &name, &version) < 0)
			return -1;
549

550
551
		if (pkg->name == NULL)
			pkg->name = apk_db_get_name(db, name);
Timo Teräs's avatar
Timo Teräs committed
552
553
554
555
556
		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) {
557
				apk_blob_t blob = apk_blob_from_istream(is, ae->size);
558
559
				apk_pkg_add_info(ri->db, ri->pkg, fields[i].field,
						 trim(blob));
Timo Teräs's avatar
Timo Teräs committed
560
561
562
				free(blob.ptr);
				break;
			}
563
		}
Timo Teräs's avatar
Timo Teräs committed
564
565
566
		if (apk_script_type(slash+1) == APK_SCRIPT_POST_INSTALL ||
		    apk_script_type(slash+1) == APK_SCRIPT_PRE_INSTALL)
			ri->has_install = 1;
567
	} else if (ri->version < 2) {
568
569
		/* Version 1.x packages do not contain installed size
		 * in metadata, so we calculate it here */
570
		pkg->installed_size += apk_calc_installed_size(ae->size);
Timo Teräs's avatar
Timo Teräs committed
571
	}
572
573
574
575

	return 0;
}

576
struct apk_package *apk_pkg_read(struct apk_database *db, const char *file,
Timo Teräs's avatar
Timo Teräs committed
577
				 struct apk_sign_ctx *sctx)
578
579
{
	struct read_info_ctx ctx;
580
	struct apk_file_info fi;
581
	struct apk_bstream *bs;
582
	struct apk_istream *tar;
583
	char realfile[PATH_MAX];
584
	int r;
585
586
587

	if (realpath(file, realfile) < 0)
		return NULL;
588
589
	if (apk_file_get_info(realfile, APK_CHECKSUM_NONE, &fi) < 0)
		return NULL;
590

591
	memset(&ctx, 0, sizeof(ctx));
Timo Teräs's avatar
Timo Teräs committed
592
	ctx.sctx = sctx;
593
	ctx.pkg = apk_pkg_new();
594
595
596
	if (ctx.pkg == NULL)
		return NULL;

597
	bs = apk_bstream_from_file(realfile);
Timo Teräs's avatar
Timo Teräs committed
598
	if (bs == NULL)
599
600
601
602
		goto err;

	ctx.db = db;
	ctx.has_install = 0;
603
	ctx.pkg->size = fi.size;
604

Timo Teräs's avatar
Timo Teräs committed
605
	tar = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, sctx);
606
607
	r = apk_tar_parse(tar, read_info_entry, &ctx);
	tar->close(tar);
Timo Teräs's avatar
Timo Teräs committed
608
	if (r < 0)
609
		goto err;
Timo Teräs's avatar
Timo Teräs committed
610
	if (ctx.pkg->name == NULL)
611
		goto err;
Timo Teräs's avatar
Timo Teräs committed
612
613
	if (sctx->action == APK_SIGN_VERIFY && !sctx->data_verified &&
	    !(apk_flags & APK_FORCE))
614
615
616
617
618
		goto err;

	/* Add implicit busybox dependency if there is scripts */
	if (ctx.has_install) {
		struct apk_dependency dep = {
619
			.name = apk_db_get_name(db, APK_BLOB_STR("busybox")),
620
621
622
		};
		apk_deps_add(&ctx.pkg->depends, &dep);
	}
623
	ctx.pkg->filename = strdup(realfile);
624

625
	return apk_db_pkg_add(db, ctx.pkg);
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
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);

642
643
	if (pkg->depends)
		free(pkg->depends);
644
645
646
647
648
649
650
651
652
653
654
655
656
	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)
{
657
	if (list_hashed(&pkg->installed_pkgs_list))
658
659
		return APK_PKG_INSTALLED;
	return APK_PKG_NOT_INSTALLED;
660
661
}

662
663
664
void apk_pkg_set_state(struct apk_database *db, struct apk_package *pkg, int state)
{
	switch (state) {
665
	case APK_PKG_INSTALLED:
666
667
668
669
670
671
		if (!list_hashed(&pkg->installed_pkgs_list)) {
			db->installed.stats.packages++;
			list_add_tail(&pkg->installed_pkgs_list,
				      &db->installed.packages);
		}
		break;
672
	case APK_PKG_NOT_INSTALLED:
673
674
675
676
677
678
679
680
		if (list_hashed(&pkg->installed_pkgs_list)) {
			db->installed.stats.packages--;
			list_del(&pkg->installed_pkgs_list);
		}
		break;
	}
}

681
int apk_pkg_add_script(struct apk_package *pkg, struct apk_istream *is,
682
683
684
685
686
687
688
689
		       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;
690
	r = is->read(is, script->script, size);
691
692
693
694
695
696
697
698
699
	if (r < 0) {
		free(script);
		return r;
	}

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

700
int apk_pkg_run_script(struct apk_package *pkg, int root_fd,
701
702
		       unsigned int type)
{
703
704
705
706
	static const char * const environment[] = {
		"PATH=/usr/sbin:/usr/bin:/sbin:/bin",
		NULL
	};
707
708
709
710
711
712
	struct apk_script *script;
	struct hlist_node *c;
	int fd, status;
	pid_t pid;
	char fn[1024];

713
	fchdir(root_fd);
714
	hlist_for_each_entry(script, c, &pkg->scripts, script_list) {
715
		if (script->type != type)
716
717
718
719
720
			continue;

		snprintf(fn, sizeof(fn),
			"tmp/%s-%s.%s",
			pkg->name->name, pkg->version,
Timo Teräs's avatar
Timo Teräs committed
721
			apk_script_types[type]);
722
723
724
725
726
727
728
729
730
731
732
733
		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) {
734
735
			if (chroot(".") < 0) {
				apk_error("chroot: %s", strerror(errno));
Timo Teräs's avatar
Timo Teräs committed
736
			} else {
Timo Teräs's avatar
Timo Teräs committed
737
				execle(fn, apk_script_types[type],
738
				       pkg->version, "", NULL, environment);
Timo Teräs's avatar
Timo Teräs committed
739
			}
740
741
742
			exit(1);
		}
		waitpid(pid, &status, 0);
Timo Teräs's avatar
Timo Teräs committed
743
		unlink(fn);
744
745
746
747
748
749
750
751
		if (WIFEXITED(status))
			return WEXITSTATUS(status);
		return -1;
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
752
static int parse_index_line(void *ctx, apk_blob_t line)
753
{
Timo Teräs's avatar
Timo Teräs committed
754
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
755

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

759
	apk_pkg_add_info(ri->db, ri->pkg, line.ptr[0], APK_BLOB_PTR_LEN(line.ptr+2, line.len-2));
760
761
762
763
764
	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
765
	struct read_info_ctx ctx;
766

767
	ctx.pkg = apk_pkg_new();
Timo Teräs's avatar
Timo Teräs committed
768
	if (ctx.pkg == NULL)
769
770
		return NULL;

Timo Teräs's avatar
Timo Teräs committed
771
	ctx.db = db;
772
	ctx.version = 0;
Timo Teräs's avatar
Timo Teräs committed
773
	ctx.has_install = 0;
774

Timo Teräs's avatar
Timo Teräs committed
775
776
777
778
	apk_blob_for_each_segment(blob, "\n", parse_index_line, &ctx);

	if (ctx.pkg->name == NULL) {
		apk_pkg_free(ctx.pkg);
779
780
		apk_error("Failed to parse index entry: %.*s",
			  blob.len, blob.ptr);
Timo Teräs's avatar
Timo Teräs committed
781
		ctx.pkg = NULL;
782
783
	}

Timo Teräs's avatar
Timo Teräs committed
784
	return ctx.pkg;
785
786
}

787
788
int apk_pkg_write_index_entry(struct apk_package *info,
			      struct apk_ostream *os)
789
{
790
	char buf[512];
791
792
793
794
	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
795
	apk_blob_push_csum(&bbuf, &info->csum);
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
	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"));

	if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
813
		return -1;
814
815

	if (info->depends != NULL) {
816
817
818
819
820
821
822
		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;
823
824
	}

825
	return 0;
826
}
827

828
829
830
831
832
int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b)
{
	return apk_version_compare(a->version, b->version);
}

833
834
835
836
837
838
839
840
841
842
843
844
845
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++;
846
	}
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
	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,
	};
}