database.c 21.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* database.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.
 *
 * This program is free software; you can redistribute it and/or modify it 
 * 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.
 */

#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
15
#include <limits.h>
16 17 18 19 20 21 22 23
#include <unistd.h>
#include <malloc.h>
#include <string.h>

#include "apk_defines.h"
#include "apk_package.h"
#include "apk_database.h"
#include "apk_state.h"
24
#include "apk_applet.h"
25 26 27 28 29 30 31 32 33 34 35

struct install_ctx {
	struct apk_database *db;
	struct apk_package *pkg;

	int script;
	struct apk_db_dir *dircache;
	struct hlist_node **file_dir_node;
	struct hlist_node **file_pkg_node;
};

36
static apk_blob_t pkg_name_get_key(apk_hash_item item)
37
{
38
	return APK_BLOB_STR(((struct apk_name *) item)->name);
39 40 41 42 43
}

static const struct apk_hash_ops pkg_name_hash_ops = {
	.node_offset = offsetof(struct apk_name, hash_node),
	.get_key = pkg_name_get_key,
44 45
	.hash_key = apk_blob_hash,
	.compare = apk_blob_compare,
46 47 48
	.delete_item = (apk_hash_delete_f) apk_name_free,
};

49
static apk_blob_t pkg_info_get_key(apk_hash_item item)
50
{
51
	return APK_BLOB_BUF(((struct apk_package *) item)->csum);
52 53
}

54
static unsigned long csum_hash(apk_blob_t csum)
55
{
56 57
	/* Checksum's highest bits have the most "randomness", use that
	 * directly as hash */
58
	return *(unsigned long *) csum.ptr;
59 60 61 62 63
}

static const struct apk_hash_ops pkg_info_hash_ops = {
	.node_offset = offsetof(struct apk_package, hash_node),
	.get_key = pkg_info_get_key,
64 65
	.hash_key = csum_hash,
	.compare = apk_blob_compare,
66 67 68
	.delete_item = (apk_hash_delete_f) apk_pkg_free,
};

69
static apk_blob_t apk_db_dir_get_key(apk_hash_item item)
70
{
71
	return APK_BLOB_STR(((struct apk_db_dir *) item)->dirname);
72 73 74 75 76
}

static const struct apk_hash_ops dir_hash_ops = {
	.node_offset = offsetof(struct apk_db_dir, hash_node),
	.get_key = apk_db_dir_get_key,
77 78
	.hash_key = apk_blob_hash,
	.compare = apk_blob_compare,
79 80 81
	.delete_item = (apk_hash_delete_f) free,
};

82
struct apk_name *apk_db_get_name(struct apk_database *db, apk_blob_t name)
83 84 85 86 87 88 89 90 91 92 93
{
	struct apk_name *pn;

	pn = (struct apk_name *) apk_hash_get(&db->available.names, name);
	if (pn != NULL)
		return pn;

	pn = calloc(1, sizeof(struct apk_name));
	if (pn == NULL)
		return NULL;

94
	pn->name = apk_blob_cstr(name);
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
	apk_hash_insert(&db->available.names, pn);

	return pn;
}

void apk_name_free(struct apk_name *name)
{
	free(name->name);
	free(name);
}

static struct apk_db_dir *apk_db_dir_ref(struct apk_database *db,
					 struct apk_db_dir *dir,
					 int create_dir)
{
	if (dir->refs == 0) {
		if (dir->parent != NULL)
			apk_db_dir_ref(db, dir->parent, create_dir);
		db->installed.stats.dirs++;
114
		if (create_dir && dir->mode) {
115
			mkdir(dir->dirname, dir->mode);
116 117
			chown(dir->dirname, dir->uid, dir->gid);
		}
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
	}
	dir->refs++;

	return dir;
}

static void apk_db_dir_unref(struct apk_database *db, struct apk_db_dir *dir)
{
	dir->refs--;
	if (dir->refs > 0)
		return;

	db->installed.stats.dirs--;
	rmdir(dir->dirname);

	if (dir->parent != NULL)
		apk_db_dir_unref(db, dir->parent);
}

