database.c 55.1 KB
Newer Older
1 2 3
/* database.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
4
 * Copyright (C) 2008-2009 Timo Teräs <timo.teras@iki.fi>
5 6
 * All rights reserved.
 *
7
 * This program is free software; you can redistribute it and/or modify it
8 9 10 11 12 13 14
 * 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
#include <unistd.h>
#include <malloc.h>
#include <string.h>
19
#include <stdlib.h>
20
#include <signal.h>
21
#include <fnmatch.h>
Timo Teräs's avatar
Timo Teräs committed
22
#include <sys/vfs.h>
23
#include <sys/file.h>
Natanael Copa's avatar
Natanael Copa committed
24
#include <sys/stat.h>
25 26 27 28 29

#include "apk_defines.h"
#include "apk_package.h"
#include "apk_database.h"
#include "apk_state.h"
30
#include "apk_applet.h"
31
#include "apk_archive.h"
Natanael Copa's avatar
Natanael Copa committed
32 33
#include "apk_print.h"

34 35 36 37 38 39 40 41
#if defined(__x86_64__)
#define APK_DEFAULT_ARCH	"x86_64"
#elif defined(__i386__)
#define APK_DEFAULT_ARCH	"x86"
#else
#define APK_DEFAULT_ARCH	"noarch"
#endif

42 43 44 45 46
enum {
	APK_DISALLOW_RMDIR = 0,
	APK_ALLOW_RMDIR = 1
};

Natanael Copa's avatar
Natanael Copa committed
47 48
int apk_verbosity = 1;
unsigned int apk_flags = 0;
49
const char *apk_arch = APK_DEFAULT_ARCH;
50

51
const char * const apkindex_tar_gz = "APKINDEX.tar.gz";
Timo Teräs's avatar
Timo Teräs committed
52 53 54
static const char * const apk_static_cache_dir = "var/lib/apk";
static const char * const apk_linked_cache_dir = "etc/apk/cache";

55 56 57
struct install_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
58
	struct apk_installed_package *ipkg;
59 60

	int script;
61
	char **script_args;
62
	int script_pending : 1;
63

64
	struct apk_db_dir_instance *diri;
Timo Teräs's avatar
Timo Teräs committed
65
	struct apk_checksum data_csum;
66
	struct apk_sign_ctx sctx;
67
	struct apk_name_array *replaces;
68

69 70 71 72 73
	apk_progress_cb cb;
	void *cb_ctx;
	size_t installed_size;
	size_t current_file_size;

74 75
	struct hlist_node **diri_node;
	struct hlist_node **file_diri_node;
76 77
};

78
static apk_blob_t pkg_name_get_key(apk_hash_item item)
79
{
80
	return APK_BLOB_STR(((struct apk_name *) item)->name);
81 82
}

83 84 85
static void pkg_name_free(struct apk_name *name)
{
	free(name->name);
Timo Teräs's avatar
Timo Teräs committed
86 87
	apk_package_array_free(&name->pkgs);
	apk_name_array_free(&name->rdepends);
Timo Teräs's avatar
Timo Teräs committed
88
	apk_name_array_free(&name->rinstall_if);
89 90 91
	free(name);
}

92 93 94
static const struct apk_hash_ops pkg_name_hash_ops = {
	.node_offset = offsetof(struct apk_name, hash_node),
	.get_key = pkg_name_get_key,
95 96
	.hash_key = apk_blob_hash,
	.compare = apk_blob_compare,
97
	.delete_item = (apk_hash_delete_f) pkg_name_free,
98 99
};

100
static apk_blob_t pkg_info_get_key(apk_hash_item item)
101
{
Timo Teräs's avatar
Timo Teräs committed
102
	return APK_BLOB_CSUM(((struct apk_package *) item)->csum);
103 104
}

105
static unsigned long csum_hash(apk_blob_t csum)
106
{
107 108
	/* Checksum's highest bits have the most "randomness", use that
	 * directly as hash */
109
	return *(unsigned long *) csum.ptr;
110 111 112 113 114
}

static const struct apk_hash_ops pkg_info_hash_ops = {
	.node_offset = offsetof(struct apk_package, hash_node),
	.get_key = pkg_info_get_key,
115 116
	.hash_key = csum_hash,
	.compare = apk_blob_compare,
117 118 119
	.delete_item = (apk_hash_delete_f) apk_pkg_free,
};

120
static apk_blob_t apk_db_dir_get_key(apk_hash_item item)
121
{
Timo Teräs's avatar
Timo Teräs committed
122 123
	struct apk_db_dir *dir = (struct apk_db_dir *) item;
	return APK_BLOB_PTR_LEN(dir->name, dir->namelen);
124 125 126 127 128
}

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,
129 130
	.hash_key = apk_blob_hash,
	.compare = apk_blob_compare,
131 132 133
	.delete_item = (apk_hash_delete_f) free,
};

134 135 136 137 138 139
struct apk_db_file_hash_key {
	apk_blob_t dirname;
	apk_blob_t filename;
};

static unsigned long apk_db_file_hash_key(apk_blob_t _key)
140
{
141 142
	struct apk_db_file_hash_key *key = (struct apk_db_file_hash_key *) _key.ptr;

Timo Teräs's avatar
Timo Teräs committed
143
	return apk_blob_hash_seed(key->filename, apk_blob_hash(key->dirname));
144 145 146 147 148 149
}

static unsigned long apk_db_file_hash_item(apk_hash_item item)
{
	struct apk_db_file *dbf = (struct apk_db_file *) item;

Timo Teräs's avatar
Timo Teräs committed
150 151
	return apk_blob_hash_seed(APK_BLOB_PTR_LEN(dbf->name, dbf->namelen),
				  dbf->diri->dir->hash);
152 153 154 155 156 157
}

