database.c 55 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 431 432
	if (pkg->license == NULL)
	        pkg->license = apk_blob_atomize(APK_BLOB_NULL);
        if (pkg->arch == NULL)
433
                pkg->arch = apk_blob_atomize(APK_BLOB_STR(APK_DEFAULT_ARCH));
434

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

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

int apk_cache_download(struct apk_database *db, const char *url,
		       const char *item, const char *cacheitem, int verify)
{
	char fullurl[PATH_MAX];
	int r;

473
	snprintf(fullurl, sizeof(fullurl), "%s%s" BLOB_FMT "/%s",
474
		 url, url[strlen(url)-1] == '/' ? "" : "/",
475
		 BLOB_PRINTF(*db->arch), item);
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
	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);

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

503 504
	if (renameat(db->cachetmp_fd, cacheitem, db->cache_fd, cacheitem) < 0)
		return -errno;
505

506
	return 0;
507 508
}

509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
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
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
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 {
561 562
			diri = find_diri(ipkg, bdir, diri, &file_diri_node);
			if (diri == NULL) {
563 564
				diri = apk_db_diri_new(db, pkg, bdir, &diri_node);
				file_diri_node = &diri->owned_files.first;
565
			}
Timo Teräs's avatar
Timo Teräs committed
566 567 568 569 570 571 572
			file = apk_db_file_get(db, diri, bfile, &file_diri_node);
		}
	}

	return 0;
}

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

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

589
			if (repo >= 0)
590
				pkg->repos |= BIT(repo);
591

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

601 602 603 604 605 606 607 608
		/* Get field */
		field = l.ptr[0];
		l.ptr += 2;
		l.len -= 2;

		/* If no package, create new */
		if (pkg == NULL) {
			pkg = apk_pkg_new();
609
			ipkg = NULL;
610 611 612
			diri = NULL;
			file_diri_node = NULL;
		}
613

614
		/* Standard index line? */
615 616 617 618 619 620 621 622 623
		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);
			}
624 625
			continue;
		}
626 627
		if (repo != -1 || ipkg == NULL)
			continue;
628

629 630 631 632 633
		/* Check FDB special entries */
		switch (field) {
		case 'F':
			if (pkg->name == NULL) {
				apk_error("FDB directory entry before package entry");
634 635
				return -1;
			}
636 637 638 639 640 641 642 643
			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;
			}
644 645 646 647 648
			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);
649 650 651 652 653 654
			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
655
			file = apk_db_file_get(db, diri, l, &file_diri_node);
656 657 658 659 660 661
			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
662
			apk_blob_pull_csum(&l, &file->csum);
663 664
			break;
		default:
665 666 667 668 669 670 671 672
			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;
673
		}
674 675 676 677
		if (APK_BLOB_IS_NULL(l)) {
			apk_error("FDB format error in entry '%c'", field);
			return -1;
		}
678 679
	}

680
	return 0;
681 682
}

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

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

700
		hlist_for_each_entry(diri, c1, &ipkg->owned_dirs, pkg_dirs_list) {
701 702 703 704 705 706 707 708 709
			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"));
710

711
			hlist_for_each_entry(file, c2, &diri->owned_files, diri_files_list) {
712 713
				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
714
				if (file->csum.type != APK_CHECKSUM_NONE) {
715
					apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nZ:"));
Timo Teräs's avatar
Timo Teräs committed
716
					apk_blob_push_csum(&bbuf, &file->csum);
717
				}
718
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
719

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

	return 0;
}

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

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

Timo Teräs's avatar
Timo Teräs committed
751 752
			fi = (struct apk_file_info) {
				.name = filename,
753
				.size = ipkg->script[i].len,
Timo Teräs's avatar
Timo Teräs committed
754
				.mode = 0755 | S_IFREG,
755
				.mtime = now,
Timo Teräs's avatar
Timo Teräs committed
756 757 758 759 760 761
			};
			/* 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("-"));
762
			apk_blob_push_blob(&bfn, *pkg->version);
Timo Teräs's avatar
Timo Teräs committed
763 764 765
			apk_blob_push_blob(&bfn, APK_BLOB_STR("."));
			apk_blob_push_csum(&bfn, &pkg->csum);
			apk_blob_push_blob(&bfn, APK_BLOB_STR("."));
766
			apk_blob_push_blob(&bfn, APK_BLOB_STR(apk_script_types[i]));
Timo Teräs's avatar
Timo Teräs committed
767 768
			apk_blob_push_blob(&bfn, APK_BLOB_PTR_LEN("", 1));

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

775
	return apk_tar_write_entry(os, NULL, NULL);
776 777
}

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

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

		pkg = apk_db_get_pkg(db, &csum);
793 794
		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
795 796
		else
			apk_istream_skip(is, hdr.size);
797 798 799 800 801
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
802 803 804 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
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);
836 837
	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
838 839 840 841

	return 0;
}

842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
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);
863 864
		bfn = apk_blob_pushed(APK_BLOB_BUF(buf), bfn);
		os->write(os, bfn.ptr, bfn.len);
865

866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
		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
891 892
		if (ipkg->triggers->num != 0 &&
		    !list_hashed(&ipkg->trigger_pkgs_list))
893 894 895 896 897
			list_add_tail(&ipkg->trigger_pkgs_list,
				      &db->installed.triggers);
	}
}

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

	/* 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
913
	if (!(flags & APK_OPENF_NO_WORLD)) {
914
		blob = apk_blob_from_file(db->root_fd, "var/lib/apk/world");
Timo Teräs's avatar
Timo Teräs committed
915 916 917 918
		if (APK_BLOB_IS_NULL(blob))
			return -ENOENT;
		apk_deps_parse(db, &db->world, blob);
		free(blob.ptr);
919

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

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

933 934 935 936 937
		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);
		}
938 939
	}

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

	return 0;
}

957 958 959 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
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 };
986
	struct apk_installed_package *ipkg;
987 988 989 990 991 992 993 994
	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 */
995 996 997 998
	os = apk_ostream_to_file(db->cache_fd,
				 "installed",
				 "installed.new",
				 0644);
999 1000 1001
	if (os == NULL)
		return -1;

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

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

1026 1027 1028 1029 1030 1031 1032 1033
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;
}

1034 1035 1036 1037
static int apk_db_create(struct apk_database *db)
{
	int fd;

1038 1039 1040 1041 1042 1043
	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);
1044 1045
	mkdirat(db->root_fd, "var/cache", 0755);
	mkdirat(db->root_fd, "var/cache/misc", 0755);
1046

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

	return 0;
}

1055 1056 1057 1058
static void handle_alarm(int sig)
{
}

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

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

1080 1081
	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
1082 1083
	apk_hash_init(&db->installed.dirs, &dir_hash_ops, 2000);
	apk_hash_init(&db->installed.files, &file_hash_ops, 10000);
1084
	list_init(&db->installed.packages);
1085
	list_init(&db->installed.triggers);
Timo Teräs's avatar
Timo Teräs committed
1086 1087
	apk_dependency_array_init(&db->world);
	apk_string_array_init(&db->protected_paths);
Timo Teräs's avatar
Timo Teräs committed
1088
	db->cache_dir = apk_static_cache_dir;
1089
	db->permanent = 1;
1090

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

	if (fstatat64(db->root_fd, apk_linked_cache_dir, &st, 0) == 0 &&
1106
	    S_ISDIR(st.st_mode) && major(st.st_dev) != 0)
1107 1108
		db->cache_dir = apk_linked_cache_dir;

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

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

1136
				alarm(dbopts->lock_wait);