static struct apk_db_dir *apk_db_dir_get(struct apk_database *db,
					 apk_blob_t name)
{
	struct apk_db_dir *dir;
	apk_blob_t bparent;
142
	int i;
143

144
	if (name.len && name.ptr[name.len-1] == '/')
145 146
		name.len--;

147
	dir = (struct apk_db_dir *) apk_hash_get(&db->installed.dirs, name);
148 149 150 151 152 153 154 155
	if (dir != NULL)
		return dir;

	dir = calloc(1, sizeof(*dir) + name.len + 1);
	memcpy(dir->dirname, name.ptr, name.len);
	dir->dirname[name.len] = 0;
	apk_hash_insert(&db->installed.dirs, dir);

156 157
	if (name.len == 0)
		dir->parent = NULL;
158
	else if (apk_blob_rsplit(name, '/', &bparent, NULL))
159
		dir->parent = apk_db_dir_get(db, bparent);
160 161
	else
		dir->parent = apk_db_dir_get(db, APK_BLOB_NULL);
162

163 164 165 166 167 168 169 170 171 172 173
	if (dir->parent != NULL)
		dir->flags = dir->parent->flags;

	for (i = 0; i < db->protected_paths->num; i++) {
		if (db->protected_paths->item[i][0] == '-' &&
		    strcmp(&db->protected_paths->item[i][1], dir->dirname) == 0)
			dir->flags &= ~APK_DBDIRF_PROTECTED;
		else if (strcmp(db->protected_paths->item[i], dir->dirname) == 0)
			dir->flags |= APK_DBDIRF_PROTECTED;
	}

174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
	return dir;
}

static struct apk_db_file *apk_db_file_new(struct apk_db_dir *dir,
					   apk_blob_t name,
					   struct hlist_node **after)
{
	struct apk_db_file *file;

	file = calloc(1, sizeof(*file) + name.len + 1);
	hlist_add_after(&file->dir_files_list, after);
	file->dir = dir;
	memcpy(file->filename, name.ptr, name.len);
	file->filename[name.len] = 0;

	return file;
}

static void apk_db_file_set_owner(struct apk_database *db,
				  struct apk_db_file *file,
				  struct apk_package *owner,
				  int create_dir,
				  struct hlist_node **after)
{
198 199 200 201 202
	if (file->owner != NULL) {
		hlist_del(&file->pkg_files_list, &file->owner->owned_files);
	} else {
		db->installed.stats.files++;
	}
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
	file->dir = apk_db_dir_ref(db, file->dir, create_dir);
	file->owner = owner;
	hlist_add_after(&file->pkg_files_list, after);
}

static struct apk_db_file *apk_db_file_get(struct apk_database *db,
					   apk_blob_t name,
					   struct install_ctx *ctx)
{
	struct apk_db_dir *dir;
	struct apk_db_file *file;
	struct hlist_node *cur;
	apk_blob_t bdir, bfile;

	dir = NULL;
218 219 220 221
	if (!apk_blob_rsplit(name, '/', &bdir, &bfile)) {
		dir = apk_db_dir_get(db, APK_BLOB_NULL);
		bfile = name;
	} else if (ctx != NULL && ctx->dircache != NULL) {
222 223 224 225 226
		dir = ctx->dircache;
		if (strncmp(dir->dirname, bdir.ptr, bdir.len) != 0 ||
		    dir->dirname[bdir.len] != 0)
			dir = NULL;
	}
227
	if (dir == NULL)
228
		dir = apk_db_dir_get(db, bdir);
229 230
	if (ctx != NULL && dir != ctx->dircache) {
		ctx->dircache = dir;
231
		ctx->file_dir_node = hlist_tail_ptr(&dir->files);
232 233 234 235 236 237 238 239 240 241 242 243 244 245
	}

	hlist_for_each_entry(file, cur, &dir->files, dir_files_list) {
		if (strncmp(file->filename, bfile.ptr, bfile.len) == 0 &&
		    file->filename[bfile.len] == 0)
			return file;
	}

	file = apk_db_file_new(dir, bfile, ctx->file_dir_node);
	ctx->file_dir_node = &file->dir_files_list.next;

