database.c 69.7 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-2011 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 <mntent.h>
16
#include <limits.h>
17 18 19
#include <unistd.h>
#include <malloc.h>
#include <string.h>
20
#include <stdlib.h>
21
#include <signal.h>
22
#include <fnmatch.h>
Timo Teräs's avatar
Timo Teräs committed
23
#include <sys/vfs.h>
24
#include <sys/file.h>
25
#include <sys/wait.h>
Natanael Copa's avatar
Natanael Copa committed
26
#include <sys/stat.h>
27
#include <sys/statvfs.h>
28 29 30 31

#include "apk_defines.h"
#include "apk_package.h"
#include "apk_database.h"
32
#include "apk_applet.h"
33
#include "apk_archive.h"
Natanael Copa's avatar
Natanael Copa committed
34 35
#include "apk_print.h"

Timo Teräs's avatar
Timo Teräs committed
36 37 38 39 40
static const apk_spn_match_def apk_spn_repo_separators = {
	[4] = (1<<0) /* */,
	[7] = (1<<2) /*:*/,
};

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

49 50
static const char * const apkindex_tar_gz = "APKINDEX.tar.gz";

Timo Teräs's avatar
Timo Teräs committed
51
static const char * const apk_static_cache_dir = "var/cache/apk";
Timo Teräs's avatar
Timo Teräs committed
52
static const char * const apk_linked_cache_dir = "etc/apk/cache";
Timo Teräs's avatar
Timo Teräs committed
53

Timo Teräs's avatar
Timo Teräs committed
54
static const char * const apk_lock_file = "var/lock/apkdb";
Timo Teräs's avatar
Timo Teräs committed
55

Timo Teräs's avatar
Timo Teräs committed
56 57 58
static const char * const apk_world_file = "etc/apk/world";
static const char * const apk_world_file_tmp = "etc/apk/world.new";
static const char * const apk_world_file_old = "var/lib/apk/world";
59
static const char * const apk_arch_file = "etc/apk/arch";
Timo Teräs's avatar
Timo Teräs committed
60 61 62 63 64 65 66 67 68 69 70 71 72

static const char * const apk_scripts_file = "lib/apk/db/scripts.tar";
static const char * const apk_scripts_file_tmp = "lib/apk/db/scripts.tar.new";
static const char * const apk_scripts_file_old = "var/lib/apk/scripts.tar";

static const char * const apk_triggers_file = "lib/apk/db/triggers";
static const char * const apk_triggers_file_tmp = "lib/apk/db/triggers.new";
static const char * const apk_triggers_file_old = "var/lib/apk/triggers";

static const char * const apk_installed_file = "lib/apk/db/installed";
static const char * const apk_installed_file_tmp = "lib/apk/db/installed.new";
static const char * const apk_installed_file_old = "var/lib/apk/installed";

73 74 75
struct install_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
76
	struct apk_installed_package *ipkg;
77 78

	int script;
79
	char **script_args;
80
	int script_pending : 1;
81

82
	struct apk_db_dir_instance *diri;
Timo Teräs's avatar
Timo Teräs committed
83
	struct apk_checksum data_csum;
84
	struct apk_sign_ctx sctx;
85

86 87 88 89 90
	apk_progress_cb cb;
	void *cb_ctx;
	size_t installed_size;
	size_t current_file_size;

91 92
	struct hlist_node **diri_node;
	struct hlist_node **file_diri_node;
93 94
};

95
static apk_blob_t pkg_name_get_key(apk_hash_item item)
96
{
97
	return APK_BLOB_STR(((struct apk_name *) item)->name);
98 99
}

100 101 102
static void pkg_name_free(struct apk_name *name)
{
	free(name->name);
103
	apk_provider_array_free(&name->providers);
Timo Teräs's avatar
Timo Teräs committed
104
	apk_name_array_free(&name->rdepends);
Timo Teräs's avatar
Timo Teräs committed
105
	apk_name_array_free(&name->rinstall_if);
106 107 108
	free(name);
}

109 110 111
static const struct apk_hash_ops pkg_name_hash_ops = {
	.node_offset = offsetof(struct apk_name, hash_node),
	.get_key = pkg_name_get_key,
112 113
	.hash_key = apk_blob_hash,
	.compare = apk_blob_compare,
114
	.delete_item = (apk_hash_delete_f) pkg_name_free,
115 116
};

117
static apk_blob_t pkg_info_get_key(apk_hash_item item)
118
{
Timo Teräs's avatar
Timo Teräs committed
119
	return APK_BLOB_CSUM(((struct apk_package *) item)->csum);
120 121
}

