database.c 73.5 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
enum {
42 43
	APK_DIR_FREE = 0,
	APK_DIR_REMOVE
44 45
};

Natanael Copa's avatar
Natanael Copa committed
46 47
int apk_verbosity = 1;
unsigned int apk_flags = 0;
48

49 50
static apk_blob_t tmpprefix = { .len=8, .ptr = ".apknew." };

51 52
static const char * const apkindex_tar_gz = "APKINDEX.tar.gz";

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

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

Timo Teräs's avatar
Timo Teräs committed
58 59 60
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";
61
static const char * const apk_arch_file = "etc/apk/arch";
Timo Teräs's avatar
Timo Teräs committed
62 63 64 65 66 67 68 69 70

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

Timo Teräs's avatar
Timo Teräs committed
71
const char * const apk_installed_file = "lib/apk/db/installed";
Timo Teräs's avatar
Timo Teräs committed
72 73 74
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";

75 76
static struct apk_db_acl *apk_default_acl_dir, *apk_default_acl_file;

77 78 79
struct install_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
80
	struct apk_installed_package *ipkg;
81 82

	int script;
83
	char **script_args;
84
	int script_pending : 1;
85

86
	struct apk_db_dir_instance *diri;
Timo Teräs's avatar
Timo Teräs committed
87
	struct apk_checksum data_csum;
88
	struct apk_sign_ctx sctx;
89

90 91 92 93 94
	apk_progress_cb cb;
	void *cb_ctx;
	size_t installed_size;
	size_t current_file_size;

95 96
	struct hlist_node **diri_node;
	struct hlist_node **file_diri_node;
97 98
};

99
static apk_blob_t pkg_name_get_key(apk_hash_item item)
100
{
101
	return APK_BLOB_STR(((struct apk_name *) item)->name);
102 103
}

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

113 114 115
static const struct apk_hash_ops pkg_name_hash_ops = {
	.node_offset = offsetof(struct apk_name, hash_node),
	.get_key = pkg_name_get_key,
116 117
	.hash_key = apk_blob_hash,
	.compare = apk_blob_compare,
118
	.delete_item = (apk_hash_delete_f) pkg_name_free,
119 120
};

121
static apk_blob_t pkg_info_get_key(apk_hash_item item)
122
{
Timo Teräs's avatar
Timo Teräs committed
123
	return APK_BLOB_CSUM(((struct apk_package *) item)->csum);
124 125
}

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

static const struct apk_hash_ops pkg_info_hash_ops = {
	.node_offset = offsetof(struct apk_package, hash_node),
	.get_key = pkg_info_get_key,
136 137
	.hash_key = csum_hash,
	.compare = apk_blob_compare,
138 139 140
	.delete_item = (apk_hash_delete_f) apk_pkg_free,
};

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

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,
150 151
	.hash_key = apk_blob_hash,
	.compare = apk_blob_compare,
152 153 154
	.delete_item = (apk_hash_delete_f) free,
};

155 156 157 158 159 160
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)
161
{
162 163
	struct apk_db_file_hash_key *key = (struct apk_db_file_hash_key *) _key.ptr;

Timo Teräs's avatar
Timo Teräs committed
164
	return apk_blob_hash_seed(key->filename, apk_blob_hash(key->dirname));
165 166 167 168 169 170
}

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
171 172
	return apk_blob_hash_seed(APK_BLOB_PTR_LEN(dbf->name, dbf->namelen),
				  dbf->diri->dir->hash);
173 174 175 176 177 178
}

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
179
	struct apk_db_dir *dir = dbf->diri->dir;
180 181
	int r;

Timo Teräs's avatar
Timo Teräs committed
182 183
	r = apk_blob_compare(key->filename,
			     APK_BLOB_PTR_LEN(dbf->name, dbf->namelen));
184 185 186
	if (r != 0)
		return r;

Timo Teräs's avatar
Timo Teräs committed
187 188 189
	r = apk_blob_compare(key->dirname,
			     APK_BLOB_PTR_LEN(dir->name, dir->namelen));
	return r;
190 191 192 193
}

