database.c 23 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

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

	int script;
31 32 33
	struct apk_db_dir_instance *diri;

	struct hlist_node **diri_node;
34
	struct hlist_node **file_dir_node;
35
	struct hlist_node **file_diri_node;
36 37
};

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

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

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

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

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

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

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,
79 80
	.hash_key = apk_blob_hash,
	.compare = apk_blob_compare,
81 82 83
	.delete_item = (apk_hash_delete_f) free,
};

84
struct apk_name *apk_db_get_name(struct apk_database *db, apk_blob_t name)
85 86 87 88 89 90 91 92 93 94 95
{
	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;

96
	pn->name = apk_blob_cstr(name);
97 98 99 100 101 102 103 104 105 106 107
	apk_hash_insert(&db->available.names, pn);

	return pn;
}

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

108
static void apk_db_dir_put(struct apk_database *db, struct apk_db_dir *dir)
109 110 111 112 113 114 115 116 117
{
	dir->refs--;
	if (dir->refs > 0)
		return;

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

	if (dir->parent != NULL)
118
		apk_db_dir_put(db, dir->parent);
119 120
}

121 122 123 124 125 126 127 128
static struct apk_db_dir *apk_db_dir_get(struct apk_db_dir *dir)
{
	dir->refs++;
	return dir;
}

static struct apk_db_dir *apk_db_dir_get_db(struct apk_database *db,
					    apk_blob_t name)
129 130 131
{
	struct apk_db_dir *dir;
	apk_blob_t bparent;
132
	int i;
133

134
	if (name.len && name.ptr[name.len-1] == '/')
135 136
		name.len--;

137
	dir = (struct apk_db_dir *) apk_hash_get(&db->installed.dirs, name);
138
	if (dir != NULL)
139
		return apk_db_dir_get(dir);
140

141
	db->installed.stats.dirs++;
142
	dir = calloc(1, sizeof(*dir) + name.len + 1);
143
	dir->refs = 1;
144 145 146 147
	memcpy(dir->dirname, name.ptr, name.len);
	dir->dirname[name.len] = 0;
	apk_hash_insert(&db->installed.dirs, dir);

148 149
	if (name.len == 0)
		dir->parent = NULL;
150
	else if (apk_blob_rsplit(name, '/', &bparent, NULL))
151
		dir->parent = apk_db_dir_get_db(db, bparent);
152
	else
153
		dir->parent = apk_db_dir_get_db(db, APK_BLOB_NULL);
154

155 156 157 158 159 160 161 162 163 164 165
	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;
	}

166 167 168
	return dir;
}

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
static struct apk_db_dir_instance *apk_db_diri_new(struct apk_database *db,
						   struct apk_package *pkg,
						   apk_blob_t name,
						   struct hlist_node **after)
{
	struct apk_db_dir_instance *diri;

	diri = calloc(1, sizeof(struct apk_db_dir_instance));
	hlist_add_after(&diri->pkg_dirs_list, after);
	diri->dir = apk_db_dir_get_db(db, name);
	diri->pkg = pkg;

	return diri;
}

static void apk_db_diri_set(struct apk_db_dir_instance *diri, mode_t mode,
			    uid_t uid, gid_t gid)
{
	diri->mode = mode;
	diri->uid = uid;
	diri->gid = gid;
}

static void apk_db_diri_create(struct apk_db_dir_instance *diri)
{
	if (diri->dir->refs == 1) {
		mkdir(diri->dir->dirname, diri->mode);
		chown(diri->dir->dirname, diri->uid, diri->gid);
	}
}

200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
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);
	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,
216
				  struct apk_db_dir_instance *diri,
217 218
				  struct hlist_node **after)
{
219 220
	if (file->diri != NULL) {
		hlist_del(&file->diri_files_list, &file->diri->owned_files);
221 222 223
	} else {
		db->installed.stats.files++;
	}
224 225
	file->diri = diri;
	hlist_add_after(&file->diri_files_list, after);
226 227 228
}