	return file;
}

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
static struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package *pkg)
{
	struct apk_package *idb;

	idb = apk_hash_get(&db->available.packages, APK_BLOB_BUF(pkg->csum));
	if (idb == NULL) {
		idb = pkg;
		pkg->id = db->pkg_id++;
		apk_hash_insert(&db->available.packages, pkg);
		*apk_package_array_add(&pkg->name->pkgs) = pkg;
	} else {
		idb->repos |= pkg->repos;
		apk_pkg_free(pkg);
	}
	return idb;
}

static int apk_db_index_read(struct apk_database *db, struct apk_istream *is, int repo)
264 265 266 267 268 269 270 271 272
{
	struct apk_package *pkg = NULL;
	struct apk_db_dir *dir = NULL;
	struct apk_db_file *file = NULL;
	struct hlist_node **file_dir_node = NULL;
	struct hlist_node **file_pkg_node = NULL;

	char buf[1024];
	apk_blob_t l, r;
273
	int n, field;
274 275 276

	r = APK_BLOB_PTR_LEN(buf, 0);
	while (1) {
Timo Teräs's avatar
Timo Teräs committed
277
		n = is->read(is, &r.ptr[r.len], sizeof(buf) - r.len);
278 279 280 281 282
		if (n <= 0)
			break;
		r.len += n;

		while (apk_blob_splitstr(r, "\n", &l, &r)) {
283 284 285 286 287 288 289 290 291 292 293
			if (l.len < 2 || l.ptr[1] != ':') {
				if (pkg == NULL)
					continue;

				if (repo != -1)
					pkg->repos |= BIT(repo);
				else
					apk_pkg_set_state(db, pkg, APK_STATE_INSTALL);

				if (apk_db_pkg_add(db, pkg) != pkg && repo == -1) {
					apk_error("Installed database load failed");
294 295
					return -1;
				}
296 297 298 299 300 301 302 303 304 305 306 307
				pkg = NULL;
				continue;
			}

			/* Get field */
			field = l.ptr[0];
			l.ptr += 2;
			l.len -= 2;

			/* If no package, create new */
			if (pkg == NULL) {
				pkg = apk_pkg_new();
308 309
				dir = NULL;
				file_dir_node = NULL;
310
				file_pkg_node = hlist_tail_ptr(&pkg->owned_files);
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
			}

			/* Standard index line? */
			if (apk_pkg_add_info(db, pkg, field, l) == 0)
				continue;

			if (repo != -1) {
				apk_error("Invalid index entry '%c'", field);
				return -1;
			}

			/* Check FDB special entries */
			switch (field) {
			case 'F':
				if (pkg->name == NULL) {
326 327 328 329
					apk_error("FDB directory entry before package entry");
					return -1;
				}
				dir = apk_db_dir_get(db, l);
330
				file_dir_node = hlist_tail_ptr(&dir->files);
331
				break;
332 333 334 335 336 337 338 339
			case 'M':
				if (dir == NULL) {
					apk_error("FDB directory metadata entry before directory entry");
					return -1;
				}
				sscanf(l.ptr, "%d:%d:%o", &dir->uid, &dir->gid, &dir->mode);
				break;
			case 'R':
340 341 342 343 344 345 346 347 348
				if (dir == NULL) {
					apk_error("FDB file entry before directory entry");
					return -1;
				}
				file = apk_db_file_new(dir, l, file_dir_node);
				apk_db_file_set_owner(db, file, pkg, FALSE, file_pkg_node);
				file_dir_node = &file->dir_files_list.next;
				file_pkg_node = &file->pkg_files_list.next;
				break;
349
			case 'Z':
350 351 352 353 354 355 356 357 358
				if (file == NULL) {
					apk_error("FDB checksum entry before file entry");
					return -1;
				}
				if (apk_hexdump_parse(APK_BLOB_BUF(file->csum), l)) {
					apk_error("Not a valid checksum");
					return -1;
				}
				break;
359 360 361 362 363 364 365 366 367 368 369 370 371
			default:
				apk_error("FDB entry '%c' unsupported", n);
				return -1;
			}
		}

		memcpy(&buf[0], r.ptr, r.len);
		r = APK_BLOB_PTR_LEN(buf, r.len);
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
372
static int apk_db_write_fdb(struct apk_database *db, struct apk_ostream *os)
373 374 375 376
{
	struct apk_package *pkg;
	struct apk_db_dir *dir;
	struct apk_db_file *file;
377
	struct hlist_node *c2;
378
	char buf[1024];
379
	apk_blob_t blob;
380 381
	int n;

382
	list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) {
383 384
		blob = apk_pkg_format_index_entry(pkg, sizeof(buf), buf);
		if (blob.ptr)
Timo Teräs's avatar
Timo Teräs committed
385
			os->write(os, blob.ptr, blob.len - 1);
386 387 388 389 390 391

		dir = NULL;
		hlist_for_each_entry(file, c2, &pkg->owned_files, pkg_files_list) {
			if (file->owner == NULL)
				continue;

Timo Teräs's avatar
Timo Teräs committed
392
			n = 0;
393 394
			if (dir != file->dir) {
				dir = file->dir;
395 396 397 398 399
				n += snprintf(&buf[n], sizeof(buf)-n,
					      "F:%s\n"
					      "M:%d:%d:%o\n",
					      dir->dirname,
					      dir->uid, dir->gid, dir->mode);
400 401 402
			}

			n += snprintf(&buf[n], sizeof(buf)-n,
403
				      "R:%s\n",
404
				      file->filename);
405
			if (csum_valid(file->csum)) {
406
				n += snprintf(&buf[n], sizeof(buf)-n, "Z:");
407 408 409 410
				n += apk_hexdump_format(sizeof(buf)-n, &buf[n],
							APK_BLOB_BUF(file->csum));
				n += snprintf(&buf[n], sizeof(buf)-n, "\n");
			}
411

Timo Teräs's avatar
Timo Teräs committed
412
			if (os->write(os, buf, n) != n)
Timo Teräs's avatar
Timo Teräs committed
413
				return -1;
414
		}
Timo Teräs's avatar
Timo Teräs committed
415
		os->write(os, "\n", 1);
416 417 418 419 420 421 422 423 424 425 426
	}

	return 0;
}

struct apk_script_header {
	csum_t csum;
	unsigned int type;
	unsigned int size;
};

Timo Teräs's avatar
Timo Teräs committed
427
static int apk_db_scriptdb_write(struct apk_database *db, struct apk_ostream *os)
428 429 430 431
{
	struct apk_package *pkg;
	struct apk_script *script;
	struct apk_script_header hdr;
432
	struct hlist_node *c2;
433

434
	list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) {
435 436 437 438 439
		hlist_for_each_entry(script, c2, &pkg->scripts, script_list) {
			memcpy(hdr.csum, pkg->csum, sizeof(csum_t));
			hdr.type = script->type;
			hdr.size = script->size;

Timo Teräs's avatar
Timo Teräs committed
440 441 442 443 444
			if (os->write(os, &hdr, sizeof(hdr)) != sizeof(hdr))
				return -1;

			if (os->write(os, script->script, script->size) != script->size)
				return -1;
445 446 447 448 449 450
		}
	}

	return 0;
}