static int apk_db_file_compare_item(apk_hash_item item, apk_blob_t _key)
{
	struct apk_db_file *dbf = (struct apk_db_file *) item;
	struct apk_db_file_hash_key *key = (struct apk_db_file_hash_key *) _key.ptr;
Timo Teräs's avatar
Timo Teräs committed
158
	struct apk_db_dir *dir = dbf->diri->dir;
159 160
	int r;

Timo Teräs's avatar
Timo Teräs committed
161 162
	r = apk_blob_compare(key->filename,
			     APK_BLOB_PTR_LEN(dbf->name, dbf->namelen));
163 164 165
	if (r != 0)
		return r;

Timo Teräs's avatar
Timo Teräs committed
166 167 168
	r = apk_blob_compare(key->dirname,
			     APK_BLOB_PTR_LEN(dir->name, dir->namelen));
	return r;
169 170 171 172
}

static const struct apk_hash_ops file_hash_ops = {
	.node_offset = offsetof(struct apk_db_file, hash_node),
173 174 175
	.hash_key = apk_db_file_hash_key,
	.hash_item = apk_db_file_hash_item,
	.compare_item = apk_db_file_compare_item,
176 177 178
	.delete_item = (apk_hash_delete_f) free,
};

Timo Teräs's avatar
Timo Teräs committed
179 180 181 182 183
struct apk_name *apk_db_query_name(struct apk_database *db, apk_blob_t name)
{
	return (struct apk_name *) apk_hash_get(&db->available.names, name);
}

184
struct apk_name *apk_db_get_name(struct apk_database *db, apk_blob_t name)
185 186
{
	struct apk_name *pn;
187
	unsigned long hash = apk_hash_from_key(&db->available.names, name);
188

189
	pn = (struct apk_name *) apk_hash_get_hashed(&db->available.names, name, hash);
190 191 192 193 194 195 196
	if (pn != NULL)
		return pn;

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

197
	pn->name = apk_blob_cstr(name);
198
	pn->id = db->name_id++;
Timo Teräs's avatar
Timo Teräs committed
199 200
	apk_package_array_init(&pn->pkgs);
	apk_name_array_init(&pn->rdepends);
Timo Teräs's avatar
Timo Teräs committed
201
	apk_name_array_init(&pn->rinstall_if);
202
	apk_hash_insert_hashed(&db->available.names, pn, hash);
203 204 205 206

	return pn;
}

207 208
static void apk_db_dir_unref(struct apk_database *db, struct apk_db_dir *dir,
			     int allow_rmdir)
209 210 211 212 213 214
{
	dir->refs--;
	if (dir->refs > 0)
		return;

	db->installed.stats.dirs--;
215 216
	if (allow_rmdir)
		unlinkat(db->root_fd, dir->name, AT_REMOVEDIR);
217 218

	if (dir->parent != NULL)
219
		apk_db_dir_unref(db, dir->parent, allow_rmdir);
220 221
}

222
static struct apk_db_dir *apk_db_dir_ref(struct apk_db_dir *dir)
223 224 225 226 227
{
	dir->refs++;
	return dir;
}

228 229
struct apk_db_dir *apk_db_dir_query(struct apk_database *db,
				    apk_blob_t name)
230 231 232 233
{
	return (struct apk_db_dir *) apk_hash_get(&db->installed.dirs, name);
}

234 235
static struct apk_db_dir *apk_db_dir_get(struct apk_database *db,
					 apk_blob_t name)
236 237 238
{
	struct apk_db_dir *dir;
	apk_blob_t bparent;
239
	unsigned long hash = apk_hash_from_key(&db->installed.dirs, name);
240
	int i;
241

242
	if (name.len && name.ptr[name.len-1] == '/')
243 244
		name.len--;

245
	dir = (struct apk_db_dir *) apk_hash_get_hashed(&db->installed.dirs, name, hash);
246
	if (dir != NULL)
247
		return apk_db_dir_ref(dir);
248

249
	db->installed.stats.dirs++;
250 251
	dir = malloc(sizeof(*dir) + name.len + 1);
	memset(dir, 0, sizeof(*dir));
252
	dir->refs = 1;
253
	dir->rooted_name[0] = '/';
Timo Teräs's avatar
Timo Teräs committed
254 255 256 257
	memcpy(dir->name, name.ptr, name.len);
	dir->name[name.len] = 0;
	dir->namelen = name.len;
	dir->hash = hash;
258
	apk_hash_insert_hashed(&db->installed.dirs, dir, hash);
259

260 261
	if (name.len == 0)
		dir->parent = NULL;
262
	else if (apk_blob_rsplit(name, '/', &bparent, NULL))
263
		dir->parent = apk_db_dir_get(db, bparent);
264
	else
265
		dir->parent = apk_db_dir_get(db, APK_BLOB_NULL);
266

267 268 269 270
	if (dir->parent != NULL)
		dir->flags = dir->parent->flags;

	for (i = 0; i < db->protected_paths->num; i++) {
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
		int flags = dir->flags, j;

		flags |= APK_DBDIRF_PROTECTED;
		for (j = 0; ; j++) {
			switch (db->protected_paths->item[i][j]) {
			case '-':
				flags &= ~(APK_DBDIRF_PROTECTED |
					   APK_DBDIRF_SYMLINKS_ONLY);
				continue;
			case '*':
				flags |= APK_DBDIRF_SYMLINKS_ONLY |
					 APK_DBDIRF_PROTECTED;
				continue;
			}
			break;
		}

		if (strcmp(&db->protected_paths->item[i][j], dir->name) == 0)
			dir->flags = flags;
290 291
	}

292 293 294
	return dir;
}

295 296 297
static struct apk_db_dir_instance *apk_db_diri_new(struct apk_database *db,
						   struct apk_package *pkg,
						   apk_blob_t name,
298
						   struct hlist_node ***after)