122
static unsigned long csum_hash(apk_blob_t csum)
123
{
124 125
	/* Checksum's highest bits have the most "randomness", use that
	 * directly as hash */
126
	return *(unsigned long *) csum.ptr;
127 128 129 130 131
}

static const struct apk_hash_ops pkg_info_hash_ops = {
	.node_offset = offsetof(struct apk_package, hash_node),
	.get_key = pkg_info_get_key,
132 133
	.hash_key = csum_hash,
	.compare = apk_blob_compare,
134 135 136
	.delete_item = (apk_hash_delete_f) apk_pkg_free,
};

137
static apk_blob_t apk_db_dir_get_key(apk_hash_item item)
138
{
Timo Teräs's avatar
Timo Teräs committed
139 140
	struct apk_db_dir *dir = (struct apk_db_dir *) item;
	return APK_BLOB_PTR_LEN(dir->name, dir->namelen);
141 142 143 144 145
}

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,
146 147
	.hash_key = apk_blob_hash,
	.compare = apk_blob_compare,
148 149 150
	.delete_item = (apk_hash_delete_f) free,
};

151 152 153 154 155 156
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)
157
{
158 159
	struct apk_db_file_hash_key *key = (struct apk_db_file_hash_key *) _key.ptr;

Timo Teräs's avatar
Timo Teräs committed
160
	return apk_blob_hash_seed(key->filename, apk_blob_hash(key->dirname));
161 162 163 164 165 166
}

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
167 168
	return apk_blob_hash_seed(APK_BLOB_PTR_LEN(dbf->name, dbf->namelen),
				  dbf->diri->dir->hash);
169 170 171 172 173 174
}

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
175
	struct apk_db_dir *dir = dbf->diri->dir;
176 177
	int r;

Timo Teräs's avatar
Timo Teräs committed
178 179
	r = apk_blob_compare(key->filename,
			     APK_BLOB_PTR_LEN(dbf->name, dbf->namelen));
180 181 182
	if (r != 0)
		return r;

Timo Teräs's avatar
Timo Teräs committed
183 184 185
	r = apk_blob_compare(key->dirname,
			     APK_BLOB_PTR_LEN(dir->name, dir->namelen));
	return r;
186 187 188 189
}

static const struct apk_hash_ops file_hash_ops = {
	.node_offset = offsetof(struct apk_db_file, hash_node),
190 191 192
	.hash_key = apk_db_file_hash_key,
	.hash_item = apk_db_file_hash_item,
	.compare_item = apk_db_file_compare_item,
193 194 195
	.delete_item = (apk_hash_delete_f) free,
};

Timo Teräs's avatar
Timo Teräs committed
196 197 198 199 200
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);
}

201
struct apk_name *apk_db_get_name(struct apk_database *db, apk_blob_t name)
202 203
{
	struct apk_name *pn;
204
	unsigned long hash = apk_hash_from_key(&db->available.names, name);
205

206
	pn = (struct apk_name *) apk_hash_get_hashed(&db->available.names, name, hash);
207 208 209 210 211 212 213
	if (pn != NULL)
		return pn;

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

214
	pn->name = apk_blob_cstr(name);
215
	apk_provider_array_init(&pn->providers);
Timo Teräs's avatar
Timo Teräs committed
216
	apk_name_array_init(&pn->rdepends);
Timo Teräs's avatar
Timo Teräs committed
217
	apk_name_array_init(&pn->rinstall_if);
218
	apk_hash_insert_hashed(&db->available.names, pn, hash);
219 220 221 222

	return pn;
}

223 224
static void apk_db_dir_mkdir(struct apk_database *db, struct apk_db_dir *dir)
{
225 226 227 228 229 230 231
	if (apk_flags & APK_SIMULATE)
		return;

	/* Don't mess with root, as no package provides it directly */
	if (dir->namelen == 0)
		return;

232
	if ((dir->refs == 1) ||
233
	    (fchmodat(db->root_fd, dir->name, dir->mode, 0) != 0 &&
234 235 236
	     errno == ENOENT))
		if ((mkdirat(db->root_fd, dir->name, dir->mode) != 0 &&
		     errno == EEXIST))
237
			if (fchmodat(db->root_fd, dir->name, dir->mode, 0) != 0)
238 239 240 241 242 243
				;

	if (fchownat(db->root_fd, dir->name, dir->uid, dir->gid, 0) != 0)
		;
}