451
static int apk_db_scriptdb_read(struct apk_database *db, struct apk_istream *is)
452 453 454 455
{
	struct apk_package *pkg;
	struct apk_script_header hdr;

456
	while (is->read(is, &hdr, sizeof(hdr)) == sizeof(hdr)) {
457
		pkg = apk_db_get_pkg(db, hdr.csum);
458 459
		if (pkg != NULL)
			apk_pkg_add_script(pkg, is, hdr.type, hdr.size);
460 461 462 463 464
	}

	return 0;
}

465
int apk_db_create(const char *root)
466
{
467
	apk_blob_t deps = APK_BLOB_STR("busybox, alpine-baselayout, "
468
				       "apk-tools, alpine-conf\n");
469
	int fd;
470

471 472
	fchdir(apk_cwd_fd);
	chdir(root);
473

474 475 476 477 478 479 480 481
	mkdir("tmp", 01777);
	mkdir("dev", 0755);
	mknod("dev/null", 0666, makedev(1, 3));
	mkdir("var", 0755);
	mkdir("var/lib", 0755);
	mkdir("var/lib/apk", 0755);

	fd = creat("var/lib/apk/world", 0600);
482 483 484 485 486 487
	if (fd < 0)
		return -1;
	write(fd, deps.ptr, deps.len);
	close(fd);

	return 0;
488 489
}