static struct apk_db_file *apk_db_file_get(struct apk_database *db,
229
					   apk_blob_t bfile,
230 231 232 233 234
					   struct install_ctx *ctx)
{
	struct apk_db_dir *dir;
	struct apk_db_file *file;
	struct hlist_node *cur;
235 236

	dir = ctx->diri->dir;
237 238 239 240 241 242 243

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

244 245
	if (ctx->file_dir_node == NULL)
		ctx->file_dir_node = hlist_tail_ptr(&dir->files);
246 247 248 249 250 251
	file = apk_db_file_new(dir, bfile, ctx->file_dir_node);
	ctx->file_dir_node = &file->dir_files_list.next;

	return file;
}

252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
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)
270 271
{
	struct apk_package *pkg = NULL;
272
	struct apk_db_dir_instance *diri = NULL;
273
	struct apk_db_file *file = NULL;
274
	struct hlist_node **diri_node = NULL;
275
	struct hlist_node **file_dir_node = NULL;
276
	struct hlist_node **file_diri_node = NULL;
277 278 279

	char buf[1024];
	apk_blob_t l, r;
280
	int n, field, ret;
281

282
	ret = -1;
283 284
	r = APK_BLOB_PTR_LEN(buf, 0);
	while (1) {
Timo Teräs's avatar
Timo Teräs committed
285
		n = is->read(is, &r.ptr[r.len], sizeof(buf) - r.len);
286 287 288 289 290
		if (n <= 0)
			break;
		r.len += n;

		while (apk_blob_splitstr(r, "\n", &l, &r)) {
291 292 293 294 295 296 297 298 299 300 301
			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");
302 303
					return -1;
				}
304 305 306 307 308 309 310 311 312 313 314 315
				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();
316 317
				diri = NULL;
				diri_node = &pkg->owned_dirs.first;
318
				file_dir_node = NULL;
319
				file_diri_node = NULL;
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
			}

			/* 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) {
335 336 337
					apk_error("FDB directory entry before package entry");
					return -1;
				}
338 339 340 341
				diri = apk_db_diri_new(db, pkg, l, diri_node);
				diri_node = &diri->pkg_dirs_list.next;
				file_dir_node = hlist_tail_ptr(&diri->dir->files);
				file_diri_node = &diri->owned_files.first;
342
				break;
343
			case 'M':
344
				if (diri == NULL) {
345 346 347
					apk_error("FDB directory metadata entry before directory entry");
					return -1;
				}
348 349
				sscanf(l.ptr, "%d:%d:%o",
				       &diri->uid, &diri->gid, &diri->mode);
350 351
				break;
			case 'R':
352
				if (diri == NULL) {
353 354 355
					apk_error("FDB file entry before directory entry");
					return -1;
				}
356
				file = apk_db_file_new(diri->dir, l, file_dir_node);
357
				file_dir_node = &file->dir_files_list.next;
358 359
				apk_db_file_set_owner(db, file, diri, file_diri_node);
				file_diri_node = &file->diri_files_list.next;
360
				break;
361
			case 'Z':
362 363 364 365 366 367 368 369 370
				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;
371 372 373 374 375 376 377 378 379
			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);
	}
380
	ret = 0;
381

382
	return ret;
383 384
}

Timo Teräs's avatar
Timo Teräs committed
385
static int apk_db_write_fdb(struct apk_database *db, struct apk_ostream *os)
386 387
{
	struct apk_package *pkg;
388
	struct apk_db_dir_instance *diri;
389
	struct apk_db_file *file;
390
	struct hlist_node *c1, *c2;
391
	char buf[1024];
392
	apk_blob_t blob;
393
	int n = 0;
394

395
	list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) {
396 397
		blob = apk_pkg_format_index_entry(pkg, sizeof(buf), buf);
		if (blob.ptr)
Timo Teräs's avatar
Timo Teräs committed
398
			os->write(os, blob.ptr, blob.len - 1);
399

400 401 402 403 404 405
		hlist_for_each_entry(diri, c1, &pkg->owned_dirs, pkg_dirs_list) {
			n += snprintf(&buf[n], sizeof(buf)-n,
				      "F:%s\n"
				      "M:%d:%d:%o\n",
				      diri->dir->dirname,
				      diri->uid, diri->gid, diri->mode);
406

407
			hlist_for_each_entry(file, c2, &diri->owned_files, diri_files_list) {
408
				n += snprintf(&buf[n], sizeof(buf)-n,
409 410 411 412 413 414 415 416
					      "R:%s\n",
					      file->filename);
				if (csum_valid(file->csum)) {
					n += snprintf(&buf[n], sizeof(buf)-n, "Z:");
					n += apk_hexdump_format(sizeof(buf)-n, &buf[n],
								APK_BLOB_BUF(file->csum));
					n += snprintf(&buf[n], sizeof(buf)-n, "\n");
				}
417

418 419 420
				if (os->write(os, buf, n) != n)
					return -1;
				n = 0;
421
			}
422
		}
Timo Teräs's avatar
Timo Teräs committed
423
		os->write(os, "\n", 1);
424 425 426 427 428 429 430 431 432 433 434
	}

	return 0;
}

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

Timo Teräs's avatar
Timo Teräs committed
435
static int apk_db_scriptdb_write(struct apk_database *db, struct apk_ostream *os)
436 437 438 439
{
	struct apk_package *pkg;
	struct apk_script *script;
	struct apk_script_header hdr;
440
	struct hlist_node *c2;
441

442
	list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) {
443 444 445 446 447
		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
448 449 450 451 452
			if (os->write(os, &hdr, sizeof(hdr)) != sizeof(hdr))
				return -1;

			if (os->write(os, script->script, script->size) != script->size)
				return -1;
453 454 455 456 457 458
		}
	}

	return 0;
}

459
static int apk_db_scriptdb_read(struct apk_database *db, struct apk_istream *is)
460 461 462 463
{
	struct apk_package *pkg;
	struct apk_script_header hdr;

464
	while (is->read(is, &hdr, sizeof(hdr)) == sizeof(hdr)) {
465
		pkg = apk_db_get_pkg(db, hdr.csum);
466 467
		if (pkg != NULL)
			apk_pkg_add_script(pkg, is, hdr.type, hdr.size);
468 469 470 471 472
	}

	return 0;
}

473
int apk_db_create(const char *root)
474
{
475
	apk_blob_t deps = APK_BLOB_STR("busybox, alpine-baselayout, "
476
				       "apk-tools, alpine-conf\n");
477
	int fd;
478

479 480
	fchdir(apk_cwd_fd);
	chdir(root);
481

482 483 484 485 486 487 488 489
	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);
490 491 492 493 494 495
	if (fd < 0)
		return -1;
	write(fd, deps.ptr, deps.len);
	close(fd);

	return 0;
496 497
}

498
static int apk_db_read_state(struct apk_database *db)
499
{
500
	struct apk_istream *is;
Timo Teräs's avatar
Timo Teräs committed
501
	apk_blob_t blob;
502 503

	if (db->root == NULL)
504
		return 0;
505 506 507 508 509 510 511 512 513

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

Timo Teräs's avatar
Timo Teräs committed
516 517
	blob = apk_blob_from_file("var/lib/apk/world");
	if (APK_BLOB_IS_NULL(blob)) {
518
		apk_error("Please run 'apk create' to initialize root");
519
		return -1;
520
	}
Timo Teräs's avatar
Timo Teräs committed
521 522
	apk_deps_parse(db, &db->world, blob);
	free(blob.ptr);
523

524
	is = apk_istream_from_file("var/lib/apk/installed");
Timo Teräs's avatar
Timo Teräs committed
525
	if (is != NULL) {
526
		apk_db_index_read(db, is, -1);
Timo Teräs's avatar
Timo Teräs committed
527
		is->close(is);
528 529
	}

530 531
	is = apk_istream_from_file("var/lib/apk/scripts");
	if (is != NULL) {
532
		apk_db_scriptdb_read(db, is);
533
		is->close(is);
534 535 536 537 538
	}

	return 0;
}

539 540 541 542 543 544 545 546
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;
}

547 548
int apk_db_open(struct apk_database *db, const char *root)
{
549
	apk_blob_t blob;
550

551 552 553 554
	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);
555
	list_init(&db->installed.packages);
556 557 558 559 560

	if (root != NULL) {
		db->root = strdup(root);
		db->root_fd = open(root, O_RDONLY);
		if (db->root_fd < 0) {
561
			apk_error("%s: %s", root, strerror(errno));
562 563 564 565
			free(db->root);
			return -1;
		}
	}
566 567 568 569 570 571 572 573 574 575 576 577 578 579

	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
580
	if (apk_repository != NULL)
581
		apk_db_add_repository(db, APK_BLOB_STR(apk_repository));
582

583
	return 0;
584 585
}

586 587 588 589 590
struct write_ctx {
	struct apk_database *db;
	int fd;
};

591
static int apk_db_write_config(struct apk_database *db)
592
{
Timo Teräs's avatar
Timo Teräs committed
593
	struct apk_ostream *os;
594
	char buf[1024];
Timo Teräs's avatar
Timo Teräs committed
595
	int n;
596 597

	if (db->root == NULL)
598
		return 0;
599

600 601
	fchdir(db->root_fd);

Timo Teräs's avatar
Timo Teräs committed
602 603
	os = apk_ostream_to_file("var/lib/apk/world", 0600);
	if (os == NULL)
604 605
		return -1;
	n = apk_deps_format(buf, sizeof(buf), db->world);
Timo Teräs's avatar
Timo Teräs committed
606 607
	os->write(os, buf, n);
	os->close(os);
608

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

Timo Teräs's avatar
Timo Teräs committed
615 616
	os = apk_ostream_to_file("var/lib/apk/scripts", 0600);
	if (os == NULL)
617
		return -1;
Timo Teräs's avatar
Timo Teräs committed
618 619
	apk_db_scriptdb_write(db, os);
	os->close(os);
620 621 622 623

	return 0;
}

624
void apk_db_close(struct apk_database *db)
625 626 627 628
{
	apk_hash_free(&db->available.names);
	apk_hash_free(&db->available.packages);
	apk_hash_free(&db->installed.dirs);
629 630
	if (db->root != NULL) {
		close(db->root_fd);
631
		free(db->root);
632
	}
633 634 635 636
}

struct apk_package *apk_db_get_pkg(struct apk_database *db, csum_t sum)
{
637 638
	return apk_hash_get(&db->available.packages,
			    APK_BLOB_PTR_LEN((void*) sum, sizeof(csum_t)));
639 640
}

641
struct apk_package *apk_db_pkg_add_file(struct apk_database *db, const char *file)
642 643 644 645
{
	struct apk_package *info;

	info = apk_pkg_read(db, file);
646 647 648
	if (info != NULL)
		apk_db_pkg_add(db, info);
	return info;
649 650 651 652
}

static int write_index_entry(apk_hash_item item, void *ctx)
{
Timo Teräs's avatar
Timo Teräs committed
653
	struct apk_ostream *os = (struct apk_ostream *) ctx;
654 655 656 657
	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
658 659 660 661 662
	if (APK_BLOB_IS_NULL(blob))
		return 0;

	if (os->write(os, blob.ptr, blob.len) != blob.len)
		return -1;
663 664 665 666

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
667
void apk_db_index_write(struct apk_database *db, struct apk_ostream *os)
668
{
Timo Teräs's avatar
Timo Teräs committed
669
	apk_hash_foreach(&db->available.packages, write_index_entry, (void *) os);
670 671
}

672
int apk_db_add_repository(apk_database_t _db, apk_blob_t repository)
673
{
674 675
	struct apk_database *db = _db.db;
	struct apk_istream *is;
676
	char tmp[256];
677
	int r;
678 679 680 681 682 683

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

	r = db->num_repos++;
	db->repos[r] = (struct apk_repository){
684
		.url = apk_blob_cstr(repository)
685 686
	};

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

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

726
static int apk_db_install_archive_entry(void *_ctx,
727
					const struct apk_file_info *ae,
728
					struct apk_istream *is)
729
{
730
	struct install_ctx *ctx = (struct install_ctx *) _ctx;
731
	struct apk_database *db = ctx->db;
732 733 734
	struct apk_package *pkg = ctx->pkg, *opkg;
	apk_blob_t name = APK_BLOB_STR(ae->name), bdir, bfile;
	struct apk_db_dir_instance *diri = ctx->diri;
735
	struct apk_db_file *file;
736 737
	struct apk_file_info fi;
	char alt_name[PATH_MAX];
738
	const char *p;
Timo Teräs's avatar
Timo Teräs committed
739
	int r = 0, type = APK_SCRIPT_INVALID;
740

Timo Teräs's avatar
Timo Teräs committed
741 742 743 744 745 746 747 748
	/* 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 */
749 750 751 752 753 754 755 756 757
		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);
