package.c 20.5 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
void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action)
{
	memset(ctx, 0, sizeof(struct apk_sign_ctx));
Timo Teräs's avatar
Timo Teräs committed
262
263
	ctx->action = action;
	switch (action) {
Timo Teräs's avatar
Timo Teräs committed
264
265
266
267
268
	case APK_SIGN_VERIFY:
		ctx->md = EVP_md_null();
		break;
	case APK_SIGN_GENERATE_V1:
		ctx->md = EVP_md5();
Timo Teräs's avatar
Timo Teräs committed
269
270
		ctx->control_started = 1;
		ctx->data_started = 1;
Timo Teräs's avatar
Timo Teräs committed
271
272
273
274
275
276
277
		break;
	case APK_SIGN_GENERATE:
	default:
		action = APK_SIGN_GENERATE;
		ctx->md = EVP_sha1();
		break;
	}
278
279
280
	EVP_MD_CTX_init(&ctx->mdctx);
	EVP_DigestInit_ex(&ctx->mdctx, ctx->md, NULL);
	EVP_MD_CTX_set_flags(&ctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
Timo Teräs's avatar
Timo Teräs committed
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
}


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;

299
	if (fi->name[0] != '.' || strchr(fi->name, '/') != NULL) {
Timo Teräs's avatar
Timo Teräs committed
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
		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;
}

344
int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data)
Timo Teräs's avatar
Timo Teräs committed
345
346
347
348
349
350
{
	struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx;
	unsigned char calculated[EVP_MAX_MD_SIZE];
	int r;

	switch (part) {
351
352
353
	case APK_MPART_DATA:
		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
354
355
		break;
	case APK_MPART_BOUNDARY:
356
357
		EVP_DigestUpdate(&sctx->mdctx, data.ptr, data.len);

Timo Teräs's avatar
Timo Teräs committed
358
359
360
		/* We are not interested about checksums of signature,
		 * reset checksum if we are still in signatures */
		if (!sctx->control_started) {
361
362
363
			EVP_DigestFinal_ex(&sctx->mdctx, calculated, NULL);
			EVP_DigestInit_ex(&sctx->mdctx, sctx->md, NULL);
			EVP_MD_CTX_set_flags(&sctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
Timo Teräs's avatar
Timo Teräs committed
364
365
366
367
368
369
370
371
372
373
374
375
376
			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) {
377
			r = EVP_VerifyFinal(&sctx->mdctx,
Timo Teräs's avatar
Timo Teräs committed
378
379
380
381
382
383
384
					   (unsigned char *) sctx->signature.data.ptr,
					   sctx->signature.data.len,
					   sctx->signature.pkey);
			if (r != 1)
				return 1;

			sctx->control_verified = 1;
385
386
			EVP_DigestInit_ex(&sctx->mdctx, sctx->md, NULL);
			EVP_MD_CTX_set_flags(&sctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
Timo Teräs's avatar
Timo Teräs committed
387
388
389
390
			return 0;
		} else if (sctx->action == APK_SIGN_GENERATE &&
			   sctx->has_data_checksum) {
			/* Package identity is checksum of control block */
391
392
			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
393
394
395
			return 1;
		} else {
			/* Reset digest for hashing data */
396
397
398
			EVP_DigestFinal_ex(&sctx->mdctx, calculated, NULL);
			EVP_DigestInit_ex(&sctx->mdctx, sctx->md, NULL);
			EVP_MD_CTX_set_flags(&sctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
Timo Teräs's avatar
Timo Teräs committed
399
400
401
402
		}
		break;
	case APK_MPART_END:
		if (sctx->action == APK_SIGN_VERIFY) {
403
404
			if (sctx->has_data_checksum) {
				/* Check that data checksum matches */
405
406
				EVP_DigestFinal_ex(&sctx->mdctx, calculated, NULL);
				if (EVP_MD_CTX_size(&sctx->mdctx) != 0 &&
407
				    memcmp(calculated, sctx->data_checksum,
408
				           EVP_MD_CTX_size(&sctx->mdctx)) == 0)
409
410
411
					sctx->data_verified = 1;
			} else if (sctx->signature.pkey != NULL) {
				/* Assume that the data is fully signed */
412
				r = EVP_VerifyFinal(&sctx->mdctx,
413
414
415
416
417
418
419
420
					   (unsigned char *) sctx->signature.data.ptr,
					   sctx->signature.data.len,
					   sctx->signature.pkey);
				if (r == 1) {
					sctx->control_verified = 1;
					sctx->data_verified = 1;
				}
			}
Timo Teräs's avatar
Timo Teräs committed
421
		} else if (!sctx->has_data_checksum) {
Timo Teräs's avatar
Timo Teräs committed
422
			/* Package identity is checksum of all data */
423
424
			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
425
426
427
428
429
430
		}
		return 1;
	}
	return 0;
}

431
432
433
struct read_info_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
434
435
	struct apk_sign_ctx *sctx;
	int version;
436
	int has_install : 1;
437
438
};

439
440
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
441
442
443
{
	switch (field) {
	case 'P':
444
		pkg->name = apk_db_get_name(db, value);
Timo Teräs's avatar
Timo Teräs committed
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
		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
462
		apk_blob_pull_csum(&value, &pkg->csum);
Timo Teräs's avatar
Timo Teräs committed
463
464
		break;
	case 'S':
465
		pkg->size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
466
467
		break;
	case 'I':
468
		pkg->installed_size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
469
		break;
470
471
	default:
		return -1;
Timo Teräs's avatar
Timo Teräs committed
472
	}
473
474
	if (APK_BLOB_IS_NULL(value))
		return -1;
Timo Teräs's avatar
Timo Teräs committed
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
	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;

499
	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
Timo Teräs's avatar
Timo Teräs committed
500
501
502
		return 0;

	for (i = 0; i < ARRAY_SIZE(fields); i++) {
503
		if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0) {
504
			apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
505
			return 0;
Timo Teräs's avatar
Timo Teräs committed
506
507
508
		}
	}

Timo Teräs's avatar
Timo Teräs committed
509
510
511
512
513
514
515
516
	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)));
	}