490
static int apk_db_read_state(struct apk_database *db)
491
{
492
	struct apk_istream *is;
Timo Teräs's avatar
Timo Teräs committed
493
	apk_blob_t blob;
494 495

	if (db->root == NULL)
496
		return 0;
497 498 499 500 501 502 503 504 505

	/* Read:
	 * 1. installed repository
	 * 2. source repositories
	 * 3. master dependencies
	 * 4. package statuses
	 * 5. files db
	 * 6. script db
	 */
506 507
	fchdir(db->root_fd);

Timo Teräs's avatar
Timo Teräs committed
508 509
	blob = apk_blob_from_file("var/lib/apk/world");
	if (APK_BLOB_IS_NULL(blob)) {
510
		apk_error("Please run 'apk create' to initialize root");
511
		return -1;
512
	}
Timo Teräs's avatar
Timo Teräs committed
513 514
	apk_deps_parse(db, &db->world, blob);
	free(blob.ptr);
515

516
	is = apk_istream_from_file("var/lib/apk/installed");
Timo Teräs's avatar
Timo Teräs committed
517
	if (is != NULL) {
518
		apk_db_index_read(db, is, -1);
Timo Teräs's avatar
Timo Teräs committed
519
		is->close(is);
520 521
	}

522 523
	is = apk_istream_from_file("var/lib/apk/scripts");
	if (is != NULL) {
524
		apk_db_scriptdb_read(db, is);
525
		is->close(is);
526 527 528 529 530
	}

	return 0;
}

531 532 533 534 535 536 537 538
static int add_protected_path(void *ctx, apk_blob_t blob)
{
	struct apk_database *db = (struct apk_database *) ctx;

	*apk_string_array_add(&db->protected_paths) = apk_blob_cstr(blob);
	return 0;
}

539 540
int apk_db_open(struct apk_database *db, const char *root)
{
541
	apk_blob_t blob;
542

543 544 545 546
	memset(db, 0, sizeof(*db));
	apk_hash_init(&db->available.names, &pkg_name_hash_ops, 1000);
	apk_hash_init(&db->available.packages, &pkg_info_hash_ops, 4000);
	apk_hash_init(&db->installed.dirs, &dir_hash_ops, 1000);
547
	list_init(&db->installed.packages);
548 549 550 551 552

	if (root != NULL) {
		db->root = strdup(root);
		db->root_fd = open(root, O_RDONLY);
		if (db->root_fd < 0) {
553
			apk_error("%s: %s", root, strerror(errno));
554 555 556 557
			free(db->root);
			return -1;
		}
	}
558 559 560 561 562 563 564 565 566 567 568 569 570 571

	blob = APK_BLOB_STR("etc:-etc/init.d");
	apk_blob_for_each_segment(blob, ":", add_protected_path, db);

	if (apk_db_read_state(db) != 0)
		return -1;

	fchdir(db->root_fd);
	blob = apk_blob_from_file("etc/apk/repositories");
	if (!APK_BLOB_IS_NULL(blob)) {
		apk_blob_for_each_segment(blob, "\n", apk_db_add_repository, db);
		free(blob.ptr);
	}

Timo Teräs's avatar
Timo Teräs committed
572
	if (apk_repository != NULL)
573
		apk_db_add_repository(db, APK_BLOB_STR(apk_repository));
574

575
	return 0;
576 577
}

578 579 580 581 582
struct write_ctx {
	struct apk_database *db;
	int fd;
};

