database.c 21 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 58
	/* Checksum's highest bits have the most "randomness", use that
	 * directly as hash */
	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;
}

Timo Teräs's avatar
Timo Teräs committed
246
static int apk_db_read_fdb(struct apk_database *db, struct apk_istream *is)
247 248 249 250 251 252 253 254 255 256 257 258 259 260
{
	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;
	csum_t csum;
	int n;

	r = APK_BLOB_PTR_LEN(buf, 0);
	while (1) {
Timo Teräs's avatar
Timo Teräs committed
261
		n = is->read(is, &r.ptr[r.len], sizeof(buf) - r.len);
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
		if (n <= 0)
			break;
		r.len += n;

		while (apk_blob_splitstr(r, "\n", &l, &r)) {
			n = l.ptr[0];
			l.ptr++;
			l.len--;
			switch (n) {
			case 'P':
				if (apk_hexdump_parse(APK_BLOB_BUF(csum), l)) {
					apk_error("Not a valid checksum");
					return -1;
				}
				pkg = apk_db_get_pkg(db, csum);
				if (pkg == NULL) {
					apk_error("Package '%.*s' is installed, but not in any repository",
						  l.len, l.ptr);
					return -1;
				}
282
				if (!list_hashed(&pkg->installed_pkgs_list)) {
283
					db->installed.stats.packages++;
284
					list_add_tail(&pkg->installed_pkgs_list, &db->installed.packages);
285 286 287
				}
				dir = NULL;
				file_dir_node = NULL;
288
				file_pkg_node = hlist_tail_ptr(&pkg->owned_files);
289 290 291 292 293 294 295
				break;
			case 'D':
				if (pkg == NULL) {
					apk_error("FDB directory entry before package entry");
					return -1;
				}
				dir = apk_db_dir_get(db, l);
296
				file_dir_node = hlist_tail_ptr(&dir->files);
297 298 299 300 301 302 303 304 305 306 307
				break;
			case 'F':
				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;
308 309 310 311 312 313 314 315 316 317
			case 'C':
				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;
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
			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;
}

static int apk_db_write_fdb(struct apk_database *db, int fd)
{
	struct apk_package *pkg;
	struct apk_db_dir *dir;
	struct apk_db_file *file;
336
	struct hlist_node *c2;
337 338 339
	char buf[1024];
	int n;

340
	list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) {
341 342 343 344 345
		n = 0;
		buf[n++] = 'P';
		n += apk_hexdump_format(sizeof(buf)-n, &buf[n],
					APK_BLOB_BUF(pkg->csum));
		buf[n++] = '\n';
Timo Teräs's avatar
Timo Teräs committed
346 347 348
		if (write(fd, buf, n) != n)
			return -1;
		n = 0;
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364

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

			if (dir != file->dir) {
				n += snprintf(&buf[n], sizeof(buf)-n,
					     "D%s\n",
					      file->dir->dirname);
				dir = file->dir;
			}

			n += snprintf(&buf[n], sizeof(buf)-n,
				      "F%s\n",
				      file->filename);
365 366 367 368 369 370
			if (csum_valid(file->csum)) {
				n += snprintf(&buf[n], sizeof(buf)-n, "C");
				n += apk_hexdump_format(sizeof(buf)-n, &buf[n],
							APK_BLOB_BUF(file->csum));
				n += snprintf(&buf[n], sizeof(buf)-n, "\n");
			}
371

Timo Teräs's avatar
Timo Teräs committed
372 373
			if (write(fd, buf, n) != n)
				return -1;
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
			n = 0;
		}
	}

	return 0;
}

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

static int apk_db_write_scriptdb(struct apk_database *db, int fd)
{
	struct apk_package *pkg;
	struct apk_script *script;
	struct apk_script_header hdr;
392
	struct hlist_node *c2;
393

394
	list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) {
395 396 397 398 399 400 401 402 403 404 405 406 407
		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;

			write(fd, &hdr, sizeof(hdr));
			write(fd, script->script, script->size);
		}
	}

	return 0;
}

408
static int apk_db_read_scriptdb(struct apk_database *db, struct apk_istream *is)
409 410 411 412
{
	struct apk_package *pkg;
	struct apk_script_header hdr;

413
	while (is->read(is, &hdr, sizeof(hdr)) == sizeof(hdr)) {
414
		pkg = apk_db_get_pkg(db, hdr.csum);
415 416
		if (pkg != NULL)
			apk_pkg_add_script(pkg, is, hdr.type, hdr.size);
417 418 419 420 421
	}

	return 0;
}

422
int apk_db_create(const char *root)
423
{
424
	apk_blob_t deps = APK_BLOB_STR("busybox, alpine-baselayout, "
425
				       "apk-tools, alpine-conf\n");
426
	int fd;
427

428 429
	fchdir(apk_cwd_fd);
	chdir(root);
430

431 432 433 434 435 436 437 438
	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);