244
void apk_db_dir_unref(struct apk_database *db, struct apk_db_dir *dir, int allow_rmdir)
245 246
{
	dir->refs--;
247 248
	if (dir->refs > 0) {
		if (allow_rmdir) {
249
			dir->recalc_mode = 1;
250 251 252
			dir->mode = 0;
			dir->uid = (uid_t) -1;
			dir->gid = (gid_t) -1;
253
		}
254
		return;
255 256
	}

257
	db->installed.stats.dirs--;
258 259

	if (allow_rmdir) {
260 261
		/* The final instance of this directory was removed,
		 * so this directory gets deleted in reality too. */
262
		dir->recalc_mode = 0;
263 264 265 266
		dir->mode = 0;
		dir->uid = (uid_t) -1;
		dir->gid = (gid_t) -1;

267 268
		if (dir->namelen)
			unlinkat(db->root_fd, dir->name, AT_REMOVEDIR);
269
	} else if (dir->recalc_mode) {
270 271
		/* Directory permissions need a reset. */
		apk_db_dir_mkdir(db, dir);
272
	}
273 274

	if (dir->parent != NULL)
275
		apk_db_dir_unref(db, dir->parent, allow_rmdir);
276 277
}

278
struct apk_db_dir *apk_db_dir_ref(struct apk_db_dir *dir)
279 280 281 282 283
{
	dir->refs++;
	return dir;
}

284 285
struct apk_db_dir *apk_db_dir_query(struct apk_database *db,
				    apk_blob_t name)
286 287 288 289
{
	return (struct apk_db_dir *) apk_hash_get(&db->installed.dirs, name);
}

290
struct apk_db_dir *apk_db_dir_get(struct apk_database *db, apk_blob_t name)
291 292
{
	struct apk_db_dir *dir;
293
	struct apk_protected_path_array *ppaths;
294
	apk_blob_t bparent;
295
	unsigned long hash = apk_hash_from_key(&db->installed.dirs, name);
296
	char *relative_name;
297
	int i;
298

299
	if (name.len && name.ptr[name.len-1] == '/')
300 301
		name.len--;

302
	dir = (struct apk_db_dir *) apk_hash_get_hashed(&db->installed.dirs, name, hash);
303
	if (dir != NULL)
304
		return apk_db_dir_ref(dir);
305

306
	db->installed.stats.dirs++;
307 308
	dir = malloc(sizeof(*dir) + name.len + 1);
	memset(dir, 0, sizeof(*dir));
309
	dir->refs = 1;
310 311
	dir->uid = (uid_t) -1;
	dir->gid = (gid_t) -1;
312
	dir->rooted_name[0] = '/';
Timo Teräs's avatar
Timo Teräs committed
313 314 315 316
	memcpy(dir->name, name.ptr, name.len);
	dir->name[name.len] = 0;
	dir->namelen = name.len;
	dir->hash = hash;
317
	apk_protected_path_array_init(&dir->protected_paths);
318
	apk_hash_insert_hashed(&db->installed.dirs, dir, hash);
319

320
	if (name.len == 0) {
321
		dir->parent = NULL;
322 323 324
		dir->has_protected_children = 1;
		ppaths = NULL;
	} else if (apk_blob_rsplit(name, '/', &bparent, NULL)) {
325
		dir->parent = apk_db_dir_get(db, bparent);
326 327 328 329 330
		dir->protected = dir->parent->protected;
		dir->has_protected_children = dir->protected;
		dir->symlinks_only = dir->parent->symlinks_only;
		ppaths = dir->parent->protected_paths;
	} else {
331
		dir->parent = apk_db_dir_get(db, APK_BLOB_NULL);
332 333
		ppaths = db->protected_paths;
	}
334

335 336
	if (ppaths == NULL)
		return dir;
337

338 339 340 341
	relative_name = strrchr(dir->rooted_name, '/') + 1;
	for (i = 0; i < ppaths->num; i++) {
		struct apk_protected_path *ppath = &ppaths->item[i];
		char *slash;
342

343 344 345 346 347
		slash = strchr(ppath->relative_pattern, '/');
		if (slash != NULL) {
			*slash = 0;
			if (fnmatch(ppath->relative_pattern, relative_name, FNM_PATHNAME) != 0) {
				*slash = '/';
348 349
				continue;
			}
350 351 352 353 354 355 356 357 358 359
			*slash = '/';

			*apk_protected_path_array_add(&dir->protected_paths) = (struct apk_protected_path) {
				.relative_pattern = slash + 1,
				.protected = ppath->protected,
				.symlinks_only = ppath->symlinks_only,
			};
		} else {
			if (fnmatch(ppath->relative_pattern, relative_name, FNM_PATHNAME) != 0)
				continue;
360

361 362 363
			dir->protected = ppath->protected;
			dir->symlinks_only = ppath->symlinks_only;
		}
364
		dir->has_protected_children |= ppath->protected;
365 366
	}

367 368 369
	return dir;
}

