package.c 22 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
283
	case APK_SIGN_VERIFY_IDENTITY:
		if (identity->type == APK_CHECKSUM_MD5) {
			ctx->md = EVP_md5();
			ctx->control_started = 1;
			ctx->data_started = 1;
			ctx->has_data_checksum = 1;
		} else {
			ctx->md = EVP_sha1();
		}
		memcpy(&ctx->identity, identity, sizeof(ctx->identity));
		break;
Timo Teräs's avatar
Timo Teräs committed
284
285
	case APK_SIGN_GENERATE_V1:
		ctx->md = EVP_md5();
Timo Teräs's avatar
Timo Teräs committed
286
287
		ctx->control_started = 1;
		ctx->data_started = 1;
Timo Teräs's avatar
Timo Teräs committed
288
289
290
291
292
293
294
		break;
	case APK_SIGN_GENERATE:
	default:
		action = APK_SIGN_GENERATE;
		ctx->md = EVP_sha1();
		break;
	}
295
296
297
	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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
}


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;

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

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

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

	switch (part) {
408
409
410
	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
411
412
		break;
	case APK_MPART_BOUNDARY:
413
414
		EVP_DigestUpdate(&sctx->mdctx, data.ptr, data.len);

Timo Teräs's avatar
Timo Teräs committed
415
416
417
		/* We are not interested about checksums of signature,
		 * reset checksum if we are still in signatures */
		if (!sctx->control_started) {
418
419
420
			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
421
422
423
424
425
426
427
428
429
			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;
430
431
		if (!sctx->has_data_checksum)
			return 0;
Timo Teräs's avatar
Timo Teräs committed
432
433
434
435

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

			sctx->control_verified = 1;
444
445
			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
446
			return 0;
447
		} else if (sctx->action == APK_SIGN_GENERATE) {
Timo Teräs's avatar
Timo Teräs committed
448
			/* Package identity is checksum of control block */
449
450
			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
451
452
453
			return 1;
		} else {
			/* Reset digest for hashing data */
454
455
456
			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);
457
458
459
460
461
462
463

			if (sctx->action == APK_SIGN_VERIFY_IDENTITY) {
				if (memcmp(calculated, sctx->identity.data,
					   sctx->identity.type) == 0)
					sctx->control_verified = 1;
				return 1;
			}
Timo Teräs's avatar
Timo Teräs committed
464
465
466
		}
		break;
	case APK_MPART_END:
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
		if (sctx->has_data_checksum) {
			/* Check that data checksum matches */
			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)
				sctx->data_verified = 1;
		} else if (sctx->action == APK_SIGN_VERIFY) {
			if (sctx->signature.pkey == NULL)
				return 1;

			/* 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);
			if (r == 1) {
				sctx->control_verified = 1;
				sctx->data_verified = 1;
486
			}
487
		} else {
Timo Teräs's avatar
Timo Teräs committed
488
			/* Package identity is checksum of all data */
489
490
			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
491
492
493
494
495
496
		}
		return 1;
	}
	return 0;
}

497
498
499
struct read_info_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
500
501
	struct apk_sign_ctx *sctx;
	int version;
502
	int has_install : 1;
503
504
};

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

565
	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
Timo Teräs's avatar
Timo Teräs committed
566
567
568
		return 0;

	for (i = 0; i < ARRAY_SIZE(fields); i++) {
569
		if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0) {
570
			apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
571
			return 0;
Timo Teräs's avatar
Timo Teräs committed
572
573
574
		}
	}

Timo Teräs's avatar
Timo Teräs committed
575
576
577
578
579
580
581
582
	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)));
	}
583

Timo Teräs's avatar
Timo Teräs committed
584
585
586
	return 0;
}

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

Timo Teräs's avatar
Timo Teräs committed
606
	/* Meta info and scripts */
Timo Teräs's avatar
Timo Teräs committed
607
608
	if (apk_sign_ctx_process_file(ri->sctx, ae, is) == 0)
		return 0;
609

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

	if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
Timo Teräs's avatar
Timo Teräs committed
625
		/* APK 1.0 format */
626
		ri->version = 1;