583
static int apk_db_write_config(struct apk_database *db)
584
{
Timo Teräs's avatar
Timo Teräs committed
585
	struct apk_ostream *os;
586
	char buf[1024];
Timo Teräs's avatar
Timo Teräs committed
587
	int n;
588 589

	if (db->root == NULL)
590
		return 0;
591

592 593
	fchdir(db->root_fd);

Timo Teräs's avatar
Timo Teräs committed
594 595
	os = apk_ostream_to_file("var/lib/apk/world", 0600);
	if (os == NULL)
596 597
		return -1;
	n = apk_deps_format(buf, sizeof(buf), db->world);
Timo Teräs's avatar
Timo Teräs committed
598 599
	os->write(os, buf, n);
	os->close(os);
600

Timo Teräs's avatar
Timo Teräs committed
601 602
	os = apk_ostream_to_file("var/lib/apk/installed", 0600);
	if (os == NULL)
603
		return -1;
Timo Teräs's avatar
Timo Teräs committed
604 605
	apk_db_write_fdb(db, os);
	os->close(os);
606

Timo Teräs's avatar
Timo Teräs committed
607 608
	os = apk_ostream_to_file("var/lib/apk/scripts", 0600);
	if (os == NULL)
609
		return -1;
Timo Teräs's avatar
Timo Teräs committed
610 611
	apk_db_scriptdb_write(db, os);
	os->close(os);
612 613 614 615

	return 0;
}

616
void apk_db_close(struct apk_database *db)
617 618 619 620
{
	apk_hash_free(&db->available.names);
	apk_hash_free(&db->available.packages);
	apk_hash_free(&db->installed.dirs);
621 622
	if (db->root != NULL) {
		close(db->root_fd);
623
		free(db->root);
624
	}
625 626 627 628
}

struct apk_package *apk_db_get_pkg(struct apk_database *db, csum_t sum)
{
629 630
	return apk_hash_get(&db->available.packages,
			    APK_BLOB_PTR_LEN((void*) sum, sizeof(csum_t)));
631 632
}

633
struct apk_package *apk_db_pkg_add_file(struct apk_database *db, const char *file)
634 635 636 637
{
	struct apk_package *info;

	info = apk_pkg_read(db, file);
638 639 640
	if (info != NULL)
		apk_db_pkg_add(db, info);
	return info;
641 642 643 644
}