758
		if (type == APK_SCRIPT_INVALID)
759
			return 0;
Timo Teräs's avatar
Timo Teräs committed
760
	}
761

Timo Teräs's avatar
Timo Teräs committed
762 763
	/* Handle script */
	if (type != APK_SCRIPT_INVALID) {
764
		apk_pkg_add_script(pkg, is, type, ae->size);
765

Timo Teräs's avatar
Timo Teräs committed
766 767
		if (type == APK_SCRIPT_GENERIC ||
		    type == ctx->script) {
768
			r = apk_pkg_run_script(pkg, db->root_fd, ctx->script);
769 770 771 772 773 774 775 776
			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
777
	/* Installable entry */
778
	if (!S_ISDIR(ae->mode)) {
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
		if (!apk_blob_rsplit(name, '/', &bdir, &bfile))
			return 0;

		if (bfile.len > 6 && memcmp(bfile.ptr, ".keep_", 6) == 0)
			return 0;

		r = strlen(diri->dir->dirname);
		r = strlen(bdir.ptr);
		r = 0;

		/* Make sure the file is part of the cached directory tree */
		if (diri == NULL ||
		    strncmp(diri->dir->dirname, bdir.ptr, bdir.len) != 0 ||
		    diri->dir->dirname[bdir.len] != 0) {
			struct hlist_node *n;
794

795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
			hlist_for_each_entry(diri, n, &pkg->owned_dirs, pkg_dirs_list) {
				if (strncmp(diri->dir->dirname, bdir.ptr, bdir.len) == 0 &&
				    diri->dir->dirname[bdir.len] == 0)
					break;
			}
			if (diri == NULL) {
				apk_error("%s: File '%*s' entry without directory entry.\n",
					  pkg->name->name, name.len, name.ptr);
				return -1;
			}
			ctx->diri = diri;
		}

		file = apk_db_file_get(db, name, ctx);
		if (file == NULL) {
			apk_error("%s: Failed to create fdb entry for '%*s'\n",
				  pkg->name->name, name.len, name.ptr);
812 813 814
			return -1;
		}

815 816 817 818 819 820 821 822 823
		if (file->diri != NULL) {
			opkg = file->diri->pkg;
			if (opkg->name != pkg->name &&
			    strcmp(opkg->name->name, "busybox") != 0) {
				apk_error("%s: Trying to overwrite %s owned by %s.\n",
					  pkg->name->name, ae->name, opkg->name->name);
				return -1;
			}
		}
824

825 826 827 828
		if (ctx->file_diri_node == NULL)
			ctx->file_diri_node = hlist_tail_ptr(&diri->owned_files);
		apk_db_file_set_owner(db, file, diri, ctx->file_diri_node);
		ctx->file_diri_node = &file->diri_files_list.next;
829

830
		if ((diri->dir->flags & APK_DBDIRF_PROTECTED) &&
831 832 833 834 835 836 837
		    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",
838
				 diri->dir->dirname, file->filename);
839 840 841 842 843
			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));
844 845 846
	} else {
		if (name.ptr[name.len-1] == '/')
			name.len--;
847 848 849 850 851 852 853 854 855 856 857 858 859

		printf("%s: %*s\n", pkg->name->name, name.len, name.ptr);

		if (ctx->diri_node == NULL)
			ctx->diri_node = hlist_tail_ptr(&pkg->owned_dirs);
		ctx->diri = diri = apk_db_diri_new(db, pkg, name,
						   ctx->diri_node);
		ctx->diri_node = &diri->pkg_dirs_list.next;
		ctx->file_dir_node = NULL;
		ctx->file_diri_node = NULL;

		apk_db_diri_set(diri, ae->mode & 0777, ae->uid, ae->gid);
		apk_db_diri_create(diri);
860 861 862 863 864 865 866 867
	}

	return r;
}