299 300 301 302
{
	struct apk_db_dir_instance *diri;

	diri = calloc(1, sizeof(struct apk_db_dir_instance));
303
	if (diri != NULL) {
304 305
		hlist_add_after(&diri->pkg_dirs_list, *after);
		*after = &diri->pkg_dirs_list.next;
306
		diri->dir = apk_db_dir_get(db, name);
307 308
		diri->pkg = pkg;
	}
309 310 311 312 313 314 315 316 317 318 319 320

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

321
static void apk_db_diri_mkdir(struct apk_database *db, struct apk_db_dir_instance *diri)
322
{
323 324 325 326
	if (mkdirat(db->root_fd, diri->dir->name, diri->mode) == 0) {
		if (fchownat(db->root_fd, diri->dir->name, diri->uid, diri->gid, 0) != 0)
			;
	}
327 328
}

329
static void apk_db_diri_free(struct apk_database *db,
330 331
			     struct apk_db_dir_instance *diri,
			     int allow_rmdir)
332
{
333
	apk_db_dir_unref(db, diri->dir, allow_rmdir);
334 335 336
	free(diri);
}

337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
struct apk_db_file *apk_db_file_query(struct apk_database *db,
				      apk_blob_t dir,
				      apk_blob_t name)
{
	struct apk_db_file_hash_key key;

	key = (struct apk_db_file_hash_key) {
		.dirname = dir,
		.filename = name,
	};

	return (struct apk_db_file *) apk_hash_get(&db->installed.files,
						   APK_BLOB_BUF(&key));
}

Timo Teräs's avatar
Timo Teräs committed
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
static struct apk_db_file *apk_db_file_new(struct apk_db_dir_instance *diri,
					   apk_blob_t name,
					   struct hlist_node ***after)
{
	struct apk_db_file *file;

	file = malloc(sizeof(*file) + name.len + 1);
	if (file == NULL)
		return NULL;

	memset(file, 0, sizeof(*file));
	memcpy(file->name, name.ptr, name.len);
	file->name[name.len] = 0;
	file->namelen = name.len;

	file->diri = diri;
	hlist_add_after(&file->diri_files_list, *after);
	*after = &file->diri_files_list.next;

	return file;
}

374
static struct apk_db_file *apk_db_file_get(struct apk_database *db,
375
					   struct apk_db_dir_instance *diri,
376 377
					   apk_blob_t name,
					   struct hlist_node ***after)
378 379
{
	struct apk_db_file *file;
380
	struct apk_db_file_hash_key key;
Timo Teräs's avatar
Timo Teräs committed
381 382
	struct apk_db_dir *dir = diri->dir;
	unsigned long hash;
383 384

	key = (struct apk_db_file_hash_key) {
Timo Teräs's avatar
Timo Teräs committed
385
		.dirname = APK_BLOB_PTR_LEN(dir->name, dir->namelen),
386 387
		.filename = name,
	};
388

Timo Teräs's avatar
Timo Teräs committed
389 390 391
	hash = apk_blob_hash_seed(name, dir->hash);
	file = (struct apk_db_file *) apk_hash_get_hashed(
		&db->installed.files, APK_BLOB_BUF(&key), hash);
392 393 394
	if (file != NULL)
		return file;

Timo Teräs's avatar
Timo Teräs committed
395
	file = apk_db_file_new(diri, name, after);
Timo Teräs's avatar
Timo Teräs committed
396
	apk_hash_insert_hashed(&db->installed.files, file, hash);
397
	db->installed.stats.files++;
398 399 400 401

	return file;
}

Timo Teräs's avatar
Timo Teräs committed
402 403 404
static void apk_db_pkg_rdepends(struct apk_database *db, struct apk_package *pkg)
{
	int i, j;
Timo Teräs's avatar
Timo Teräs committed
405
	struct apk_name *rname;
Timo Teräs's avatar
Timo Teräs committed
406 407

	for (i = 0; i < pkg->depends->num; i++) {
Timo Teräs's avatar
Timo Teräs committed
408
		rname = pkg->depends->item[i].name;
Timo Teräs's avatar
Timo Teräs committed
409 410
		for (j = 0; j < rname->rdepends->num; j++)
			if (rname->rdepends->item[j] == pkg->name)
Timo Teräs's avatar
Timo Teräs committed
411
				goto rdeps_done;
Timo Teräs's avatar
Timo Teräs committed
412 413
		*apk_name_array_add(&rname->rdepends) = pkg->name;
	}
Timo Teräs's avatar
Timo Teräs committed
414 415 416 417 418 419 420 421 422 423
rdeps_done:
	for (i = 0; i < pkg->install_if->num; i++) {
		rname = pkg->install_if->item[i].name;
		for (j = 0; j < rname->rinstall_if->num; j++)
			if (rname->rinstall_if->item[j] == pkg->name)
				goto riif_done;
		*apk_name_array_add(&rname->rinstall_if) = pkg->name;
	}
riif_done:
	return;
Timo Teräs's avatar
Timo Teräs committed
424 425
}

426
struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package *pkg)
427 428 429
{
	struct apk_package *idb;

430
	if (pkg->license == NULL)
431
		pkg->license = apk_blob_atomize(APK_BLOB_NULL);
432

Timo Teräs's avatar
Timo Teräs committed
433
	idb = apk_hash_get(&db->available.packages, APK_BLOB_CSUM(pkg->csum));
434 435 436 437
	if (idb == NULL) {
		idb = pkg;
		apk_hash_insert(&db->available.packages, pkg);
		*apk_package_array_add(&pkg->name->pkgs) = pkg;
Timo Teräs's avatar
Timo Teräs committed
438
		apk_db_pkg_rdepends(db, pkg);
439 440
	} else {
		idb->repos |= pkg->repos;
441 442 443 444
		if (idb->filename == NULL && pkg->filename != NULL) {
			idb->filename = pkg->filename;
			pkg->filename = NULL;
		}
445 446 447 448 449
		if (idb->ipkg == NULL && pkg->ipkg != NULL) {
			idb->ipkg = pkg->ipkg;
			idb->ipkg->pkg = idb;
			pkg->ipkg = NULL;
		}
450 451 452 453 454
		apk_pkg_free(pkg);
	}
	return idb;
}