static int write_index_entry(apk_hash_item item, void *ctx)
{
Timo Teräs's avatar
Timo Teräs committed
645
	struct apk_ostream *os = (struct apk_ostream *) ctx;
646 647 648 649
	char buf[1024];
	apk_blob_t blob;

	blob = apk_pkg_format_index_entry(item, sizeof(buf), buf);
Timo Teräs's avatar
Timo Teräs committed
650 651 652 653 654
	if (APK_BLOB_IS_NULL(blob))
		return 0;

	if (os->write(os, blob.ptr, blob.len) != blob.len)
		return -1;
655 656 657 658

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
659
void apk_db_index_write(struct apk_database *db, struct apk_ostream *os)
660
{
Timo Teräs's avatar
Timo Teräs committed
661
	apk_hash_foreach(&db->available.packages, write_index_entry, (void *) os);
662 663
}

664
int apk_db_add_repository(apk_database_t _db, apk_blob_t repository)
665
{
666 667
	struct apk_database *db = _db.db;
	struct apk_istream *is;
668
	char tmp[256];
669
	int r;
670 671 672 673 674 675

	if (db->num_repos >= APK_MAX_REPOS)
		return -1;

	r = db->num_repos++;
	db->repos[r] = (struct apk_repository){
676
		.url = apk_blob_cstr(repository)
677 678
	};

Timo Teräs's avatar
Timo Teräs committed
679 680
	snprintf(tmp, sizeof(tmp), "%s/APK_INDEX.gz", db->repos[r].url);
	is = apk_istream_from_file_gz(tmp);
681
	if (is == NULL) {
682 683 684
		apk_error("Failed to open index file %s", tmp);
		return -1;
	}
685 686
	apk_db_index_read(db, is, r);
	is->close(is);
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717

	return 0;
}

int apk_db_recalculate_and_commit(struct apk_database *db)
{
	struct apk_state *state;
	int r;

	state = apk_state_new(db);
	r = apk_state_satisfy_deps(state, db->world);
	if (r == 0) {
		r = apk_state_commit(state, db);
		if (r != 0) {
			apk_error("Failed to commit changes");
			return r;
		}
		apk_db_write_config(db);

		apk_message("OK: %d packages, %d dirs, %d files",
			    db->installed.stats.packages,
			    db->installed.stats.dirs,
			    db->installed.stats.files);
	} else {
		apk_error("Failed to build installation graph");
	}
	apk_state_unref(state);

	return r;
}

718
static int apk_db_install_archive_entry(void *_ctx,
719
					const struct apk_file_info *ae,
720
					struct apk_istream *is)
721
{
722
	struct install_ctx *ctx = (struct install_ctx *) _ctx;
723 724 725
	struct apk_database *db = ctx->db;
	struct apk_package *pkg = ctx->pkg;
	apk_blob_t name = APK_BLOB_STR(ae->name);
726
	struct apk_db_dir *dir;
727
	struct apk_db_file *file;
728 729
	struct apk_file_info fi;
	char alt_name[PATH_MAX];
730
	const char *p;
Timo Teräs's avatar
Timo Teräs committed
731
	int r = 0, type = APK_SCRIPT_INVALID;
732

Timo Teräs's avatar
Timo Teräs committed
733 734 735 736 737 738 739 740
	/* Package metainfo and script processing */
	if (ae->name[0] == '.') {
		/* APK 2.0 format */
		if (strcmp(ae->name, ".INSTALL") != 0)
			return 0;
		type = APK_SCRIPT_GENERIC;
	} else if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
		/* APK 1.0 format */
741 742 743 744 745 746 747 748 749
		p = &ae->name[11];
		if (strncmp(p, pkg->name->name, strlen(pkg->name->name)) != 0)
			return 0;
		p += strlen(pkg->name->name) + 1;
		if (strncmp(p, pkg->version, strlen(pkg->version)) != 0)
			return 0;
		p += strlen(pkg->version) + 1;

		type = apk_script_type(p);
750
		if (type == APK_SCRIPT_INVALID)
751
			return 0;
Timo Teräs's avatar
Timo Teräs committed
752
	}
753

Timo Teräs's avatar
Timo Teräs committed
754 755
	/* Handle script */
	if (type != APK_SCRIPT_INVALID) {
756
		apk_pkg_add_script(pkg, is, type, ae->size);
757

Timo Teräs's avatar
Timo Teräs committed
758 759
		if (type == APK_SCRIPT_GENERIC ||
		    type == ctx->script) {
760
			r = apk_pkg_run_script(pkg, db->root_fd, ctx->script);
761 762 763 764 765 766 767 768
			if (r != 0)
				apk_error("%s-%s: Failed to execute pre-install/upgrade script",
					  pkg->name->name, pkg->version);
		}

		return r;
	}

Timo Teräs's avatar
Timo Teräs committed
769
	/* Installable entry */
770
	if (ctx->file_pkg_node == NULL)
771
		ctx->file_pkg_node = hlist_tail_ptr(&pkg->owned_files);
772 773 774 775 776 777 778

	if (!S_ISDIR(ae->mode)) {
		file = apk_db_file_get(db, name, ctx);
		if (file == NULL)
			return -1;

		if (file->owner != NULL &&
779 780
		    file->owner->name != pkg->name &&
		    strcmp(file->owner->name->name, "busybox") != 0) {
781 782 783 784 785 786 787 788 789 790 791 792
			apk_error("%s: Trying to overwrite %s owned by %s.\n",
				  pkg->name->name, ae->name,
				  file->owner->name->name);
			return -1;
		}

		apk_db_file_set_owner(db, file, pkg, TRUE, ctx->file_pkg_node);
		ctx->file_pkg_node = &file->pkg_files_list.next;

		if (strncmp(file->filename, ".keep_", 6) == 0)
			return 0;

793 794 795 796 797 798 799 800 801 802 803 804 805 806
		if ((file->dir->flags & APK_DBDIRF_PROTECTED) &&
		    csum_valid(file->csum) &&
		    apk_file_get_info(ae->name, &fi) == 0 &&
		    memcmp(file->csum, fi.csum, sizeof(csum_t)) != 0) {
			/* Protected file, which is modified locally.
			 * Extract to separate place */
			snprintf(alt_name, sizeof(alt_name),
				 "%s/%s.apk-new",
				 dir->dirname, file->filename);
			r = apk_archive_entry_extract(ae, is, alt_name);
		} else {
			r = apk_archive_entry_extract(ae, is, NULL);
		}
		memcpy(file->csum, ae->csum, sizeof(csum_t));
807 808 809
	} else {
		if (name.ptr[name.len-1] == '/')
			name.len--;
810 811 812 813
		dir = apk_db_dir_get(db, name);
		dir->mode = ae->mode & 07777;
		dir->uid = ae->uid;
		dir->gid = ae->gid;
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
	}

	return r;
}

