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
	/* 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 372 373 374 375 376
			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;
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 385
		blob = apk_pkg_format_index_entry(pkg, sizeof(buf), buf);
		if (blob.ptr)
			write(fd, blob.ptr, blob.len - 1);
386

387
		n = 0;
388 389 390 391 392 393 394
		dir = NULL;
		hlist_for_each_entry(file, c2, &pkg->owned_files, pkg_files_list) {
			if (file->owner == NULL)
				continue;

			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 413
			if (write(fd, buf, n) != n)
				return -1;
414 415
			n = 0;
		}
416
		write(fd, "\n", 1);
417 418 419 420 421 422 423 424 425 426 427
	}

	return 0;
}

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

428
static int apk_db_scriptdb_write(struct apk_database *db, int fd)
429 430 431 432
{
	struct apk_package *pkg;
	struct apk_script *script;
	struct apk_script_header hdr;
433
	struct hlist_node *c2;
434

435
	list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) {
436 437 438 439 440 441 442 443 444 445 446 447 448
		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;
}

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

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

	return 0;
}

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

469 470
	fchdir(apk_cwd_fd);
	chdir(root);
471

472 473 474 475 476 477 478 479
	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);
480 481 482 483 484 485
	if (fd < 0)
		return -1;
	write(fd, deps.ptr, deps.len);
	close(fd);

	return 0;
486 487
}

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

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

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

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

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

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

	return 0;
}

529 530 531 532 533 534 535 536
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;
}

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

541 542 543 544
	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);
545
	list_init(&db->installed.packages);
546 547 548 549 550

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

	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
570
	if (apk_repository != NULL)
571
		apk_db_add_repository(db, APK_BLOB_STR(apk_repository));
572

573
	return 0;
574 575
}

576 577 578 579 580
struct write_ctx {
	struct apk_database *db;
	int fd;
};

581
static int apk_db_write_config(struct apk_database *db)
582 583 584 585 586
{
	char buf[1024];
	int n, fd;

	if (db->root == NULL)
587
		return 0;
588

589 590 591
	fchdir(db->root_fd);

	fd = creat("var/lib/apk/world", 0600);
592 593 594 595 596 597
	if (fd < 0)
		return -1;
	n = apk_deps_format(buf, sizeof(buf), db->world);
	write(fd, buf, n);
	close(fd);

598
	fd = creat("var/lib/apk/installed", 0600);
599 600 601 602 603
	if (fd < 0)
		return -1;
	apk_db_write_fdb(db, fd);
	close(fd);

604
	fd = creat("var/lib/apk/scripts", 0600);
605 606
	if (fd < 0)
		return -1;
607
	apk_db_scriptdb_write(db, fd);
608 609 610 611 612
	close(fd);

	return 0;
}

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

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

630
struct apk_package *apk_db_pkg_add_file(struct apk_database *db, const char *file)
631 632 633 634
{
	struct apk_package *info;

	info = apk_pkg_read(db, file);
635 636 637
	if (info != NULL)
		apk_db_pkg_add(db, info);
	return info;
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
}

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

		db->installed.stats.files--;
	}
832
	apk_pkg_set_state(db, pkg, APK_STATE_NO_INSTALL);
833 834 835 836 837 838
}

int apk_db_install_pkg(struct apk_database *db,
		       struct apk_package *oldpkg,
		       struct apk_package *newpkg)
{
839
	struct apk_bstream *bs;
840 841 842
	struct install_ctx ctx;
	csum_t csum;
	char file[256];
Timo Teräs's avatar
Timo Teräs committed
843
	int r;
844

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

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

	/* Install the new stuff */
865 866 867 868
	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
869
		bs = apk_bstream_from_url(file);
870
	} else
Timo Teräs's avatar
Timo Teräs committed
871
		bs = apk_bstream_from_file(newpkg->filename);
872

Timo Teräs's avatar
Timo Teräs committed
873
	if (bs == NULL) {
874
		apk_error("%s: %s", file, strerror(errno));
875
		return errno;
876
	}
877 878 879 880 881 882 883

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

887
	bs->close(bs, csum);
888

889
	apk_pkg_set_state(db, newpkg, APK_STATE_INSTALL);
890 891 892 893 894

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

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