439 440 441 442 443 444
	if (fd < 0)
		return -1;
	write(fd, deps.ptr, deps.len);
	close(fd);

	return 0;
445 446
}

447
static int apk_db_read_state(struct apk_database *db)
448
{
449
	struct apk_istream *is;
Timo Teräs's avatar
Timo Teräs committed
450
	apk_blob_t blob;
451 452

	if (db->root == NULL)
453
		return 0;
454 455 456 457 458 459 460 461 462

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

Timo Teräs's avatar
Timo Teräs committed
465 466
	blob = apk_blob_from_file("var/lib/apk/world");
	if (APK_BLOB_IS_NULL(blob)) {
467
		apk_error("Please run 'apk create' to initialize root");
468
		return -1;
469
	}
Timo Teräs's avatar
Timo Teräs committed
470 471
	apk_deps_parse(db, &db->world, blob);
	free(blob.ptr);
472

Timo Teräs's avatar
Timo Teräs committed
473 474 475 476
	is = apk_istream_from_file("var/lib/apk/files");
	if (is != NULL) {
		apk_db_read_fdb(db, is);
		is->close(is);
477 478
	}

479 480 481 482
	is = apk_istream_from_file("var/lib/apk/scripts");
	if (is != NULL) {
		apk_db_read_scriptdb(db, is);
		is->close(is);
483 484
	}

485 486 487 488 489 490
	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);
	}

491 492 493
	return 0;
}

494 495 496 497 498 499 500 501
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;
}

502 503
int apk_db_open(struct apk_database *db, const char *root)
{
504 505
	apk_blob_t dirs;

506 507 508 509
	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);
510
	list_init(&db->installed.packages);
511 512 513 514 515

	if (root != NULL) {
		db->root = strdup(root);
		db->root_fd = open(root, O_RDONLY);
		if (db->root_fd < 0) {
516
			apk_error("%s: %s", root, strerror(errno));
517 518 519 520
			free(db->root);
			return -1;
		}
	}
Timo Teräs's avatar
Timo Teräs committed
521
	if (apk_repository != NULL)
522
		apk_db_add_repository(db, APK_BLOB_STR(apk_repository));
523

524 525 526 527
	dirs = APK_BLOB_STR("etc:-etc/init.d");
	apk_blob_for_each_segment(dirs, ":", add_protected_path, db);

	return apk_db_read_state(db);
528 529
}

530 531 532 533 534
struct write_ctx {
	struct apk_database *db;
	int fd;
};

535
static int apk_db_write_config(struct apk_database *db)
536 537 538 539 540
{
	char buf[1024];
	int n, fd;

	if (db->root == NULL)
541
		return 0;
542

543 544 545
	fchdir(db->root_fd);

	fd = creat("var/lib/apk/world", 0600);
546 547 548 549 550 551
	if (fd < 0)
		return -1;
	n = apk_deps_format(buf, sizeof(buf), db->world);
	write(fd, buf, n);
	close(fd);

552
	fd = creat("var/lib/apk/files", 0600);
553 554 555 556 557
	if (fd < 0)
		return -1;
	apk_db_write_fdb(db, fd);
	close(fd);

558
	fd = creat("var/lib/apk/scripts", 0600);
559 560 561 562 563 564 565 566
	if (fd < 0)
		return -1;
	apk_db_write_scriptdb(db, fd);
	close(fd);

	return 0;
}

567
void apk_db_close(struct apk_database *db)
568 569 570 571
{
	apk_hash_free(&db->available.names);
	apk_hash_free(&db->available.packages);
	apk_hash_free(&db->installed.dirs);
572 573
	if (db->root != NULL) {
		close(db->root_fd);
574
		free(db->root);
575
	}
576 577 578 579 580 581
}

static void apk_db_pkg_add(struct apk_database *db, struct apk_package *pkg)
{
	struct apk_package *idb;

582
	idb = apk_hash_get(&db->available.packages, APK_BLOB_BUF(pkg->csum));
583 584 585 586 587 588 589 590 591 592 593 594
	if (idb == NULL) {
		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);
	}
}

struct apk_package *apk_db_get_pkg(struct apk_database *db, csum_t sum)
{
595 596
	return apk_hash_get(&db->available.packages,
			    APK_BLOB_PTR_LEN((void*) sum, sizeof(csum_t)));
597 598 599 600 601 602 603 604 605 606 607 608 609 610
}

int apk_db_pkg_add_file(struct apk_database *db, const char *file)
{
	struct apk_package *info;

	info = apk_pkg_read(db, file);
	if (info == NULL)
		return FALSE;

	apk_db_pkg_add(db, info);
	return TRUE;
}