370 371 372
static struct apk_db_dir_instance *apk_db_diri_new(struct apk_database *db,
						   struct apk_package *pkg,
						   apk_blob_t name,
373
						   struct hlist_node ***after)
374 375 376 377
{
	struct apk_db_dir_instance *diri;

	diri = calloc(1, sizeof(struct apk_db_dir_instance));
378
	if (diri != NULL) {
379 380
		hlist_add_after(&diri->pkg_dirs_list, *after);
		*after = &diri->pkg_dirs_list.next;
381
		diri->dir = apk_db_dir_get(db, name);
382 383
		diri->pkg = pkg;
	}
384 385 386 387

	return diri;
}

388 389 390 391
static void apk_db_dir_apply_diri_permissions(struct apk_db_dir_instance *diri)
{
	struct apk_db_dir *dir = diri->dir;

392 393
	if (diri->uid < dir->uid ||
	    (diri->uid == dir->uid && diri->gid < dir->gid)) {
394 395
		dir->uid = diri->uid;
		dir->gid = diri->gid;
396 397 398
		dir->mode = diri->mode;
	} else if (diri->uid == dir->uid && diri->gid == dir->gid) {
		dir->mode &= diri->mode;
399 400 401
	}
}

402 403 404
static void apk_db_diri_set(struct apk_db_dir_instance *diri, mode_t mode,
			    uid_t uid, gid_t gid)
{
405
	diri->mode = mode & 07777;
406 407
	diri->uid = uid;
	diri->gid = gid;
408
	apk_db_dir_apply_diri_permissions(diri);
409 410
}

411
static void apk_db_diri_free(struct apk_database *db,
412 413
			     struct apk_db_dir_instance *diri,
			     int allow_rmdir)
414
{
415 416
	if (allow_rmdir == APK_DISALLOW_RMDIR &&
	    diri->dir->recalc_mode)
417 418
		apk_db_dir_apply_diri_permissions(diri);

419
	apk_db_dir_unref(db, diri->dir, allow_rmdir);
420 421 422
	free(diri);
}

423 424 425 426 427 428
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;

429 430 431
	if (dir.len && dir.ptr[dir.len-1] == '/')
		dir.len--;

432 433 434 435 436 437 438 439 440
	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
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
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;
}

463 464 465 466 467 468 469
static void apk_db_file_set(struct apk_db_file *file, mode_t mode, uid_t uid, gid_t gid)
{
	file->mode = mode & 07777;
	file->uid = uid;
	file->gid = gid;
}

470
static struct apk_db_file *apk_db_file_get(struct apk_database *db,
471
					   struct apk_db_dir_instance *diri,
472 473
					   apk_blob_t name,
					   struct hlist_node ***after)
474 475
{
	struct apk_db_file *file;
476
	struct apk_db_file_hash_key key;
Timo Teräs's avatar
Timo Teräs committed
477 478
	struct apk_db_dir *dir = diri->dir;
	unsigned long hash;
479 480

	key = (struct apk_db_file_hash_key) {
Timo Teräs's avatar
Timo Teräs committed
481
		.dirname = APK_BLOB_PTR_LEN(dir->name, dir->namelen),
482 483
		.filename = name,
	};
484

Timo Teräs's avatar
Timo Teräs committed
485 486 487
	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);
488 489 490
	if (file != NULL)
		return file;

Timo Teräs's avatar
Timo Teräs committed
491
	file = apk_db_file_new(diri, name, after);
Timo Teräs's avatar
Timo Teräs committed
492
	apk_hash_insert_hashed(&db->installed.files, file, hash);
493
	db->installed.stats.files++;
494 495 496 497

	return file;
}