static const struct apk_hash_ops file_hash_ops = {
	.node_offset = offsetof(struct apk_db_file, hash_node),
194 195 196
	.hash_key = apk_db_file_hash_key,
	.hash_item = apk_db_file_hash_item,
	.compare_item = apk_db_file_compare_item,
197 198 199
	.delete_item = (apk_hash_delete_f) free,
};

Timo Teräs's avatar
Timo Teräs committed
200 201 202 203 204
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);
}

205
struct apk_name *apk_db_get_name(struct apk_database *db, apk_blob_t name)
206 207
{
	struct apk_name *pn;
208
	unsigned long hash = apk_hash_from_key(&db->available.names, name);
209

210
	pn = (struct apk_name *) apk_hash_get_hashed(&db->available.names, name, hash);
211 212 213 214 215 216 217
	if (pn != NULL)
		return pn;

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

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

	return pn;
}

227 228 229 230 231 232 233 234
static struct apk_db_acl *apk_db_acl_atomize(mode_t mode, uid_t uid, gid_t gid)
{
	struct apk_db_acl acl = { .mode = mode & 07777, .uid = uid, .gid = gid };
	apk_blob_t *b;
	b = apk_blob_atomize_dup(APK_BLOB_STRUCT(acl));
	return (struct apk_db_acl *) b->ptr;
}

235
static void apk_db_dir_prepare(struct apk_database *db, struct apk_db_dir *dir, mode_t newmode)
236
{
237
	struct stat st;
238

239 240
	if (dir->namelen == 0) return;
	if (dir->created) return;
241

242 243 244 245 246 247 248 249 250 251 252 253 254
	if (fstatat(db->root_fd, dir->name, &st, AT_SYMLINK_NOFOLLOW) == 0) {
		/* If directory exists and stats match what we expect,
		 * then we can allow auto updating the permissions */
		dir->created = 1;
		dir->update_permissions |=
			(st.st_mode & 07777) == (dir->mode & 07777) &&
			st.st_uid == dir->uid && st.st_gid == dir->gid;
	} else if (newmode) {
		if (!(apk_flags & APK_SIMULATE))
			mkdirat(db->root_fd, dir->name, newmode);
		dir->created = 1;
		dir->update_permissions = 1;
	}
255 256
}

257
void apk_db_dir_unref(struct apk_database *db, struct apk_db_dir *dir, int rmdir_mode)
258
{
259
	if (--dir->refs > 0) return;
260
	db->installed.stats.dirs--;
261
	if (dir->namelen == 0) return;
262

263 264 265
	if (rmdir_mode == APK_DIR_REMOVE && !(apk_flags & APK_SIMULATE))
		if (unlinkat(db->root_fd, dir->name, AT_REMOVEDIR))
			;
266

267
	apk_db_dir_unref(db, dir->parent, rmdir_mode);
268 269
}

270
struct apk_db_dir *apk_db_dir_ref(struct apk_db_dir *dir)
271 272 273 274 275
{
	dir->refs++;
	return dir;
}

276 277
struct apk_db_dir *apk_db_dir_query(struct apk_database *db,
				    apk_blob_t name)
278 279 280 281
{
	return (struct apk_db_dir *) apk_hash_get(&db->installed.dirs, name);
}