455
void apk_cache_format_index(apk_blob_t to, struct apk_repository *repo)
456
{
457
	/* APKINDEX.12345678.tar.gz */
458
	apk_blob_push_blob(&to, APK_BLOB_STR("APKINDEX."));
459 460
	apk_blob_push_hexdump(&to, APK_BLOB_PTR_LEN((char *) repo->csum.data,
						    APK_CACHE_CSUM_BYTES));
461
	apk_blob_push_blob(&to, APK_BLOB_STR(".tar.gz"));
462 463 464
	apk_blob_push_blob(&to, APK_BLOB_PTR_LEN("", 1));
}

465
int apk_cache_download(struct apk_database *db, const char *url, apk_blob_t *arch,
466 467 468 469 470
		       const char *item, const char *cacheitem, int verify)
{
	char fullurl[PATH_MAX];
	int r;

471 472 473 474 475 476 477 478
	if (arch != NULL)
		snprintf(fullurl, sizeof(fullurl), "%s%s" BLOB_FMT "/%s",
			 url, url[strlen(url)-1] == '/' ? "" : "/",
			 BLOB_PRINTF(*arch), item);
	else
		snprintf(fullurl, sizeof(fullurl), "%s%s/%s",
			 url, url[strlen(url)-1] == '/' ? "" : "/",
			 item);
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
	apk_message("fetch %s", fullurl);

	if (apk_flags & APK_SIMULATE)
		return 0;

	r = apk_url_download(fullurl, db->cachetmp_fd, cacheitem);
	if (r < 0)
		return r;

	if (verify != APK_SIGN_NONE) {
		struct apk_istream *is;
		struct apk_sign_ctx sctx;

		apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL, db->keys_fd);
		is = apk_bstream_gunzip_mpart(
			apk_bstream_from_file(db->cachetmp_fd, cacheitem),
			apk_sign_ctx_mpart_cb, &sctx);

497
		r = apk_tar_parse(is, apk_sign_ctx_verify_tar, &sctx, FALSE, &db->id_cache);
498 499 500 501 502 503 504
		is->close(is);
		apk_sign_ctx_free(&sctx);
		if (r != 0) {
			unlinkat(db->cachetmp_fd, cacheitem, 0);
			return r;
		}
	}
505

506 507
	if (renameat(db->cachetmp_fd, cacheitem, db->cache_fd, cacheitem) < 0)
		return -errno;
508

509
	return 0;
510 511
}

512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
static struct apk_db_dir_instance *find_diri(struct apk_installed_package *ipkg,
					     apk_blob_t dirname,
					     struct apk_db_dir_instance *curdiri,
					     struct hlist_node ***tail)
{
	struct hlist_node *n;
	struct apk_db_dir_instance *diri;

	if (curdiri != NULL &&
	    apk_blob_compare(APK_BLOB_PTR_LEN(curdiri->dir->name,
					      curdiri->dir->namelen),
			     dirname) == 0)
		return curdiri;

	hlist_for_each_entry(diri, n, &ipkg->owned_dirs, pkg_dirs_list) {
		if (apk_blob_compare(APK_BLOB_PTR_LEN(diri->dir->name,
						      diri->dir->namelen), dirname) == 0) {
			if (tail != NULL)
				*tail = hlist_tail_ptr(&diri->owned_files);
			return diri;
		}
	}
	return NULL;
}

Timo Teräs's avatar
Timo Teräs committed
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
int apk_db_read_overlay(struct apk_database *db, struct apk_bstream *bs)
{
	struct apk_db_dir_instance *diri = NULL;
	struct hlist_node **diri_node = NULL, **file_diri_node = NULL;
	struct apk_package *pkg;
	struct apk_installed_package *ipkg;
	struct apk_db_file *file;
	apk_blob_t token = APK_BLOB_STR("\n"), line, bdir, bfile;

	pkg = apk_pkg_new();
	if (pkg == NULL)
		return -1;

	ipkg = apk_pkg_install(db, pkg);
	if (ipkg == NULL)
		return -1;

	diri_node = hlist_tail_ptr(&ipkg->owned_dirs);

	while (!APK_BLOB_IS_NULL(line = bs->read(bs, token))) {
		if (!apk_blob_rsplit(line, '/', &bdir, &bfile))
			break;

		if (bfile.len == 0) {
			diri = apk_db_diri_new(db, pkg, bdir, &diri_node);
			file_diri_node = &diri->owned_files.first;
		} else {
564 565
			diri = find_diri(ipkg, bdir, diri, &file_diri_node);
			if (diri == NULL) {
566 567
				diri = apk_db_diri_new(db, pkg, bdir, &diri_node);
				file_diri_node = &diri->owned_files.first;
568
			}
Timo Teräs's avatar
Timo Teräs committed
569 570 571 572 573 574 575
			file = apk_db_file_get(db, diri, bfile, &file_diri_node);
		}
	}

	return 0;
}