611
int apk_db_index_read(struct apk_database *db, struct apk_istream *is, int repo)
612 613 614 615 616 617 618 619
{
	struct apk_package *pkg;
	char buf[1024];
	int n;
	apk_blob_t l, r;

	r = APK_BLOB_PTR_LEN(buf, 0);
	while (1) {
620
		n = is->read(is, &r.ptr[r.len], sizeof(buf) - r.len);
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
		if (n <= 0)
			break;
		r.len += n;

		while (apk_blob_splitstr(r, "\n\n", &l, &r)) {
			pkg = apk_pkg_parse_index_entry(db, l);
			if (pkg != NULL) {
				pkg->repos |= BIT(repo);
				apk_db_pkg_add(db, pkg);
			}
		}

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

	return 0;
}

static int write_index_entry(apk_hash_item item, void *ctx)
{
	int fd = (int) ctx;
	char buf[1024];
	apk_blob_t blob;

	blob = apk_pkg_format_index_entry(item, sizeof(buf), buf);
	if (blob.ptr)
		write(fd, blob.ptr, blob.len);

	return 0;
}

void apk_db_index_write(struct apk_database *db, int fd)
{
	apk_hash_foreach(&db->available.packages, write_index_entry, (void *) fd);
}

658
int apk_db_add_repository(apk_database_t _db, apk_blob_t repository)
659
{
660 661
	struct apk_database *db = _db.db;
	struct apk_istream *is;
662
	char tmp[256];
663
	int r;
664 665 666 667 668 669

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

	r = db->num_repos++;
	db->repos[r] = (struct apk_repository){
670
		.url = apk_blob_cstr(repository)
671 672
	};

Timo Teräs's avatar
Timo Teräs committed
673 674
	snprintf(tmp, sizeof(tmp), "%s/APK_INDEX.gz", db->repos[r].url);
	is = apk_istream_from_file_gz(tmp);
675
	if (is == NULL) {
676 677 678
		apk_error("Failed to open index file %s", tmp);
		return -1;
	}
679 680
	apk_db_index_read(db, is, r);
	is->close(is);
681 682 683 684 685 686 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

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

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

Timo Teräs's avatar
Timo Teräs committed
727 728 729 730 731 732 733 734
	/* 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 */
735 736 737 738 739 740 741 742 743
		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);
744
		if (type == APK_SCRIPT_INVALID)
745
			return 0;
Timo Teräs's avatar
Timo Teräs committed
746
	}
747

Timo Teräs's avatar
Timo Teräs committed
748 749
	/* Handle script */
	if (type != APK_SCRIPT_INVALID) {
750
		apk_pkg_add_script(pkg, is, type, ae->size);
751

Timo Teräs's avatar
Timo Teräs committed
752 753
		if (type == APK_SCRIPT_GENERIC ||
		    type == ctx->script) {
754
			r = apk_pkg_run_script(pkg, db->root_fd, ctx->script);
755 756 757 758 759 760 761 762
			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
763
	/* Installable entry */
764
	if (ctx->file_pkg_node == NULL)
765
		ctx->file_pkg_node = hlist_tail_ptr(&pkg->owned_files);
766 767 768 769 770 771 772

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

		if (file->owner != NULL &&
773 774
		    file->owner->name != pkg->name &&
		    strcmp(file->owner->name->name, "busybox") != 0) {
775 776 777 778 779 780 781 782 783 784 785 786
			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;

787 788 789 790 791 792 793 794 795 796 797 798 799 800
		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));
801 802 803
	} else {
		if (name.ptr[name.len-1] == '/')
			name.len--;
804 805 806 807
		dir = apk_db_dir_get(db, name);
		dir->mode = ae->mode & 07777;
		dir->uid = ae->uid;
		dir->gid = ae->gid;
808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
	}

	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);
828
		__hlist_del(c, &pkg->owned_files.first);
829 830 831 832

		db->installed.stats.files--;
	}
	db->installed.stats.packages--;
833
	list_del(&pkg->installed_pkgs_list);
834 835 836 837 838 839
}

int apk_db_install_pkg(struct apk_database *db,
		       struct apk_package *oldpkg,
		       struct apk_package *newpkg)
{
840
	struct apk_bstream *bs;
841 842 843 844 845
	struct install_ctx ctx;
	csum_t csum;
	char file[256];
	int fd, r;

846
	if (fchdir(db->root_fd) < 0)
847 848 849 850 851
		return errno;

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

	/* Install the new stuff */
	snprintf(file, sizeof(file),
867
		 "%s/%s-%s.apk",
868 869 870
		 db->repos[0].url, newpkg->name->name, newpkg->version);

	fd = open(file, O_RDONLY);
871 872
	if (fd < 0) {
		apk_error("%s: %s", file, strerror(errno));
873
		return errno;
874
	}
875 876 877

	fcntl(fd, F_SETFD, FD_CLOEXEC);

878 879
	bs = apk_bstream_from_fd(fd);
	if (bs == NULL)
880 881 882 883 884 885 886 887
		goto err_close;

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

891
	bs->close(bs, csum);
892 893

	db->installed.stats.packages++;
894
	list_add_tail(&newpkg->installed_pkgs_list, &db->installed.packages);
895 896 897 898 899

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

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

err_close:
	close(fd);
	return -1;
}