282
struct apk_db_dir *apk_db_dir_get(struct apk_database *db, apk_blob_t name)
283 284
{
	struct apk_db_dir *dir;
285
	struct apk_protected_path_array *ppaths;
286
	struct apk_protected_path *ppath;
287
	apk_blob_t bparent;
288
	unsigned long hash = apk_hash_from_key(&db->installed.dirs, name);
289
	char *relative_name;
290

291
	if (name.len && name.ptr[name.len-1] == '/')
292 293
		name.len--;

294
	dir = (struct apk_db_dir *) apk_hash_get_hashed(&db->installed.dirs, name, hash);
295
	if (dir != NULL)
296
		return apk_db_dir_ref(dir);
297

298
	db->installed.stats.dirs++;
299 300
	dir = malloc(sizeof(*dir) + name.len + 1);
	memset(dir, 0, sizeof(*dir));
301
	dir->refs = 1;
302 303
	dir->uid = (uid_t) -1;
	dir->gid = (gid_t) -1;
304
	dir->rooted_name[0] = '/';
Timo Teräs's avatar
Timo Teräs committed
305 306 307 308
	memcpy(dir->name, name.ptr, name.len);
	dir->name[name.len] = 0;
	dir->namelen = name.len;
	dir->hash = hash;
309
	apk_protected_path_array_init(&dir->protected_paths);
310
	apk_hash_insert_hashed(&db->installed.dirs, dir, hash);
311

312
	if (name.len == 0) {
313
		dir->parent = NULL;
314 315 316
		dir->has_protected_children = 1;
		ppaths = NULL;
	} else if (apk_blob_rsplit(name, '/', &bparent, NULL)) {
317
		dir->parent = apk_db_dir_get(db, bparent);
318 319
		dir->protect_mode = dir->parent->protect_mode;
		dir->has_protected_children = (dir->protect_mode != APK_PROTECT_NONE);
320 321
		ppaths = dir->parent->protected_paths;
	} else {
322
		dir->parent = apk_db_dir_get(db, APK_BLOB_NULL);
323 324
		ppaths = db->protected_paths;
	}
325

326 327
	if (ppaths == NULL)
		return dir;
328

329
	relative_name = strrchr(dir->rooted_name, '/') + 1;
330 331
	foreach_array_item(ppath, ppaths) {
		char *slash = strchr(ppath->relative_pattern, '/');
332 333 334 335
		if (slash != NULL) {
			*slash = 0;
			if (fnmatch(ppath->relative_pattern, relative_name, FNM_PATHNAME) != 0) {
				*slash = '/';
336 337
				continue;
			}
338 339 340 341
			*slash = '/';

			*apk_protected_path_array_add(&dir->protected_paths) = (struct apk_protected_path) {
				.relative_pattern = slash + 1,
342
				.protect_mode = ppath->protect_mode,
343 344 345 346
			};
		} else {
			if (fnmatch(ppath->relative_pattern, relative_name, FNM_PATHNAME) != 0)
				continue;
347

348
			dir->protect_mode = ppath->protect_mode;
349
		}
350
		dir->has_protected_children |= (ppath->protect_mode != APK_PROTECT_NONE);
351 352
	}

353 354 355
	return dir;
}

356 357 358
static struct apk_db_dir_instance *apk_db_diri_new(struct apk_database *db,
						   struct apk_package *pkg,
						   apk_blob_t name,
359
						   struct hlist_node ***after)
360 361 362 363
{
	struct apk_db_dir_instance *diri;

	diri = calloc(1, sizeof(struct apk_db_dir_instance));
364
	if (diri != NULL) {
365 366
		hlist_add_after(&diri->pkg_dirs_list, *after);
		*after = &diri->pkg_dirs_list.next;
367
		diri->dir = apk_db_dir_get(db, name);
368
		diri->pkg = pkg;
369
		diri->acl = apk_default_acl_dir;
370
	}
371 372 373 374

	return diri;
}

375 376 377
static void apk_db_dir_apply_diri_permissions(struct apk_db_dir_instance *diri)
{
	struct apk_db_dir *dir = diri->dir;
378
	struct apk_db_acl *acl = diri->acl;
379

380 381 382 383 384 385
	if (acl->uid < dir->uid || (acl->uid == dir->uid && acl->gid < dir->gid)) {
		dir->uid = acl->uid;
		dir->gid = acl->gid;
		dir->mode = acl->mode;
	} else if (acl->uid == dir->uid && acl->gid == dir->gid) {
		dir->mode &= acl->mode;
386 387 388
	}
}

389
static void apk_db_diri_set(struct apk_db_dir_instance *diri, struct apk_db_acl *acl)
390
{
391
	diri->acl = acl;
392
	apk_db_dir_apply_diri_permissions(diri);
393 394
}

395
static void apk_db_diri_free(struct apk_database *db,
396
			     struct apk_db_dir_instance *diri,
397
			     int rmdir_mode)
