database.c 54.9 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>
22
#include <sys/file.h>
Natanael Copa's avatar
Natanael Copa committed
23
#include <sys/stat.h>
24 25 26 27 28

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

33 34 35 36 37 38 39 40
#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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

133 134 135 136 137 138
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)
139
{
140 141
	struct apk_db_file_hash_key *key = (struct apk_db_file_hash_key *) _key.ptr;

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

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
149 150
	return apk_blob_hash_seed(APK_BLOB_PTR_LEN(dbf->name, dbf->namelen),
				  dbf->diri->dir->hash);
151 152 153 154 155 156
}

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
157
	struct apk_db_dir *dir = dbf->diri->dir;
158 159
	int r;

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

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

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

Timo Teräs's avatar
Timo Teräs committed
178 179 180 181 182
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);
}

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

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

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

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

	return pn;
}

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

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

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

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

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

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

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

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

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

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

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

	for (i = 0; i < db->protected_paths->num; i++) {
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
		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;
289 290
	}

291 292 293
	return dir;
}

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

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

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

320
static void apk_db_diri_mkdir(struct apk_database *db, struct apk_db_dir_instance *diri)
321
{
322 323 324 325
	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)
			;
	}
326 327
}

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

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
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
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
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;
}

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

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

Timo Teräs's avatar
Timo Teräs committed
388 389 390
	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);
391 392 393
	if (file != NULL)
		return file;

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

	return file;
}

Timo Teräs's avatar
Timo Teräs committed
401 402 403
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
404
	struct apk_name *rname;
Timo Teräs's avatar
Timo Teräs committed
405 406

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

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

429 430 431
	if (pkg->license == NULL)
	        pkg->license = apk_blob_atomize(APK_BLOB_NULL);
        if (pkg->arch == NULL)
432
                pkg->arch = apk_blob_atomize(APK_BLOB_STR(APK_DEFAULT_ARCH));
433

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

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

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

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

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

505
	return 0;
506 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
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
533 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
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 {
560 561
			diri = find_diri(ipkg, bdir, diri, &file_diri_node);
			if (diri == NULL) {
562 563
				diri = apk_db_diri_new(db, pkg, bdir, &diri_node);
				file_diri_node = &diri->owned_files.first;
564
			}
Timo Teräs's avatar
Timo Teräs committed
565 566 567 568 569 570 571
			file = apk_db_file_get(db, diri, bfile, &file_diri_node);
		}
	}

	return 0;
}

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

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

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

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

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

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

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

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

679
	return 0;
680 681
}

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

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

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

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

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

	return 0;
}

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

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

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

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

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

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

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

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

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
801 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
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);
835 836
	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
837 838 839 840

	return 0;
}

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

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
		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
890 891
		if (ipkg->triggers->num != 0 &&
		    !list_hashed(&ipkg->trigger_pkgs_list))
892 893 894 895 896
			list_add_tail(&ipkg->trigger_pkgs_list,
				      &db->installed.triggers);
	}
}

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

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

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

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

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

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

	return 0;
}

956 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
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 };
985
	struct apk_installed_package *ipkg;