576
int apk_db_index_read(struct apk_database *db, struct apk_bstream *bs, int repo)
577 578
{
	struct apk_package *pkg = NULL;
579
	struct apk_installed_package *ipkg = NULL;
580
	struct apk_db_dir_instance *diri = NULL;
581
	struct apk_db_file *file = NULL;
582 583
	struct hlist_node **diri_node = NULL;
	struct hlist_node **file_diri_node = NULL;
584
	apk_blob_t token = APK_BLOB_STR("\n"), l;
585
	int field, r;
586

587 588 589 590
	while (!APK_BLOB_IS_NULL(l = bs->read(bs, token))) {
		if (l.len < 2 || l.ptr[1] != ':') {
			if (pkg == NULL)
				continue;
591

592
			if (repo >= 0)
593
				pkg->repos |= BIT(repo);
594

595
			if (apk_db_pkg_add(db, pkg) == NULL) {
596 597
				apk_error("Installed database load failed");
				return -1;
598
			}
599
			pkg = NULL;
600
			ipkg = NULL;
601 602
			continue;
		}
603

604 605 606 607 608 609 610 611
		/* Get field */
		field = l.ptr[0];
		l.ptr += 2;
		l.len -= 2;

		/* If no package, create new */
		if (pkg == NULL) {
			pkg = apk_pkg_new();
612
			ipkg = NULL;
613 614 615
			diri = NULL;
			file_diri_node = NULL;
		}
616

617
		/* Standard index line? */
618 619 620 621 622 623 624 625 626
		r = apk_pkg_add_info(db, pkg, field, l);
		if (r == 0) {
			if (repo == -1 && field == 'S') {
				/* Instert to installed database; this needs to
				 * happen after package name has been read, but
				 * before first FDB entry. */
				ipkg = apk_pkg_install(db, pkg);
				diri_node = hlist_tail_ptr(&ipkg->owned_dirs);
			}
627 628
			continue;
		}
629 630
		if (repo != -1 || ipkg == NULL)
			continue;
631

632 633 634 635 636
		/* Check FDB special entries */
		switch (field) {
		case 'F':
			if (pkg->name == NULL) {
				apk_error("FDB directory entry before package entry");
637 638
				return -1;
			}
639 640 641 642 643 644 645 646
			diri = apk_db_diri_new(db, pkg, l, &diri_node);
			file_diri_node = &diri->owned_files.first;
			break;
		case 'M':
			if (diri == NULL) {
				apk_error("FDB directory metadata entry before directory entry");
				return -1;
			}
647 648 649 650 651
			diri->uid = apk_blob_pull_uint(&l, 10);
			apk_blob_pull_char(&l, ':');
			diri->gid = apk_blob_pull_uint(&l, 10);
			apk_blob_pull_char(&l, ':');
			diri->mode = apk_blob_pull_uint(&l, 8);
652 653 654 655 656 657
			break;
		case 'R':
			if (diri == NULL) {
				apk_error("FDB file entry before directory entry");
				return -1;
			}
Timo Teräs's avatar
Timo Teräs committed
658
			file = apk_db_file_get(db, diri, l, &file_diri_node);
659 660 661 662 663 664
			break;
		case 'Z':
			if (file == NULL) {
				apk_error("FDB checksum entry before file entry");
				return -1;
			}
Timo Teräs's avatar
Timo Teräs committed
665
			apk_blob_pull_csum(&l, &file->csum);
666 667
			break;
		default:
668 669 670 671 672 673 674 675
			if (r != 0 && !(apk_flags & APK_FORCE)) {
				/* Installed db should not have unsupported fields */
				apk_error("This apk-tools is too old to handle installed packages");
				return -1;
			}
			/* Installed. So mark the package as installable. */
			pkg->filename = NULL;
			continue;
676
		}
677 678 679 680
		if (APK_BLOB_IS_NULL(l)) {
			apk_error("FDB format error in entry '%c'", field);
			return -1;
		}
681 682
	}

683
	return 0;
684 685
}

Timo Teräs's avatar
Timo Teräs committed
686
static int apk_db_write_fdb(struct apk_database *db, struct apk_ostream *os)
687
{
688
	struct apk_installed_package *ipkg;
689
	struct apk_package *pkg;
690
	struct apk_db_dir_instance *diri;
691
	struct apk_db_file *file;
692
	struct hlist_node *c1, *c2;
693
	char buf[1024];
694 695
	apk_blob_t bbuf = APK_BLOB_BUF(buf);
	int r;
696

697 698
	list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
		pkg = ipkg->pkg;
699 700 701
		r = apk_pkg_write_index_entry(pkg, os);
		if (r < 0)
			return r;
702

703
		hlist_for_each_entry(diri, c1, &ipkg->owned_dirs, pkg_dirs_list) {
704 705 706 707 708 709 710 711 712
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("F:"));
			apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN(diri->dir->name, diri->dir->namelen));
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nM:"));
			apk_blob_push_uint(&bbuf, diri->uid, 10);
			apk_blob_push_blob(&bbuf, APK_BLOB_STR(":"));
			apk_blob_push_uint(&bbuf, diri->gid, 10);
			apk_blob_push_blob(&bbuf, APK_BLOB_STR(":"));
			apk_blob_push_uint(&bbuf, diri->mode, 8);
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
713

714
			hlist_for_each_entry(file, c2, &diri->owned_files, diri_files_list) {
715 716
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("R:"));
				apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN(file->name, file->namelen));
Timo Teräs's avatar
Timo Teräs committed
717
				if (file->csum.type != APK_CHECKSUM_NONE) {
718
					apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nZ:"));
Timo Teräs's avatar
Timo Teräs committed
719
					apk_blob_push_csum(&bbuf, &file->csum);
720
				}
721
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
722

723
				if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
724
					return -1;
725
				bbuf = APK_BLOB_BUF(buf);
726
			}
727
			if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
728
				return -1;
729
			bbuf = APK_BLOB_BUF(buf);
730
		}
Timo Teräs's avatar
Timo Teräs committed
731
		os->write(os, "\n", 1);
