database.c 73.8 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
static const char * const apk_world_file = "etc/apk/world";
static const char * const apk_world_file_tmp = "etc/apk/world.new";
60
static const char * const apk_arch_file = "etc/apk/arch";
Timo Teräs's avatar
Timo Teräs committed
61 62 63 64 65 66 67

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_triggers_file = "lib/apk/db/triggers";
static const char * const apk_triggers_file_tmp = "lib/apk/db/triggers.new";

Timo Teräs's avatar
Timo Teräs committed
68
const char * const apk_installed_file = "lib/apk/db/installed";
Timo Teräs's avatar
Timo Teräs committed
69 70
static const char * const apk_installed_file_tmp = "lib/apk/db/installed.new";

71 72
static struct apk_db_acl *apk_default_acl_dir, *apk_default_acl_file;

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
static struct apk_db_acl *apk_db_acl_atomize(mode_t mode, uid_t uid, gid_t gid, const struct apk_checksum *xattr_csum)
224 225 226
{
	struct apk_db_acl acl = { .mode = mode & 07777, .uid = uid, .gid = gid };
	apk_blob_t *b;
227 228 229 230

	if (xattr_csum && xattr_csum->type != APK_CHECKSUM_NONE)
		acl.xattr_csum = *xattr_csum;

231 232 233 234
	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
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;

705 706
	if (IS_ERR_OR_NULL(bs)) return -1;

Timo Teräs's avatar
Timo Teräs committed
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
	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;
724
			diri->dir->created = 1;
Timo Teräs's avatar
Timo Teräs committed
725
		} else {
726 727
			diri = find_diri(ipkg, bdir, diri, &file_diri_node);
			if (diri == NULL) {
728 729
				diri = apk_db_diri_new(db, pkg, bdir, &diri_node);
				file_diri_node = &diri->owned_files.first;
730
			}
Timo Teräs's avatar
Timo Teräs committed
731
			(void) apk_db_file_get(db, diri, bfile, &file_diri_node);
Timo Teräs's avatar
Timo Teräs committed
732 733 734 735 736 737
		}
	}

	return 0;
}

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

754
	while (!APK_BLOB_IS_NULL(l = bs->read(bs, token))) {
755 756
		lineno++;

757 758 759
		if (l.len < 2 || l.ptr[1] != ':') {
			if (pkg == NULL)
				continue;
760

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

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

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

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

792
		/* Standard index line? */
793
		r = apk_pkg_add_info(db, pkg, field, l);
794
		if (r == 0)
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
		/* Check FDB special entries */
		switch (field) {
		case 'F':
809
			if (pkg->name == NULL) goto bad_entry;
810 811 812
			diri = apk_db_diri_new(db, pkg, l, &diri_node);
			file_diri_node = &diri->owned_files.first;
			break;
813
		case 'a':
814
			if (file == NULL) goto bad_entry;
815
		case 'M':
816
			if (diri == NULL) goto bad_entry;
817
			uid = apk_blob_pull_uint(&l, 10);
818
			apk_blob_pull_char(&l, ':');
819
			gid = apk_blob_pull_uint(&l, 10);
820
			apk_blob_pull_char(&l, ':');
821
			mode = apk_blob_pull_uint(&l, 8);
822 823 824 825 826 827
			if (apk_blob_pull_blob_match(&l, APK_BLOB_STR(":")))
				apk_blob_pull_csum(&l, &xattr_csum);
			else
				xattr_csum.type = APK_CHECKSUM_NONE;

			acl = apk_db_acl_atomize(mode, uid, gid, &xattr_csum);
828
			if (field == 'M')
829
				apk_db_diri_set(diri, acl);
830
			else
831
				file->acl = acl;
832 833
			break;
		case 'R':
834
			if (diri == NULL) goto bad_entry;
Timo Teräs's avatar
Timo Teräs committed
835
			file = apk_db_file_get(db, diri, l, &file_diri_node);
836 837
			break;
		case 'Z':
838
			if (file == NULL) goto bad_entry;
Timo Teräs's avatar
Timo Teräs committed
839
			apk_blob_pull_csum(&l, &file->csum);
840
			break;
841
		case 'r':
842
			apk_blob_pull_deps(&l, db, &ipkg->replaces);
843
			break;
844
		case 'q':
845
			ipkg->replaces_priority = apk_blob_pull_uint(&l, 10);
846
			break;
847
		case 's':
848 849
			ipkg->repository_tag = apk_db_get_tag_id(db, l);
			break;
850 851 852 853 854
		case 'f':
			for (r = 0; r < l.len; r++) {
				switch (l.ptr[r]) {
				case 'f': ipkg->broken_files = 1; break;
				case 's': ipkg->broken_script = 1; break;
855
				case 'x': ipkg->broken_xattr = 1; break;
856 857 858 859
				default:
					if (!(apk_flags & APK_FORCE))
						goto old_apk_tools;
				}
860
			}
861 862 863 864
			break;
		default:
			if (r != 0 && !(apk_flags & APK_FORCE))
				goto old_apk_tools;
865 866 867
			/* Installed. So mark the package as installable. */
			pkg->filename = NULL;
			continue;
868
		}
869
		if (APK_BLOB_IS_NULL(l)) goto bad_entry;
870
	}
871
	return 0;
872 873 874 875
old_apk_tools:
	/* Installed db should not have unsupported fields */
	apk_error("This apk-tools is too old to handle installed packages");
	return -1;
876 877 878
bad_entry:
	apk_error("FDB format error (line %d, entry '%c')", lineno, field);
	return -1;
879 880
}