Timo Teräs's avatar
Timo Teräs committed
498 499
static void apk_db_pkg_rdepends(struct apk_database *db, struct apk_package *pkg)
{
500 501
	struct apk_name *rname, **rd;
	struct apk_dependency *d;
Timo Teräs's avatar
Timo Teräs committed
502

503 504 505 506
	foreach_array_item(d, pkg->depends) {
		rname = d->name;
		foreach_array_item(rd, rname->rdepends)
			if (*rd == pkg->name)
Timo Teräs's avatar
Timo Teräs committed
507
				goto rdeps_done;
Timo Teräs's avatar
Timo Teräs committed
508
		*apk_name_array_add(&rname->rdepends) = pkg->name;
509
rdeps_done: ;
Timo Teräs's avatar
Timo Teräs committed
510
	}
511 512 513 514
	foreach_array_item(d, pkg->install_if) {
		rname = d->name;
		foreach_array_item(rd, rname->rinstall_if)
			if (*rd == pkg->name)
Timo Teräs's avatar
Timo Teräs committed
515 516
				goto riif_done;
		*apk_name_array_add(&rname->rinstall_if) = pkg->name;
517
riif_done: ;
Timo Teräs's avatar
Timo Teräs committed
518 519
	}
	return;
Timo Teräs's avatar
Timo Teräs committed
520 521
}

522 523 524 525 526
static inline void add_provider(struct apk_name *name, struct apk_provider p)
{
	*apk_provider_array_add(&name->providers) = p;
}

527
struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package *pkg)
528 529
{
	struct apk_package *idb;
530
	int i;
531

532
	if (pkg->license == NULL)
533
		pkg->license = apk_blob_atomize(APK_BLOB_NULL);
534

535 536 537 538 539
	/* Set as "cached" if installing from specified file, and
	 * for virtual packages */
	if (pkg->filename != NULL || pkg->installed_size == 0)
		pkg->repos |= BIT(APK_REPOSITORY_CACHED);

Timo Teräs's avatar
Timo Teräs committed
540
	idb = apk_hash_get(&db->available.packages, APK_BLOB_CSUM(pkg->csum));
541 542 543
	if (idb == NULL) {
		idb = pkg;
		apk_hash_insert(&db->available.packages, pkg);
544 545 546 547 548
		add_provider(pkg->name, APK_PROVIDER_FROM_PACKAGE(pkg));
		for (i = 0; i < pkg->provides->num; i++) {
			struct apk_dependency *dep = &pkg->provides->item[i];
			add_provider(dep->name, APK_PROVIDER_FROM_PROVIDES(pkg, dep));
		}
Timo Teräs's avatar
Timo Teräs committed
549
		apk_db_pkg_rdepends(db, pkg);
550 551
	} else {
		idb->repos |= pkg->repos;
552 553 554 555
		if (idb->filename == NULL && pkg->filename != NULL) {
			idb->filename = pkg->filename;
			pkg->filename = NULL;
		}
556 557 558 559 560
		if (idb->ipkg == NULL && pkg->ipkg != NULL) {
			idb->ipkg = pkg->ipkg;
			idb->ipkg->pkg = idb;
			pkg->ipkg = NULL;
		}
561 562 563 564 565
		apk_pkg_free(pkg);
	}
	return idb;
}

566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
static int apk_pkg_format_cache_pkg(apk_blob_t to, struct apk_package *pkg)
{
	/* pkgname-1.0_alpha1.12345678.apk */
	apk_blob_push_blob(&to, APK_BLOB_STR(pkg->name->name));
	apk_blob_push_blob(&to, APK_BLOB_STR("-"));
	apk_blob_push_blob(&to, *pkg->version);
	apk_blob_push_blob(&to, APK_BLOB_STR("."));
	apk_blob_push_hexdump(&to, APK_BLOB_PTR_LEN((char *) pkg->csum.data,
						    APK_CACHE_CSUM_BYTES));
	apk_blob_push_blob(&to, APK_BLOB_STR(".apk"));
	apk_blob_push_blob(&to, APK_BLOB_PTR_LEN("", 1));
	if (APK_BLOB_IS_NULL(to))
		return -ENOBUFS;
	return 0;
}

int apk_repo_format_cache_index(apk_blob_t to, struct apk_repository *repo)
583
{
584
	/* APKINDEX.12345678.tar.gz */
585
	apk_blob_push_blob(&to, APK_BLOB_STR("APKINDEX."));
586
	apk_blob_push_hexdump(&to, APK_BLOB_PTR_LEN((char *) repo->csum.data, APK_CACHE_CSUM_BYTES));
587
	apk_blob_push_blob(&to, APK_BLOB_STR(".tar.gz"));
588
	apk_blob_push_blob(&to, APK_BLOB_PTR_LEN("", 1));
589 590 591
	if (APK_BLOB_IS_NULL(to))
		return -ENOBUFS;
	return 0;
592 593
}

594 595
int apk_repo_format_real_url(struct apk_database *db, struct apk_repository *repo,
			     struct apk_package *pkg, char *buf, size_t len)