732 733 734 735 736
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
737
static int apk_db_scriptdb_write(struct apk_database *db, struct apk_ostream *os)
738
{
739
	struct apk_installed_package *ipkg;
740
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
741 742 743
	struct apk_file_info fi;
	char filename[256];
	apk_blob_t bfn;
744
	int r, i;
745
	time_t now = time(NULL);
746 747 748 749 750 751 752

	list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
		pkg = ipkg->pkg;

		for (i = 0; i < APK_SCRIPT_MAX; i++) {
			if (ipkg->script[i].ptr == NULL)
				continue;
753

Timo Teräs's avatar
Timo Teräs committed
754 755
			fi = (struct apk_file_info) {
				.name = filename,
756
				.size = ipkg->script[i].len,
Timo Teräs's avatar
Timo Teräs committed
757
				.mode = 0755 | S_IFREG,
758
				.mtime = now,
Timo Teräs's avatar
Timo Teräs committed
759 760 761 762 763 764
			};
			/* The scripts db expects file names in format:
			 * pkg-version.<hexdump of package checksum>.action */
			bfn = APK_BLOB_BUF(filename);
			apk_blob_push_blob(&bfn, APK_BLOB_STR(pkg->name->name));
			apk_blob_push_blob(&bfn, APK_BLOB_STR("-"));
765
			apk_blob_push_blob(&bfn, *pkg->version);
Timo Teräs's avatar
Timo Teräs committed
766 767 768
			apk_blob_push_blob(&bfn, APK_BLOB_STR("."));
			apk_blob_push_csum(&bfn, &pkg->csum);
			apk_blob_push_blob(&bfn, APK_BLOB_STR("."));
769
			apk_blob_push_blob(&bfn, APK_BLOB_STR(apk_script_types[i]));
Timo Teräs's avatar
Timo Teräs committed
770 771
			apk_blob_push_blob(&bfn, APK_BLOB_PTR_LEN("", 1));

772
			r = apk_tar_write_entry(os, &fi, ipkg->script[i].ptr);
Timo Teräs's avatar
Timo Teräs committed
773 774
			if (r < 0)
				return r;
775 776 777
		}
	}

778
	return apk_tar_write_entry(os, NULL, NULL);
779 780
}