398
{
399
	struct apk_db_dir *dir = diri->dir;
400

401 402 403 404
	if (rmdir_mode == APK_DIR_REMOVE)
		apk_db_dir_prepare(db, diri->dir, 0);

	apk_db_dir_unref(db, dir, rmdir_mode);
405 406 407
	free(diri);
}

408 409 410 411 412 413
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;

414 415 416
	if (dir.len && dir.ptr[dir.len-1] == '/')
		dir.len--;

417 418 419 420 421 422 423 424 425
	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
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
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;
442
	file->acl = apk_default_acl_file;
Timo Teräs's avatar
Timo Teräs committed
443 444 445 446 447 448
	hlist_add_after(&file->diri_files_list, *after);
	*after = &file->diri_files_list.next;

	return file;
}

449
static struct apk_db_file *apk_db_file_get(struct apk_database *db,
450
					   struct apk_db_dir_instance *diri,
451 452
					   apk_blob_t name,
					   struct hlist_node ***after)
453 454
{
	struct apk_db_file *file;
455
	struct apk_db_file_hash_key key;
Timo Teräs's avatar
Timo Teräs committed
456 457
	struct apk_db_dir *dir = diri->dir;
	unsigned long hash;
458 459

	key = (struct apk_db_file_hash_key) {
Timo Teräs's avatar
Timo Teräs committed
460
		.dirname = APK_BLOB_PTR_LEN(dir->name, dir->namelen),
461 462
		.filename = name,
	};
463

Timo Teräs's avatar
Timo Teräs committed
464 465 466
	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);
467 468 469
	if (file != NULL)
		return file;

Timo Teräs's avatar
Timo Teräs committed
470
	file = apk_db_file_new(diri, name, after);
Timo Teräs's avatar
Timo Teräs committed
471
	apk_hash_insert_hashed(&db->installed.files, file, hash);
472
	db->installed.stats.files++;
473 474 475 476

	return file;
}

Timo Teräs's avatar
Timo Teräs committed
477 478
static void apk_db_pkg_rdepends(struct apk_database *db, struct apk_package *pkg)
{
479 480
	struct apk_name *rname, **rd;
	struct apk_dependency *d;
Timo Teräs's avatar
Timo Teräs committed
481

482 483
	foreach_array_item(d, pkg->depends) {
		rname = d->name;
484
		rname->is_dependency |= !d->conflict;
485 486
		foreach_array_item(rd, rname->rdepends)
			if (*rd == pkg->name)
Timo Teräs's avatar
Timo Teräs committed
487
				goto rdeps_done;
Timo Teräs's avatar
Timo Teräs committed
488
		*apk_name_array_add(&rname->rdepends) = pkg->name;
489
rdeps_done: ;
Timo Teräs's avatar
Timo Teräs committed
490
	}
491 492 493 494
	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
495 496
				goto riif_done;
		*apk_name_array_add(&rname->rinstall_if) = pkg->name;
497
riif_done: ;
Timo Teräs's avatar
Timo Teräs committed
498 499
	}
	return;
Timo Teräs's avatar
Timo Teräs committed
500 501
}

502 503 504 505 506
static inline void add_provider(struct apk_name *name, struct apk_provider p)
{
	*apk_provider_array_add(&name->providers) = p;
}