596 597 598
{
	int r;

599 600 601 602 603 604 605 606 607 608 609 610
	if (pkg != NULL)
		r = snprintf(buf, len, "%s%s" BLOB_FMT "/"  PKG_FILE_FMT,
			     repo->url, repo->url[strlen(repo->url)-1] == '/' ? "" : "/",
			     BLOB_PRINTF(*db->arch), PKG_FILE_PRINTF(pkg));
	else
		r = snprintf(buf, len, "%s%s" BLOB_FMT "/%s",
			     repo->url, repo->url[strlen(repo->url)-1] == '/' ? "" : "/",
			     BLOB_PRINTF(*db->arch), apkindex_tar_gz);
	if (r >= len)
		return -ENOBUFS;
	return 0;
}
611

612 613 614 615 616 617 618 619 620 621 622
int apk_repo_format_item(struct apk_database *db, struct apk_repository *repo, struct apk_package *pkg,
			 int *fd, char *buf, size_t len)
{
	if (strcmp(repo->url, "cache") == 0) {
		*fd = db->cache_fd;
		return apk_pkg_format_cache_pkg(APK_BLOB_PTR_LEN(buf, len), pkg);
	} else {
		*fd = AT_FDCWD;
		return apk_repo_format_real_url(db, repo, pkg, buf, len);
	}
}
623

624 625 626 627 628 629 630 631 632 633 634 635 636
int apk_cache_download(struct apk_database *db, struct apk_repository *repo,
		       struct apk_package *pkg, int verify)
{
	char cacheitem[128], url[PATH_MAX];
	struct apk_istream *is;
	struct apk_bstream *bs;
	struct apk_sign_ctx sctx;
	int r, fd;

	if (pkg != NULL)
		r = apk_pkg_format_cache_pkg(APK_BLOB_BUF(cacheitem), pkg);
	else
		r = apk_repo_format_cache_index(APK_BLOB_BUF(cacheitem), repo);
637 638 639
	if (r < 0)
		return r;

640 641 642
	r = apk_repo_format_real_url(db, repo, pkg, url, sizeof(url));
	if (r < 0)
		return r;
643

644 645 646 647
	apk_message("fetch %s", url);

	if (apk_flags & APK_SIMULATE)
		return 0;
648

649 650 651 652 653
	if (verify != APK_SIGN_NONE) {
		apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL, db->keys_fd);
		bs = apk_bstream_from_url(url);
		bs = apk_bstream_tee(bs, db->cachetmp_fd, cacheitem);
		is = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, &sctx);
654
		r = apk_tar_parse(is, apk_sign_ctx_verify_tar, &sctx, FALSE, &db->id_cache);
655
		apk_sign_ctx_free(&sctx);
656 657 658 659 660 661 662 663
	} else {
		is = apk_istream_from_url(url);
		fd = openat(db->cachetmp_fd, cacheitem, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
		if (fd >= 0) {
			r = apk_istream_splice(is, fd, APK_SPLICE_ALL, NULL, NULL);
			close(fd);
		} else {
			r = -errno;
664 665
		}
	}
666 667 668 669 670
	is->close(is);
	if (r < 0) {
		unlinkat(db->cachetmp_fd, cacheitem, 0);
		return r;
	}
671

672 673 674 675
	if (db->cachetmp_fd != db->cache_fd) {
		if (renameat(db->cachetmp_fd, cacheitem, db->cache_fd, cacheitem) < 0)
			return -errno;
	}
676

677
	return 0;
678 679
}

680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
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
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
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;
	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 {
731 732
			diri = find_diri(ipkg, bdir, diri, &file_diri_node);
			if (diri == NULL) {
733 734
				diri = apk_db_diri_new(db, pkg, bdir, &diri_node);
				file_diri_node = &diri->owned_files.first;
735
			}
Timo Teräs's avatar
Timo Teräs committed
736
			(void) apk_db_file_get(db, diri, bfile, &file_diri_node);
Timo Teräs's avatar
Timo Teräs committed
737 738 739 740 741 742
		}
	}

	return 0;
}