517

Timo Teräs's avatar
Timo Teräs committed
518
519
520
	return 0;
}

521
static int read_info_entry(void *ctx, const struct apk_file_info *ae,
522
			   struct apk_istream *is)
523
{
Timo Teräs's avatar
Timo Teräs committed
524
525
526
527
528
	static struct {
		const char *str;
		char field;
	} fields[] = {
		{ "DESC",	'T' },
529
		{ "WWW",	'U' },
Timo Teräs's avatar
Timo Teräs committed
530
531
532
		{ "LICENSE",	'L' },
		{ "DEPEND", 	'D' },
	};
533
534
535
536
	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;
537
	char *slash;
Timo Teräs's avatar
Timo Teräs committed
538
	int i;
539

Timo Teräs's avatar
Timo Teräs committed
540
	/* Meta info and scripts */
Timo Teräs's avatar
Timo Teräs committed
541
542
	if (apk_sign_ctx_process_file(ri->sctx, ae, is) == 0)
		return 0;
543

Timo Teräs's avatar
Timo Teräs committed
544
	if (ae->name[0] == '.') {
Timo Teräs's avatar
Timo Teräs committed
545
546
		/* APK 2.0 format */
		if (strcmp(ae->name, ".PKGINFO") == 0) {
547
			apk_blob_t blob = apk_blob_from_istream(is, ae->size);
Timo Teräs's avatar
Timo Teräs committed
548
549
			apk_blob_for_each_segment(blob, "\n", read_info_line, ctx);
			free(blob.ptr);
550
551
			ri->version = 2;
		} else if (strcmp(ae->name, ".INSTALL") == 0) {
552
553
			apk_warning("Package '%s-%s' contains deprecated .INSTALL",
				    pkg->name->name, pkg->version);
Timo Teräs's avatar
Timo Teräs committed
554
		}
555
556
557
558
		return 0;
	}

	if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
Timo Teräs's avatar
Timo Teräs committed
559
		/* APK 1.0 format */
560
		ri->version = 1;
Timo Teräs's avatar
Timo Teräs committed
561
562
		if (!S_ISREG(ae->mode))
			return 0;
563

Timo Teräs's avatar
Timo Teräs committed
564
565
566
		slash = strchr(&ae->name[11], '/');
		if (slash == NULL)
			return 0;
567

Timo Teräs's avatar
Timo Teräs committed
568
569
570
		if (apk_pkg_parse_name(APK_BLOB_PTR_PTR(&ae->name[11], slash-1),
				       &name, &version) < 0)
			return -1;
571

572
573
		if (pkg->name == NULL)
			pkg->name = apk_db_get_name(db, name);
Timo Teräs's avatar
Timo Teräs committed
574
575
576
577
578
		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) {
579
				apk_blob_t blob = apk_blob_from_istream(is, ae->size);
580
581
				apk_pkg_add_info(ri->db, ri->pkg, fields[i].field,
						 trim(blob));
Timo Teräs's avatar
Timo Teräs committed
582
583
584
				free(blob.ptr);
				break;
			}