Timo Teräs's avatar
Timo Teräs committed
781
static int apk_db_scriptdb_read_v1(struct apk_database *db, struct apk_istream *is)
782 783
{
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
784 785 786 787 788 789
	struct {
		unsigned char md5sum[16];
		unsigned int type;
		unsigned int size;
	} hdr;
	struct apk_checksum csum;
790

791
	while (is->read(is, &hdr, sizeof(hdr)) == sizeof(hdr)) {
Timo Teräs's avatar
Timo Teräs committed
792 793 794 795
		memcpy(csum.data, hdr.md5sum, sizeof(hdr.md5sum));
		csum.type = APK_CHECKSUM_MD5;

		pkg = apk_db_get_pkg(db, &csum);
796 797
		if (pkg != NULL && pkg->ipkg != NULL)
			apk_ipkg_add_script(pkg->ipkg, is, hdr.type, hdr.size);
Timo Teräs's avatar
Timo Teräs committed
798 799
		else
			apk_istream_skip(is, hdr.size);
800 801 802 803 804
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838
static int apk_read_script_archive_entry(void *ctx,
					 const struct apk_file_info *ae,
					 struct apk_istream *is)
{
	struct apk_database *db = (struct apk_database *) ctx;
	struct apk_package *pkg;
	char *fncsum, *fnaction;
	struct apk_checksum csum;
	apk_blob_t blob;
	int type;

	if (!S_ISREG(ae->mode))
		return 0;

	/* The scripts db expects file names in format:
	 * pkgname-version.<hexdump of package checksum>.action */
	fnaction = memrchr(ae->name, '.', strlen(ae->name));
	if (fnaction == NULL || fnaction == ae->name)
		return 0;
	fncsum = memrchr(ae->name, '.', fnaction - ae->name - 1);
	if (fncsum == NULL)
		return 0;
	fnaction++;
	fncsum++;

	/* Parse it */
	type = apk_script_type(fnaction);
	if (type == APK_SCRIPT_INVALID)
		return 0;
	blob = APK_BLOB_PTR_PTR(fncsum, fnaction - 2);
	apk_blob_pull_csum(&blob, &csum);

	/* Attach script */
	pkg = apk_db_get_pkg(db, &csum);
839 840
	if (pkg != NULL && pkg->ipkg != NULL)
		apk_ipkg_add_script(pkg->ipkg, is, type, ae->size);
Timo Teräs's avatar
Timo Teräs committed
841 842 843 844

	return 0;
}

845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
static int parse_triggers(void *ctx, apk_blob_t blob)
{
	struct apk_installed_package *ipkg = ctx;

	if (blob.len == 0)
		return 0;

	*apk_string_array_add(&ipkg->triggers) = apk_blob_cstr(blob);
	return 0;
}

static void apk_db_triggers_write(struct apk_database *db, struct apk_ostream *os)
{
	struct apk_installed_package *ipkg;
	char buf[APK_BLOB_CHECKSUM_BUF];
	apk_blob_t bfn;
	int i;

	list_for_each_entry(ipkg, &db->installed.triggers, trigger_pkgs_list) {
		bfn = APK_BLOB_BUF(buf);
		apk_blob_push_csum(&bfn, &ipkg->pkg->csum);
866 867
		bfn = apk_blob_pushed(APK_BLOB_BUF(buf), bfn);
		os->write(os, bfn.ptr, bfn.len);
868

869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
		for (i = 0; i < ipkg->triggers->num; i++) {
			os->write(os, " ", 1);
			apk_ostream_write_string(os, ipkg->triggers->item[i]);
		}
		os->write(os, "\n", 1);
	}
}

static void apk_db_triggers_read(struct apk_database *db, struct apk_bstream *bs)
{
	struct apk_checksum csum;
	struct apk_package *pkg;
	struct apk_installed_package *ipkg;
	apk_blob_t l;

	while (!APK_BLOB_IS_NULL(l = bs->read(bs, APK_BLOB_STR("\n")))) {
		apk_blob_pull_csum(&l, &csum);
		apk_blob_pull_char(&l, ' ');

		pkg = apk_db_get_pkg(db, &csum);
		if (pkg == NULL || pkg->ipkg == NULL)
			continue;

		ipkg = pkg->ipkg;
		apk_blob_for_each_segment(l, " ", parse_triggers, ipkg);
Timo Teräs's avatar
Timo Teräs committed
894 895
		if (ipkg->triggers->num != 0 &&
		    !list_hashed(&ipkg->trigger_pkgs_list))
896 897 898 899 900
			list_add_tail(&ipkg->trigger_pkgs_list,
				      &db->installed.triggers);
	}
}

Timo Teräs's avatar
Timo Teräs committed
901
static int apk_db_read_state(struct apk_database *db, int flags)
902
{
903
	struct apk_istream *is;
904
	struct apk_bstream *bs;
Timo Teräs's avatar
Timo Teräs committed
905
	apk_blob_t blob;
906
	int i, r;
907 908 909 910 911 912 913 914 915

	/* Read:
	 * 1. installed repository
	 * 2. source repositories
	 * 3. master dependencies
	 * 4. package statuses
	 * 5. files db
	 * 6. script db
	 */
Timo Teräs's avatar
Timo Teräs committed
916
	if (!(flags & APK_OPENF_NO_WORLD)) {
917
		blob = apk_blob_from_file(db->root_fd, "var/lib/apk/world");
Timo Teräs's avatar
Timo Teräs committed
918 919 920 921
		if (APK_BLOB_IS_NULL(blob))
			return -ENOENT;
		apk_deps_parse(db, &db->world, blob);
		free(blob.ptr);
922

Timo Teräs's avatar
Timo Teräs committed
923
		for (i = 0; i < db->world->num; i++)
Timo Teräs's avatar
Timo Teräs committed
924 925
			db->world->item[i].name->flags |= APK_NAME_TOPLEVEL;
	}
926

Timo Teräs's avatar
Timo Teräs committed
927
	if (!(flags & APK_OPENF_NO_INSTALLED)) {
928
		bs = apk_bstream_from_file(db->root_fd, "var/lib/apk/installed");
929
		if (bs != NULL) {
930
			r = apk_db_index_read(db, bs, -1);
931
			bs->close(bs, NULL);
932 933
			if (r != 0)
				return -1;
Timo Teräs's avatar
Timo Teräs committed
934
		}
935

936 937 938 939 940
		bs = apk_bstream_from_file(db->root_fd, "var/lib/apk/triggers");
		if (bs != NULL) {
			apk_db_triggers_read(db, bs);
			bs->close(bs, NULL);
		}
941 942
	}

Timo Teräs's avatar
Timo Teräs committed
943
	if (!(flags & APK_OPENF_NO_SCRIPTS)) {
944
		is = apk_istream_from_file(db->root_fd, "var/lib/apk/scripts.tar");
Timo Teräs's avatar
Timo Teräs committed
945
		if (is != NULL) {
946
			apk_tar_parse(is, apk_read_script_archive_entry, db,
947
				      FALSE, &db->id_cache);
Timo Teräs's avatar
Timo Teräs committed
948
		} else {
949
			is = apk_istream_from_file(db->root_fd, "var/lib/apk/scripts");
Timo Teräs's avatar
Timo Teräs committed
950 951
			if (is != NULL)
				apk_db_scriptdb_read_v1(db, is);
Timo Teräs's avatar
Timo Teräs committed
952
		}
Timo Teräs's avatar
Timo Teräs committed
953 954
		if (is != NULL)
			is->close(is);
955 956 957 958 959
	}

	return 0;
}

960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
struct index_write_ctx {
	struct apk_ostream *os;
	int count;
	int force;
};

static int write_index_entry(apk_hash_item item, void *ctx)
{
	struct index_write_ctx *iwctx = (struct index_write_ctx *) ctx;
	struct apk_package *pkg = (struct apk_package *) item;
	int r;

	if (!iwctx->force && pkg->filename == NULL)
		return 0;

	r = apk_pkg_write_index_entry(pkg, iwctx->os);
	if (r < 0)
		return r;

	if (iwctx->os->write(iwctx->os, "\n", 1) != 1)
		return -1;

	iwctx->count++;
	return 0;
}

static int apk_db_index_write_nr_cache(struct apk_database *db)
{
	struct index_write_ctx ctx = { NULL, 0, TRUE };
989
	struct apk_installed_package *ipkg;
990 991 992 993 994 995 996 997
	struct apk_ostream *os;
	int r;

	if (!apk_db_cache_active(db))
		return 0;

	/* Write list of installed non-repository packages to
	 * cached index file */
998 999 1000 1001
	os = apk_ostream_to_file(db->cache_fd,
				 "installed",
				 "installed.new",
				 0644);
1002 1003 1004
	if (os == NULL)
		return -1;

1005
	ctx.os = os;
1006 1007
	list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
		if (ipkg->pkg->repos != 0)
1008
			continue;
1009
		r = write_index_entry(ipkg->pkg, &ctx);
1010 1011 1012
		if (r != 0)
			return r;
	}
1013 1014 1015
	r = os->close(os);
	if (r < 0)
		return r;
1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028

	return ctx.count;
}

int apk_db_index_write(struct apk_database *db, struct apk_ostream *os)
{
	struct index_write_ctx ctx = { os, 0, FALSE };

	apk_hash_foreach(&db->available.packages, write_index_entry, &ctx);

	return ctx.count;
}

1029 1030 1031 1032 1033 1034 1035 1036
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;
}