507
struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package *pkg)
508 509
{
	struct apk_package *idb;
510
	struct apk_dependency *dep;
511

512
	if (pkg->license == NULL)
513
		pkg->license = apk_blob_atomize(APK_BLOB_NULL);
514

515 516 517 518 519
	/* 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
520
	idb = apk_hash_get(&db->available.packages, APK_BLOB_CSUM(pkg->csum));
521 522 523
	if (idb == NULL) {
		idb = pkg;
		apk_hash_insert(&db->available.packages, pkg);
524
		add_provider(pkg->name, APK_PROVIDER_FROM_PACKAGE(pkg));
525
		foreach_array_item(dep, pkg->provides)
526
			add_provider(dep->name, APK_PROVIDER_FROM_PROVIDES(pkg, dep));
Timo Teräs's avatar
Timo Teräs committed
527
		apk_db_pkg_rdepends(db, pkg);
528 529
	} else {
		idb->repos |= pkg->repos;
530 531 532 533
		if (idb->filename == NULL && pkg->filename != NULL) {
			idb->filename = pkg->filename;
			pkg->filename = NULL;
		}
534 535 536 537 538
		if (idb->ipkg == NULL && pkg->ipkg != NULL) {
			idb->ipkg = pkg->ipkg;
			idb->ipkg->pkg = idb;
			pkg->ipkg = NULL;
		}
539 540 541 542 543
		apk_pkg_free(pkg);
	}
	return idb;
}

544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
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)
561
{
562
	/* APKINDEX.12345678.tar.gz */
563
	apk_blob_push_blob(&to, APK_BLOB_STR("APKINDEX."));
564
	apk_blob_push_hexdump(&to, APK_BLOB_PTR_LEN((char *) repo->csum.data, APK_CACHE_CSUM_BYTES));
565
	apk_blob_push_blob(&to, APK_BLOB_STR(".tar.gz"));
566
	apk_blob_push_blob(&to, APK_BLOB_PTR_LEN("", 1));
567 568 569
	if (APK_BLOB_IS_NULL(to))
		return -ENOBUFS;
	return 0;
570 571
}

572 573
int apk_repo_format_real_url(struct apk_database *db, struct apk_repository *repo,
			     struct apk_package *pkg, char *buf, size_t len)
574
{
575
	apk_blob_t arch;
576 577
	int r;

578 579 580
	if (pkg && pkg->arch) arch = *pkg->arch;
	else arch = *db->arch;

581 582 583
	if (pkg != NULL)
		r = snprintf(buf, len, "%s%s" BLOB_FMT "/"  PKG_FILE_FMT,
			     repo->url, repo->url[strlen(repo->url)-1] == '/' ? "" : "/",
584
			     BLOB_PRINTF(arch), PKG_FILE_PRINTF(pkg));
585 586 587
	else
		r = snprintf(buf, len, "%s%s" BLOB_FMT "/%s",
			     repo->url, repo->url[strlen(repo->url)-1] == '/' ? "" : "/",
588
			     BLOB_PRINTF(arch), apkindex_tar_gz);
589 590 591 592
	if (r >= len)
		return -ENOBUFS;
	return 0;
}
593

594 595 596
int apk_repo_format_item(struct apk_database *db, struct apk_repository *repo, struct apk_package *pkg,
			 int *fd, char *buf, size_t len)
{
Timo Teräs's avatar
Timo Teräs committed
597
	if (repo->url == apk_linked_cache_dir) {
598 599 600 601 602 603 604
		*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);
	}
}
605

606
int apk_cache_download(struct apk_database *db, struct apk_repository *repo,
607 608
		       struct apk_package *pkg, int verify,
		       apk_progress_cb cb, void *cb_ctx)
609
{
610
	struct stat st;
611 612 613
	struct apk_istream *is;
	struct apk_bstream *bs;
	struct apk_sign_ctx sctx;
614 615 616
	char url[PATH_MAX];
	char tmpcacheitem[128], *cacheitem = &tmpcacheitem[tmpprefix.len];
	apk_blob_t b = APK_BLOB_BUF(tmpcacheitem);
617 618
	int r, fd;

619
	apk_blob_push_blob(&b, tmpprefix);
620
	if (pkg != NULL)
621
		r = apk_pkg_format_cache_pkg(b, pkg);
622
	else
623
		r = apk_repo_format_cache_index(b, repo);
624
	if (r < 0) return r;
625

626
	r = apk_repo_format_real_url(db, repo, pkg, url, sizeof(url));
627
	if (r < 0) return r;
628

629 630 631
	if ((apk_flags & APK_FORCE) ||
	    fstatat(db->cache_fd, cacheitem, &st, 0) != 0)
		st.st_mtime = 0;
632

633
	apk_message("fetch %s", url);
634

635 636
	if (apk_flags & APK_SIMULATE) return 0;
	if (cb) cb(cb_ctx, 0);
637

638 639
	if (verify != APK_SIGN_NONE) {
		apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL, db->keys_fd);
640
		bs = apk_bstream_from_url_if_modified(url, st.st_mtime);
641
		bs = apk_bstream_tee(bs, db->cache_fd, tmpcacheitem, cb, cb_ctx);
642
		is = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, &sctx);