585
		}
Timo Teräs's avatar
Timo Teräs committed
586
587
588
		if (apk_script_type(slash+1) == APK_SCRIPT_POST_INSTALL ||
		    apk_script_type(slash+1) == APK_SCRIPT_PRE_INSTALL)
			ri->has_install = 1;
589
	} else if (ri->version < 2) {
590
591
		/* Version 1.x packages do not contain installed size
		 * in metadata, so we calculate it here */
592
		pkg->installed_size += apk_calc_installed_size(ae->size);
Timo Teräs's avatar
Timo Teräs committed
593
	}
594
595
596
597

	return 0;
}

598
struct apk_package *apk_pkg_read(struct apk_database *db, const char *file,
Timo Teräs's avatar
Timo Teräs committed
599
				 struct apk_sign_ctx *sctx)
600
601
{
	struct read_info_ctx ctx;
602
	struct apk_file_info fi;
603
	struct apk_bstream *bs;
604
	struct apk_istream *tar;
605
	char realfile[PATH_MAX];
606
	int r;
607
608
609

	if (realpath(file, realfile) < 0)
		return NULL;
610
611
	if (apk_file_get_info(realfile, APK_CHECKSUM_NONE, &fi) < 0)
		return NULL;
612

613
	memset(&ctx, 0, sizeof(ctx));
Timo Teräs's avatar
Timo Teräs committed
614
	ctx.sctx = sctx;
615
	ctx.pkg = apk_pkg_new();
616
617
618
	if (ctx.pkg == NULL)
		return NULL;

619
	bs = apk_bstream_from_file(realfile);
Timo Teräs's avatar
Timo Teräs committed
620
	if (bs == NULL)
621
622
623
624
		goto err;

	ctx.db = db;
	ctx.has_install = 0;
625
	ctx.pkg->size = fi.size;
626

Timo Teräs's avatar
Timo Teräs committed
627
	tar = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, sctx);
628
629
	r = apk_tar_parse(tar, read_info_entry, &ctx);
	tar->close(tar);
Timo Teräs's avatar
Timo Teräs committed
630
	if (r < 0)
631
		goto err;
Timo Teräs's avatar
Timo Teräs committed
632
	if (ctx.pkg->name == NULL)
633
		goto err;
Timo Teräs's avatar
Timo Teräs committed
634
635
	if (sctx->action == APK_SIGN_VERIFY && !sctx->data_verified &&
	    !(apk_flags & APK_FORCE))
636
		goto err;
Timo Teräs's avatar
Timo Teräs committed
637
638
	if (sctx->action != APK_SIGN_VERIFY)
		ctx.pkg->csum = sctx->identity;
639
640
641
642

	/* Add implicit busybox dependency if there is scripts */
	if (ctx.has_install) {
		struct apk_dependency dep = {
643
			.name = apk_db_get_name(db, APK_BLOB_STR("busybox")),
644
645
646
		};
		apk_deps_add(&ctx.pkg->depends, &dep);
	}
647
	ctx.pkg->filename = strdup(realfile);
648

649
	return apk_db_pkg_add(db, ctx.pkg);
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
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);

666
667
	if (pkg->depends)
		free(pkg->depends);
668
669
670
671
672
673
674
675
676
677
678
679
680
	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)
{
681
	if (list_hashed(&pkg->installed_pkgs_list))
682
683
		return APK_PKG_INSTALLED;
	return APK_PKG_NOT_INSTALLED;
684
685
}

686
687
688
void apk_pkg_set_state(struct apk_database *db, struct apk_package *pkg, int state)
{
	switch (state) {
689
	case APK_PKG_INSTALLED:
690
691
692
693
694
695
		if (!list_hashed(&pkg->installed_pkgs_list)) {
			db->installed.stats.packages++;
			list_add_tail(&pkg->installed_pkgs_list,
				      &db->installed.packages);
		}
		break;
696
	case APK_PKG_NOT_INSTALLED:
697
698
699
700
701
702
703
704
		if (list_hashed(&pkg->installed_pkgs_list)) {
			db->installed.stats.packages--;
			list_del(&pkg->installed_pkgs_list);
		}
		break;
	}
}