1037 1038 1039 1040
static int apk_db_create(struct apk_database *db)
{
	int fd;

1041 1042 1043 1044 1045 1046
	mkdirat(db->root_fd, "tmp", 01777);
	mkdirat(db->root_fd, "dev", 0755);
	mknodat(db->root_fd, "dev/null", 0666, makedev(1, 3));
	mkdirat(db->root_fd, "var", 0755);
	mkdirat(db->root_fd, "var/lib", 0755);
	mkdirat(db->root_fd, "var/lib/apk", 0755);
1047 1048
	mkdirat(db->root_fd, "var/cache", 0755);
	mkdirat(db->root_fd, "var/cache/misc", 0755);
1049

1050
	fd = openat(db->root_fd, "var/lib/apk/world", O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC, 0644);
1051 1052 1053 1054 1055 1056 1057
	if (fd < 0)
		return -errno;
	close(fd);

	return 0;
}

1058 1059 1060 1061
static void handle_alarm(int sig)
{
}

1062
int apk_db_open(struct apk_database *db, struct apk_db_options *dbopts)
1063
{
1064 1065
	const char *msg = NULL;
	struct apk_repository_list *repo = NULL;
Timo Teräs's avatar
Timo Teräs committed
1066
	struct apk_bstream *bs;
1067
	struct stat64 st;
Timo Teräs's avatar
Timo Teräs committed
1068
	struct statfs stfs;
Timo Teräs's avatar
Timo Teräs committed
1069
	apk_blob_t blob;
1070
	int r, rr = 0;
1071

1072
	memset(db, 0, sizeof(*db));
1073 1074 1075 1076
	if (apk_flags & APK_SIMULATE) {
		dbopts->open_flags &= ~(APK_OPENF_CREATE | APK_OPENF_WRITE);
		dbopts->open_flags |= APK_OPENF_READ;
	}
1077 1078 1079 1080 1081 1082
	if (dbopts->open_flags == 0) {
		msg = "Invalid open flags (internal error)";
		r = -1;
		goto ret_r;
	}

1083 1084
	apk_hash_init(&db->available.names, &pkg_name_hash_ops, 1000);
	apk_hash_init(&db->available.packages, &pkg_info_hash_ops, 4000);
Timo Teräs's avatar
Timo Teräs committed
1085 1086
	apk_hash_init(&db->installed.dirs, &dir_hash_ops, 2000);
	apk_hash_init(&db->installed.files, &file_hash_ops, 10000);
1087
	list_init(&db->installed.packages);
1088
	list_init(&db->installed.triggers);
Timo Teräs's avatar
Timo Teräs committed
1089 1090
	apk_dependency_array_init(&db->world);
	apk_string_array_init(&db->protected_paths);
Timo Teräs's avatar
Timo Teräs committed
1091
	db->cache_dir = apk_static_cache_dir;
1092
	db->permanent = 1;
1093

1094
	db->root = strdup(dbopts->root ?: "/");
1095
	db->root_fd = openat(AT_FDCWD, db->root, O_RDONLY | O_CLOEXEC);
1096
	if (db->root_fd < 0 && (dbopts->open_flags & APK_OPENF_CREATE)) {
1097
		mkdirat(AT_FDCWD, db->root, 0755);
1098
		db->root_fd = openat(AT_FDCWD, db->root, O_RDONLY | O_CLOEXEC);
1099 1100 1101 1102 1103
	}
	if (db->root_fd < 0) {
		msg = "Unable to open root";
		goto ret_errno;
	}
Timo Teräs's avatar
Timo Teräs committed
1104 1105
	if (fstatfs(db->root_fd, &stfs) == 0 &&
	    stfs.f_type == 0x01021994 /* TMPFS_MAGIC */)
1106 1107 1108
		db->permanent = 0;

	if (fstatat64(db->root_fd, apk_linked_cache_dir, &st, 0) == 0 &&
1109
	    S_ISDIR(st.st_mode) && major(st.st_dev) != 0)
1110 1111
		db->cache_dir = apk_linked_cache_dir;

Timo Teräs's avatar
Timo Teräs committed
1112 1113
	apk_id_cache_init(&db->id_cache, db->root_fd);

1114
	if (dbopts->open_flags & APK_OPENF_WRITE) {
1115
		db->lock_fd = openat(db->root_fd, "var/lib/apk/lock",
1116
				     O_CREAT | O_RDWR | O_CLOEXEC, 0400);
1117
		if (db->lock_fd < 0 && errno == ENOENT &&
1118
		    (dbopts->open_flags & APK_OPENF_CREATE)) {
1119 1120 1121 1122 1123
			r = apk_db_create(db);
			if (r != 0) {
				msg = "Unable to create database";
				goto ret_r;
			}
1124
			db->lock_fd = openat(db->root_fd, "var/lib/apk/lock",
1125
					     O_CREAT | O_RDWR | O_CLOEXEC, 0400);
1126 1127 1128 1129
		}
		if (db->lock_fd < 0 ||
		    flock(db->lock_fd, LOCK_EX | LOCK_NB) < 0) {
			msg = "Unable to lock database";
1130
			if (dbopts->lock_wait) {
1131 1132 1133 1134 1135 1136 1137 1138
				struct sigaction sa, old_sa;

				apk_message("Waiting for repository lock");
				memset(&sa, 0, sizeof sa);
				sa.sa_handler = handle_alarm;
				sa.sa_flags   = SA_ONESHOT;
				sigaction(SIGALRM, &sa, &old_sa);

1139
				alarm(dbopts->lock_wait);