643 644 645 646
		if (!IS_ERR_OR_NULL(is))
			r = apk_tar_parse(is, apk_sign_ctx_verify_tar, &sctx, FALSE, &db->id_cache);
		else
			r = PTR_ERR(is) ?: -EIO;
647
		apk_sign_ctx_free(&sctx);
648
	} else {
649 650 651 652 653 654
		is = apk_istream_from_url_if_modified(url, st.st_mtime);
		if (!IS_ERR_OR_NULL(is)) {
			fd = openat(db->cache_fd, tmpcacheitem, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
			if (fd < 0) r = -errno;
		} else fd = -1, r = PTR_ERR(is) ?: -EIO;

655
		if (fd >= 0) {
656
			r = apk_istream_splice(is, fd, APK_SPLICE_ALL, cb, cb_ctx);
657
			close(fd);
658 659
		}
	}
660 661
	if (!IS_ERR_OR_NULL(is)) is->close(is);
	if (r == -EALREADY) return 0;
662
	if (r < 0) {
663
		unlinkat(db->cache_fd, tmpcacheitem, 0);
664 665
		return r;
	}
666

667 668
	if (renameat(db->cache_fd, tmpcacheitem, db->cache_fd, cacheitem) < 0)
		return -errno;
669
	return 0;
670 671
}

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

	return 0;
}

736
int apk_db_index_read(struct apk_database *db, struct apk_bstream *bs, int repo)
737 738
{
	struct apk_package *pkg = NULL;
739
	struct apk_installed_package *ipkg = NULL;
740
	struct apk_db_dir_instance *diri = NULL;
741
	struct apk_db_file *file = NULL;
742
	struct apk_db_acl *acl;
743 744
	struct hlist_node **diri_node = NULL;
	struct hlist_node **file_diri_node = NULL;
745
	apk_blob_t token = APK_BLOB_STR("\n"), l;
746 747 748
	mode_t mode;
	uid_t uid;
	gid_t gid;
749
	int field, r, lineno = 0;
750

751
	while (!APK_BLOB_IS_NULL(l = bs->read(bs, token))) {
752 753
		lineno++;

754 755 756
		if (l.len < 2 || l.ptr[1] != ':') {
			if (pkg == NULL)
				continue;
757

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

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

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

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

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

803 804 805
		/* Check FDB special entries */
		switch (field) {
		case 'F':
806
			if (pkg->name == NULL) goto bad_entry;
807 808 809
			diri = apk_db_diri_new(db, pkg, l, &diri_node);
			file_diri_node = &diri->owned_files.first;
			break;
810
		case 'a':
811
			if (file == NULL) goto bad_entry;
812
		case 'M':
813
			if (diri == NULL) goto bad_entry;
814
			uid = apk_blob_pull_uint(&l, 10);
815
			apk_blob_pull_char(&l, ':');
816
			gid = apk_blob_pull_uint(&l, 10);
817
			apk_blob_pull_char(&l, ':');
818
			mode = apk_blob_pull_uint(&l, 8);
819
			acl = apk_db_acl_atomize(mode, uid, gid);
820
			if (field == 'M')
821
				apk_db_diri_set(diri, acl);
822
			else
823
				file->acl = acl;
824 825
			break;
		case 'R':
826
			if (diri == NULL) goto bad_entry;
Timo Teräs's avatar
Timo Teräs committed
827
			file = apk_db_file_get(db, diri, l, &file_diri_node);
828 829
			break;
		case 'Z':
830
			if (file == NULL) goto bad_entry;
Timo Teräs's avatar
Timo Teräs committed
831
			apk_blob_pull_csum(&l, &file->csum);
832
			break;
833
		case 'r':
834
			apk_blob_pull_deps(&l, db, &ipkg->replaces);
835
			break;
836
		case 'q':
837
			ipkg->replaces_priority = apk_blob_pull_uint(&l, 10);