static void apk_db_purge_pkg(struct apk_database *db,
			     struct apk_package *pkg)
{
868
	struct apk_db_dir_instance *diri;
869
	struct apk_db_file *file;
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885
	struct hlist_node *dc, *dn, *fc, *fn;
	char name[1024];

	hlist_for_each_entry_safe(diri, dc, dn, &pkg->owned_dirs, pkg_dirs_list) {
		hlist_for_each_entry_safe(file, fc, fn, &diri->owned_files, diri_files_list) {
			file->diri = NULL;
			snprintf(name, sizeof(name), "%s/%s",
				 diri->dir->dirname,
				 file->filename);
			unlink(name);
			__hlist_del(fc, &diri->owned_files.first);

			db->installed.stats.files--;
		}
		apk_db_dir_put(db, diri->dir);
		__hlist_del(dc, &pkg->owned_dirs.first);
886
	}
887
	apk_pkg_set_state(db, pkg, APK_STATE_NO_INSTALL);
888 889 890 891 892 893
}

int apk_db_install_pkg(struct apk_database *db,
		       struct apk_package *oldpkg,
		       struct apk_package *newpkg)
{
894
	struct apk_bstream *bs;
895 896 897
	struct install_ctx ctx;
	csum_t csum;
	char file[256];
Timo Teräs's avatar
Timo Teräs committed
898
	int r;
899

900
	if (fchdir(db->root_fd) < 0)
901 902 903 904 905
		return errno;

