database.c 74.9 KB
Newer Older
1 2 3
/* database.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
4
 * Copyright (C) 2008-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));
527 528
		if (db->open_complete)
			apk_db_pkg_rdepends(db, pkg);
529 530
	} else {
		idb->repos |= pkg->repos;
531 532 533 534
		if (idb->filename == NULL && pkg->filename != NULL) {
			idb->filename = pkg->filename;
			pkg->filename = NULL;
		}
535 536 537 538 539
		if (idb->ipkg == NULL && pkg->ipkg != NULL) {
			idb->ipkg = pkg->ipkg;
			idb->ipkg->pkg = idb;
			pkg->ipkg = NULL;
		}
540 541 542 543 544
		apk_pkg_free(pkg);
	}
	return idb;
}

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

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

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

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

595 596 597
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
598
	if (repo->url == apk_linked_cache_dir) {
599 600 601 602 603 604 605
		*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);
	}
}
606

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

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

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

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

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

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

639 640
	if (verify != APK_SIGN_NONE) {
		apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL, db->keys_fd);
641
		bs = apk_bstream_from_url_if_modified(url, st.st_mtime);
642
		bs = apk_bstream_tee(bs, db->cache_fd, tmpcacheitem, cb, cb_ctx);
643
		is = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, &sctx);
644 645 646 647
		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;
648
		apk_sign_ctx_free(&sctx);
649
	} else {
650 651 652 653 654 655
		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;

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

668 669
	if (renameat(db->cache_fd, tmpcacheitem, db->cache_fd, cacheitem) < 0)
		return -errno;
670
	return 0;
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 697
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
698 699 700 701 702 703 704 705
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;

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

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

	return 0;
}

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

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

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

762 763
			if (diri) apk_db_dir_apply_diri_permissions(diri);

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

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

782 783 784 785 786 787 788 789
		/* Get field */
		field = l.ptr[0];
		l.ptr += 2;
		l.len -= 2;

		/* If no package, create new */
		if (pkg == NULL) {
			pkg = apk_pkg_new();
790
			ipkg = NULL;
791 792 793
			diri = NULL;
			file_diri_node = NULL;
		}
794

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

809 810 811
		/* Check FDB special entries */
		switch (field) {
		case 'F':
812
			if (diri) apk_db_dir_apply_diri_permissions(diri);
813
			if (pkg->name == NULL) goto bad_entry;
814 815 816
			diri = apk_db_diri_new(db, pkg, l, &diri_node);
			file_diri_node = &diri->owned_files.first;
			break;
817
		case 'a':
818
			if (file == NULL) goto bad_entry;
819
		case 'M':
820
			if (diri == NULL) goto bad_entry;
821
			uid = apk_blob_pull_uint(&l, 10);
822
			apk_blob_pull_char(&l, ':');
823
			gid = apk_blob_pull_uint(&l, 10);
824
			apk_blob_pull_char(&l, ':');
825
			mode = apk_blob_pull_uint(&l, 8);
826 827 828 829 830 831
			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);
832
			if (field == 'M')
833
				diri->acl = acl;
834
			else
835
				file->acl = acl;
836 837
			break;
		case 'R':
838
			if (diri == NULL) goto bad_entry;
Timo Teräs's avatar
Timo Teräs committed
839
			file = apk_db_file_get(db, diri, l, &file_diri_node);
840 841
			break;
		case 'Z':
842
			if (file == NULL) goto bad_entry;
Timo Teräs's avatar
Timo Teräs committed
843
			apk_blob_pull_csum(&l, &file->csum);
844
			break;
845
		case 'r':
846
			apk_blob_pull_deps(&l, db, &ipkg->replaces);
847
			break;
848
		case 'q':
849
			ipkg->replaces_priority = apk_blob_pull_uint(&l, 10);
850
			break;
851
		case 's':
852 853
			ipkg->repository_tag = apk_db_get_tag_id(db, l);
			break;
854 855 856 857 858
		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;
859
				case 'x': ipkg->broken_xattr = 1; break;
860 861 862 863
				default:
					if (!(apk_flags & APK_FORCE))
						goto old_apk_tools;
				}
864
			}
865 866 867 868
			break;
		default:
			if (r != 0 && !(apk_flags & APK_FORCE))
				goto old_apk_tools;
869 870 871
			/* Installed. So mark the package as installable. */
			pkg->filename = NULL;
			continue;
872
		}
873
		if (APK_BLOB_IS_NULL(l)) goto bad_entry;
874
	}
875
	return 0;
876 877 878 879
old_apk_tools:
	/* Installed db should not have unsupported fields */
	apk_error("This apk-tools is too old to handle installed packages");
	return -1;
880 881 882
bad_entry:
	apk_error("FDB format error (line %d, entry '%c')", lineno, field);
	return -1;
883 884
}

885 886 887 888 889 890 891 892 893 894
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);
895 896 897 898
	if (acl->xattr_csum.type != APK_CHECKSUM_NONE) {
		apk_blob_push_blob(b, APK_BLOB_STR(":"));
		apk_blob_push_csum(b, &acl->xattr_csum);
	}
899 900 901
	apk_blob_push_blob(b, APK_BLOB_STR("\n"));
}

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

913 914
	list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
		pkg = ipkg->pkg;
915 916 917
		r = apk_pkg_write_index_entry(pkg, os);
		if (r < 0)
			return r;
918

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

949
			if (diri->acl != apk_default_acl_dir)
950 951
				apk_blob_push_db_acl(&bbuf, 'M', diri->acl);

952
			hlist_for_each_entry(file, c2, &diri->owned_files, diri_files_list) {
953 954
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("R:"));
				apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN(file->name, file->namelen));
955 956
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));