static void apk_db_purge_pkg(struct apk_database *db,
			     struct apk_package *pkg)
{
	struct apk_db_file *file;
	struct hlist_node *c, *n;
	char fn[1024];

	hlist_for_each_entry_safe(file, c, n, &pkg->owned_files, pkg_files_list) {
		file->owner = NULL;
		snprintf(fn, sizeof(fn), "%s/%s",
			 file->dir->dirname,
			 file->filename);
		unlink(fn);

		apk_db_dir_unref(db, file->dir);
834
		__hlist_del(c, &pkg->owned_files.first);
835 836 837

		db->installed.stats.files--;
	}
838
	apk_pkg_set_state(db, pkg, APK_STATE_NO_INSTALL);
839 840 841 842 843 844
}

int apk_db_install_pkg(struct apk_database *db,
		       struct apk_package *oldpkg,
		       struct apk_package *newpkg)
{
845
	struct apk_bstream *bs;
846 847 848
	struct install_ctx ctx;
	csum_t csum;
	char file[256];
Timo Teräs's avatar
Timo Teräs committed
849
	int r;
850

851
	if (fchdir(db->root_fd) < 0)
852 853 854 855 856
		return errno;

	/* Purge the old package if there */
	if (oldpkg != NULL) {
		if (newpkg == NULL) {
857
			r = apk_pkg_run_script(oldpkg, db->root_fd,
858 859 860 861 862 863
					       APK_SCRIPT_PRE_DEINSTALL);
			if (r != 0)
				return r;
		}
		apk_db_purge_pkg(db, oldpkg);
		if (newpkg == NULL) {
864
			apk_pkg_run_script(oldpkg, db->root_fd,
865 866 867 868 869 870
					   APK_SCRIPT_POST_DEINSTALL);
			return 0;
		}
	}

	/* Install the new stuff */
871 872 873 874
	if (newpkg->filename == NULL) {
		snprintf(file, sizeof(file),
			 "%s/%s-%s.apk",
			 db->repos[0].url, newpkg->name->name, newpkg->version);
Timo Teräs's avatar
Timo Teräs committed
875
		bs = apk_bstream_from_url(file);
876
	} else
Timo Teräs's avatar
Timo Teräs committed
877
		bs = apk_bstream_from_file(newpkg->filename);
878

Timo Teräs's avatar
Timo Teräs committed
879
	if (bs == NULL) {
880
		apk_error("%s: %s", file, strerror(errno));
881
		return errno;
882
	}
883 884 885 886 887 888 889

	ctx = (struct install_ctx) {
		.db = db,
		.pkg = newpkg,
		.script = (oldpkg == NULL) ?
			APK_SCRIPT_PRE_INSTALL : APK_SCRIPT_PRE_UPGRADE,
	};
890
	if (apk_parse_tar_gz(bs, apk_db_install_archive_entry, &ctx) != 0)
891 892
		goto err_close;

Timo Teräs's avatar
Timo Teräs committed
893
	bs->close(bs, csum, NULL);
894

895
	apk_pkg_set_state(db, newpkg, APK_STATE_INSTALL);
896 897 898 899 900

	if (memcmp(csum, newpkg->csum, sizeof(csum)) != 0)
		apk_warning("%s-%s: checksum does not match",
			    newpkg->name->name, newpkg->version);

901
	r = apk_pkg_run_script(newpkg, db->root_fd,
902 903
			       (oldpkg == NULL) ?
			       APK_SCRIPT_POST_INSTALL : APK_SCRIPT_POST_UPGRADE);
904
	if (r != 0) {
905 906
		apk_error("%s-%s: Failed to execute post-install/upgrade script",
			  newpkg->name->name, newpkg->version);
907 908 909
	} else if (apk_quiet) {
		write(STDOUT_FILENO, ".", 1);
	}
910 911
	return r;
err_close:
Timo Teräs's avatar
Timo Teräs committed
912
	bs->close(bs, NULL, NULL);
913 914
	return -1;
}