705
int apk_pkg_add_script(struct apk_package *pkg, struct apk_istream *is,
706
707
708
709
710
711
712
713
		       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;
714
	r = is->read(is, script->script, size);
715
716
717
718
719
720
721
722
723
	if (r < 0) {
		free(script);
		return r;
	}

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

724
int apk_pkg_run_script(struct apk_package *pkg, int root_fd,
725
726
		       unsigned int type)
{
727
728
729
730
	static const char * const environment[] = {
		"PATH=/usr/sbin:/usr/bin:/sbin:/bin",
		NULL
	};
731
732
733
734
735
736
	struct apk_script *script;
	struct hlist_node *c;
	int fd, status;
	pid_t pid;
	char fn[1024];

737
	fchdir(root_fd);
738
	hlist_for_each_entry(script, c, &pkg->scripts, script_list) {
739
		if (script->type != type)
740
741
742
743
744
			continue;

		snprintf(fn, sizeof(fn),
			"tmp/%s-%s.%s",
			pkg->name->name, pkg->version,
Timo Teräs's avatar
Timo Teräs committed
745
			apk_script_types[type]);
746
747
748
749
750
751
752
753
754
755
756
757
		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) {
758
759
			if (chroot(".") < 0) {
				apk_error("chroot: %s", strerror(errno));
Timo Teräs's avatar
Timo Teräs committed
760
			} else {
Timo Teräs's avatar
Timo Teräs committed
761
				execle(fn, apk_script_types[type],
762
				       pkg->version, "", NULL, environment);
Timo Teräs's avatar
Timo Teräs committed
763
			}
764
765
766
			exit(1);
		}
		waitpid(pid, &status, 0);
Timo Teräs's avatar
Timo Teräs committed
767
		unlink(fn);
768
769
770
771
772
773
774
775
		if (WIFEXITED(status))
			return WEXITSTATUS(status);
		return -1;
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
776
static int parse_index_line(void *ctx, apk_blob_t line)
777
{
Timo Teräs's avatar
Timo Teräs committed
778
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
779

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

783
	apk_pkg_add_info(ri->db, ri->pkg, line.ptr[0], APK_BLOB_PTR_LEN(line.ptr+2, line.len-2));
784
785
786
787
788
	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
789
	struct read_info_ctx ctx;
790

791
	ctx.pkg = apk_pkg_new();
Timo Teräs's avatar
Timo Teräs committed
792
	if (ctx.pkg == NULL)
793
794
		return NULL;

Timo Teräs's avatar
Timo Teräs committed
795
	ctx.db = db;
796
	ctx.version = 0;
Timo Teräs's avatar
Timo Teräs committed
797
	ctx.has_install = 0;
798

Timo Teräs's avatar
Timo Teräs committed
799
800
801
802
	apk_blob_for_each_segment(blob, "\n", parse_index_line, &ctx);

	if (ctx.pkg->name == NULL) {
		apk_pkg_free(ctx.pkg);
803
804
		apk_error("Failed to parse index entry: %.*s",
			  blob.len, blob.ptr);
Timo Teräs's avatar
Timo Teräs committed
805
		ctx.pkg = NULL;
806
807
	}

Timo Teräs's avatar
Timo Teräs committed
808
	return ctx.pkg;
809
810
}

811
812
int apk_pkg_write_index_entry(struct apk_package *info,
			      struct apk_ostream *os)
813
{
814
	char buf[512];
815
816
817
818
	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
819
	apk_blob_push_csum(&bbuf, &info->csum);
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
	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
836
837
838
	if (APK_BLOB_IS_NULL(bbuf))
		return -1;

839
	if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
840
		return -1;
841
842

	if (info->depends != NULL) {
843
844
845
846
847
848
849
		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;
850
851
	}

852
	return 0;
853
}
854

855
856
857
858
859
int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b)
{
	return apk_version_compare(a->version, b->version);
}

860
861
862
863
864
865
866
867
868
869
870
871
872
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++;
873
	}
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
	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,
	};
}