	/* Purge the old package if there */
	if (oldpkg != NULL) {
		if (newpkg == NULL) {
906
			r = apk_pkg_run_script(oldpkg, db->root_fd,
907 908 909 910 911 912
					       APK_SCRIPT_PRE_DEINSTALL);
			if (r != 0)
				return r;
		}
		apk_db_purge_pkg(db, oldpkg);
		if (newpkg == NULL) {
913
			apk_pkg_run_script(oldpkg, db->root_fd,
914 915 916 917 918 919
					   APK_SCRIPT_POST_DEINSTALL);
			return 0;
		}
	}

	/* Install the new stuff */
920 921 922 923
	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
924
		bs = apk_bstream_from_url(file);
925
	} else
Timo Teräs's avatar
Timo Teräs committed
926
		bs = apk_bstream_from_file(newpkg->filename);
927

Timo Teräs's avatar
Timo Teräs committed
928
	if (bs == NULL) {
929
		apk_error("%s: %s", file, strerror(errno));
930
		return errno;
931
	}
932 933 934 935 936 937 938

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

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

944
	apk_pkg_set_state(db, newpkg, APK_STATE_INSTALL);
945 946 947 948 949

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

950
	r = apk_pkg_run_script(newpkg, db->root_fd,
951 952
			       (oldpkg == NULL) ?
			       APK_SCRIPT_POST_INSTALL : APK_SCRIPT_POST_UPGRADE);
953
	if (r != 0) {
954 955
		apk_error("%s-%s: Failed to execute post-install/upgrade script",
			  newpkg->name->name, newpkg->version);
956 957 958
	} else if (apk_quiet) {
		write(STDOUT_FILENO, ".", 1);
	}
959 960
	return r;
err_close:
Timo Teräs's avatar
Timo Teräs committed
961
	bs->close(bs, NULL, NULL);
962 963
	return -1;
}