Timo Teräs's avatar
Timo Teräs committed
627
628
		if (!S_ISREG(ae->mode))
			return 0;
629

Timo Teräs's avatar
Timo Teräs committed
630
631
632
		slash = strchr(&ae->name[11], '/');
		if (slash == NULL)
			return 0;
633

Timo Teräs's avatar
Timo Teräs committed
634
635
636
		if (apk_pkg_parse_name(APK_BLOB_PTR_PTR(&ae->name[11], slash-1),
				       &name, &version) < 0)
			return -1;
637

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

	return 0;
}

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

	if (realpath(file, realfile) < 0)
		return NULL;
676
677
	if (apk_file_get_info(realfile, APK_CHECKSUM_NONE, &fi) < 0)
		return NULL;
678

679
	memset(&ctx, 0, sizeof(ctx));
Timo Teräs's avatar
Timo Teräs committed
680
	ctx.sctx = sctx;
681
	ctx.pkg = apk_pkg_new();
682
683
684
	if (ctx.pkg == NULL)
		return NULL;

685
	bs = apk_bstream_from_file(realfile);
Timo Teräs's avatar
Timo Teräs committed
686
	if (bs == NULL)
687
688
689
690
		goto err;

	ctx.db = db;
	ctx.has_install = 0;
691
	ctx.pkg->size = fi.size;
692

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

	/* Add implicit busybox dependency if there is scripts */
	if (ctx.has_install) {
		struct apk_dependency dep = {
709
			.name = apk_db_get_name(db, APK_BLOB_STR("busybox")),
710
711
712
		};
		apk_deps_add(&ctx.pkg->depends, &dep);
	}
713
	ctx.pkg->filename = strdup(realfile);
714

715
	return apk_db_pkg_add(db, ctx.pkg);
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
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);

732
733
	if (pkg->depends)
		free(pkg->depends);
734
735
736
737
738
739
740
741
742
743
744
745
746
	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)
{
747
	if (list_hashed(&pkg->installed_pkgs_list))
748
749
		return APK_PKG_INSTALLED;
	return APK_PKG_NOT_INSTALLED;
750
751
}

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

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

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

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

803
	fchdir(root_fd);
804
	hlist_for_each_entry(script, c, &pkg->scripts, script_list) {
805
		if (script->type != type)
806
807
808
809
810
			continue;

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

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
842
static int parse_index_line(void *ctx, apk_blob_t line)
843
{
Timo Teräs's avatar
Timo Teräs committed
844
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
845

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

849
	apk_pkg_add_info(ri->db, ri->pkg, line.ptr[0], APK_BLOB_PTR_LEN(line.ptr+2, line.len-2));
850
851
852
853
854
	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
855
	struct read_info_ctx ctx;
856

857
	ctx.pkg = apk_pkg_new();
Timo Teräs's avatar
Timo Teräs committed
858
	if (ctx.pkg == NULL)
859
860
		return NULL;

Timo Teräs's avatar
Timo Teräs committed
861
	ctx.db = db;
862
	ctx.version = 0;
Timo Teräs's avatar
Timo Teräs committed
863
	ctx.has_install = 0;
864

Timo Teräs's avatar
Timo Teräs committed
865
866
867
868
	apk_blob_for_each_segment(blob, "\n", parse_index_line, &ctx);

	if (ctx.pkg->name == NULL) {
		apk_pkg_free(ctx.pkg);
869
870
		apk_error("Failed to parse index entry: %.*s",
			  blob.len, blob.ptr);
Timo Teräs's avatar
Timo Teräs committed
871
		ctx.pkg = NULL;
872
873
	}

Timo Teräs's avatar
Timo Teräs committed
874
	return ctx.pkg;
875
876
}

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

905
	if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
906
		return -1;
907
908

	if (info->depends != NULL) {
909
910
911
912
913
914
915
		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;
916
917
	}

918
	return 0;
919
}
920

921
922
923
924
925
int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b)
{
	return apk_version_compare(a->version, b->version);
}

926
927
928
929
930
931
932
933
934
935
936
937
938
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++;
939
	}
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
	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,
	};
}