743
int apk_db_index_read(struct apk_database *db, struct apk_bstream *bs, int repo)
744 745
{
	struct apk_package *pkg = NULL;
746
	struct apk_installed_package *ipkg = NULL;
747
	struct apk_db_dir_instance *diri = NULL;
748
	struct apk_db_file *file = NULL;
749 750
	struct hlist_node **diri_node = NULL;
	struct hlist_node **file_diri_node = NULL;
751
	apk_blob_t token = APK_BLOB_STR("\n"), l;
752 753 754
	mode_t mode;
	uid_t uid;
	gid_t gid;
755
	int field, r;
756

757 758 759 760
	while (!APK_BLOB_IS_NULL(l = bs->read(bs, token))) {
		if (l.len < 2 || l.ptr[1] != ':') {
			if (pkg == NULL)
				continue;
761

762
			if (repo >= 0) {
763
				pkg->repos |= BIT(repo);
764 765 766 767
			} else if (repo == -1 && ipkg == NULL) {
				/* Installed package without files */
				ipkg = apk_pkg_install(db, pkg);
			}
768

769
			if (apk_db_pkg_add(db, pkg) == NULL) {
770 771
				apk_error("Installed database load failed");
				return -1;
772
			}
773
			pkg = NULL;
774
			ipkg = NULL;
775 776
			continue;
		}
777

778 779 780 781 782 783 784 785
		/* Get field */
		field = l.ptr[0];
		l.ptr += 2;
		l.len -= 2;

		/* If no package, create new */
		if (pkg == NULL) {
			pkg = apk_pkg_new();
786
			ipkg = NULL;
787 788 789
			diri = NULL;
			file_diri_node = NULL;
		}
790

791
		/* Standard index line? */
792 793
		r = apk_pkg_add_info(db, pkg, field, l);
		if (r == 0) {
794 795
			continue;
		}
796
		if (r == 1 && repo == -1 && ipkg == NULL) {
797 798 799 800 801 802
			/* 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);
		}
803 804
		if (repo != -1 || ipkg == NULL)
			continue;
805

806 807 808 809 810
		/* Check FDB special entries */
		switch (field) {
		case 'F':
			if (pkg->name == NULL) {
				apk_error("FDB directory entry before package entry");
811 812
				return -1;
			}
813 814 815
			diri = apk_db_diri_new(db, pkg, l, &diri_node);
			file_diri_node = &diri->owned_files.first;
			break;
816 817 818 819 820
		case 'a':
			if (file == NULL) {
				apk_error("FDB file attribute metadata entry before file entry");
				return -1;
			}
821 822 823 824 825
		case 'M':
			if (diri == NULL) {
				apk_error("FDB directory metadata entry before directory entry");
				return -1;
			}
826
			uid = apk_blob_pull_uint(&l, 10);
827
			apk_blob_pull_char(&l, ':');
828
			gid = apk_blob_pull_uint(&l, 10);
829
			apk_blob_pull_char(&l, ':');
830
			mode = apk_blob_pull_uint(&l, 8);
831 832 833 834
			if (field == 'M')
				apk_db_diri_set(diri, mode, uid, gid);
			else
				apk_db_file_set(file, mode, uid, gid);
835 836 837 838 839 840
			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
841
			file = apk_db_file_get(db, diri, l, &file_diri_node);
842 843 844 845 846 847
			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
848
			apk_blob_pull_csum(&l, &file->csum);
849
			break;
850
		case 'r':
851
			apk_blob_pull_deps(&l, db, &ipkg->replaces);
852
			break;
853
		case 'q':
854
			ipkg->replaces_priority = apk_blob_pull_uint(&l, 10);
855
			break;
856
		case 'p':
857
		case 's':
858 859
			ipkg->repository_tag = apk_db_get_tag_id(db, l);
			break;
860
		default:
861 862 863 864 865 866 867 868
			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;
869
		}
870 871 872 873
		if (APK_BLOB_IS_NULL(l)) {
			apk_error("FDB format error in entry '%c'", field);
			return -1;
		}
874 875
	}

876
	return 0;
877 878
}

Timo Teräs's avatar
Timo Teräs committed
879
static int apk_db_write_fdb(struct apk_database *db, struct apk_ostream *os)
880
{
881
	struct apk_installed_package *ipkg;
882
	struct apk_package *pkg;
883
	struct apk_db_dir_instance *diri;
884
	struct apk_db_file *file;
885
	struct hlist_node *c1, *c2;
886
	char buf[1024];
887 888
	apk_blob_t bbuf = APK_BLOB_BUF(buf);
	int r;
889

890 891
	list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
		pkg = ipkg->pkg;
892 893 894
		r = apk_pkg_write_index_entry(pkg, os);
		if (r < 0)
			return r;
895

896 897
		if (ipkg->replaces->num) {
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("r:"));
898
			apk_blob_push_deps(&bbuf, db, ipkg->replaces);
899 900
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
		}
901 902 903 904 905
		if (ipkg->replaces_priority) {
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("q:"));
			apk_blob_push_uint(&bbuf, ipkg->replaces_priority, 10);
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
		}