881 882 883 884 885 886 887 888 889 890
static void apk_blob_push_db_acl(apk_blob_t *b, char field, struct apk_db_acl *acl)
{
	char hdr[2] = { field, ':' };

	apk_blob_push_blob(b, APK_BLOB_BUF(hdr));
	apk_blob_push_uint(b, acl->uid, 10);
	apk_blob_push_blob(b, APK_BLOB_STR(":"));
	apk_blob_push_uint(b, acl->gid, 10);
	apk_blob_push_blob(b, APK_BLOB_STR(":"));
	apk_blob_push_uint(b, acl->mode, 8);
891 892 893 894
	if (acl->xattr_csum.type != APK_CHECKSUM_NONE) {
		apk_blob_push_blob(b, APK_BLOB_STR(":"));
		apk_blob_push_csum(b, &acl->xattr_csum);
	}
895 896 897
	apk_blob_push_blob(b, APK_BLOB_STR("\n"));
}

Timo Teräs's avatar
Timo Teräs committed
898
static int apk_db_write_fdb(struct apk_database *db, struct apk_ostream *os)
899
{
900
	struct apk_installed_package *ipkg;
901
	struct apk_package *pkg;
902
	struct apk_db_dir_instance *diri;
903
	struct apk_db_file *file;
904
	struct hlist_node *c1, *c2;
905
	char buf[1024];
906 907
	apk_blob_t bbuf = APK_BLOB_BUF(buf);
	int r;
908

909 910
	list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
		pkg = ipkg->pkg;
911 912 913
		r = apk_pkg_write_index_entry(pkg, os);
		if (r < 0)
			return r;
914

915 916
		if (ipkg->replaces->num) {
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("r:"));
917
			apk_blob_push_deps(&bbuf, db, ipkg->replaces);
918 919
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
		}
920 921 922 923 924
		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"));
		}
925
		if (ipkg->repository_tag) {
926
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("s:"));
927
			apk_blob_push_blob(&bbuf, db->repo_tags[ipkg->repository_tag].plain_name);
928 929
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
		}
930
		if (ipkg->broken_files || ipkg->broken_script || ipkg->broken_xattr) {
931 932 933 934 935
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("f:"));
			if (ipkg->broken_files)
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("f"));
			if (ipkg->broken_script)
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("s"));
936 937
			if (ipkg->broken_xattr)
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("x"));
938 939
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
		}
940
		hlist_for_each_entry(diri, c1, &ipkg->owned_dirs, pkg_dirs_list) {
941 942 943
			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("\n"));
944

945
			if (diri->acl != apk_default_acl_dir)
946 947
				apk_blob_push_db_acl(&bbuf, 'M', diri->acl);

948
			hlist_for_each_entry(file, c2, &diri->owned_files, diri_files_list) {
949 950
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("R:"));
				apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN(file->name, file->namelen));
951 952
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));

953
				if (file->acl != apk_default_acl_file)
954 955
					apk_blob_push_db_acl(&bbuf, 'a', file->acl);