906
		if (ipkg->repository_tag) {
907
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("s:"));
908 909 910
			apk_blob_push_blob(&bbuf, *db->repo_tags[ipkg->repository_tag].name);
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
		}
911
		hlist_for_each_entry(diri, c1, &ipkg->owned_dirs, pkg_dirs_list) {
912 913 914 915 916 917 918 919 920
			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"));
921

922
			hlist_for_each_entry(file, c2, &diri->owned_files, diri_files_list) {
923 924
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("R:"));
				apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN(file->name, file->namelen));
925 926 927 928 929 930 931 932
				if (file->mode != 0 || file->uid != 0 || file->gid != 0) {
					apk_blob_push_blob(&bbuf, APK_BLOB_STR("\na:"));
					apk_blob_push_uint(&bbuf, file->uid, 10);
					apk_blob_push_blob(&bbuf, APK_BLOB_STR(":"));
					apk_blob_push_uint(&bbuf, file->gid, 10);
					apk_blob_push_blob(&bbuf, APK_BLOB_STR(":"));
					apk_blob_push_uint(&bbuf, file->mode, 8);
				}
Timo Teräs's avatar
Timo Teräs committed
933
				if (file->csum.type != APK_CHECKSUM_NONE) {
934
					apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nZ:"));
Timo Teräs's avatar
Timo Teräs committed
935
					apk_blob_push_csum(&bbuf, &file->csum);
936
				}
937
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
938

939
				if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
940
					return -EIO;
941
				bbuf = APK_BLOB_BUF(buf);
942
			}
943
			if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
944
				return -EIO;
945
			bbuf = APK_BLOB_BUF(buf);
946
		}
Timo Teräs's avatar
Timo Teräs committed
947
		os->write(os, "\n", 1);
948 949 950 951 952
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
953
static int apk_db_scriptdb_write(struct apk_database *db, struct apk_ostream *os)
954
{
955
	struct apk_installed_package *ipkg;
956
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
957 958 959
	struct apk_file_info fi;
	char filename[256];
	apk_blob_t bfn;
960
	int r, i;
961
	time_t now = time(NULL);
962 963 964 965 966 967 968

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

Timo Teräs's avatar
Timo Teräs committed
970 971
			fi = (struct apk_file_info) {
				.name = filename,
972
				.size = ipkg->script[i].len,
Timo Teräs's avatar
Timo Teräs committed
973
				.mode = 0755 | S_IFREG,
974
				.mtime = now,
Timo Teräs's avatar
Timo Teräs committed
975 976 977 978 979 980
			};
			/* 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("-"));
981
			apk_blob_push_blob(&bfn, *pkg->version);
Timo Teräs's avatar
Timo Teräs committed
982 983 984
			apk_blob_push_blob(&bfn, APK_BLOB_STR("."));
			apk_blob_push_csum(&bfn, &pkg->csum);
			apk_blob_push_blob(&bfn, APK_BLOB_STR("."));
985
			apk_blob_push_blob(&bfn, APK_BLOB_STR(apk_script_types[i]));
Timo Teräs's avatar
Timo Teräs committed
986 987
			apk_blob_push_blob(&bfn, APK_BLOB_PTR_LEN("", 1));

988
			r = apk_tar_write_entry(os, &fi, ipkg->script[i].ptr);
Timo Teräs's avatar
Timo Teräs committed
989 990
			if (r < 0)
				return r;
991 992 993
		}
	}

994
	return apk_tar_write_entry(os, NULL, NULL);
995 996
}

Timo Teräs's avatar
Timo Teräs committed
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030
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);
1031 1032
	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
1033 1034 1035 1036

	return 0;
}

1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
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);
1058 1059
		bfn = apk_blob_pushed(APK_BLOB_BUF(buf), bfn);
		os->write(os, bfn.ptr, bfn.len);
1060

1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
		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
1086 1087
		if (ipkg->triggers->num != 0 &&
		    !list_hashed(&ipkg->trigger_pkgs_list))
1088 1089 1090 1091 1092
			list_add_tail(&ipkg->trigger_pkgs_list,
				      &db->installed.triggers);
	}
}

Timo Teräs's avatar
Timo Teräs committed
1093
static int apk_db_read_state(struct apk_database *db, int flags)
1094
{
1095
	struct apk_istream *is;
Timo Teräs's avatar