database.c 71.7 KB
Newer Older
1
2
3
/* database.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
4
 * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
5
6
 * All rights reserved.
 *
7
 * This program is free software; you can redistribute it and/or modify it
8
9
10
11
12
13
14
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation. See http://www.gnu.org/ for details.
 */

#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
15
#include <mntent.h>
16
#include <limits.h>
17
18
19
#include <unistd.h>
#include <malloc.h>
#include <string.h>
20
#include <stdlib.h>
21
#include <signal.h>
22
#include <fnmatch.h>
Timo Teräs's avatar
Timo Teräs committed
23
#include <sys/vfs.h>
24
#include <sys/file.h>
25
#include <sys/wait.h>
Natanael Copa's avatar
Natanael Copa committed
26
#include <sys/stat.h>
27
#include <sys/statvfs.h>
28
29
30
31

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

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

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

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

49
50
static 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
77
struct install_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
78
	struct apk_installed_package *ipkg;
79
80

	int script;
81
	char **script_args;
82
	int script_pending : 1;
83

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

88
89
90
91
92
	apk_progress_cb cb;
	void *cb_ctx;
	size_t installed_size;
	size_t current_file_size;

93
94
	struct hlist_node **diri_node;
	struct hlist_node **file_diri_node;
95
96
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	return pn;
}

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

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

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

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

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

259
	db->installed.stats.dirs--;
260
261

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

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

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

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

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

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

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

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

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

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

337
338
	if (ppaths == NULL)
		return dir;
339

340
	relative_name = strrchr(dir->rooted_name, '/') + 1;
341
342
	foreach_array_item(ppath, ppaths) {
		char *slash = strchr(ppath->relative_pattern, '/');
343
344
345
346
		if (slash != NULL) {
			*slash = 0;
			if (fnmatch(ppath->relative_pattern, relative_name, FNM_PATHNAME) != 0) {
				*slash = '/';
347
348
				continue;
			}
349
350
351
352
353
354
355
356
357
358
			*slash = '/';

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

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

366
367
368
	return dir;
}

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

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

	return diri;
}

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

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

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

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

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

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

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

431
432
433
434
435
436
437
438
439
	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
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
static struct apk_db_file *apk_db_file_new(struct apk_db_dir_instance *diri,
					   apk_blob_t name,
					   struct hlist_node ***after)
{
	struct apk_db_file *file;

	file = malloc(sizeof(*file) + name.len + 1);
	if (file == NULL)
		return NULL;

	memset(file, 0, sizeof(*file));
	memcpy(file->name, name.ptr, name.len);
	file->name[name.len] = 0;
	file->namelen = name.len;

	file->diri = diri;
	hlist_add_after(&file->diri_files_list, *after);
	*after = &file->diri_files_list.next;

	return file;
}

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

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

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

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

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

	return file;
}

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

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

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

526
struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package *pkg)
527
528
{
	struct apk_package *idb;
529
	struct apk_dependency *dep;
530

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

534
535
536
537
538
	/* 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
539
	idb = apk_hash_get(&db->available.packages, APK_BLOB_CSUM(pkg->csum));
540
541
542
	if (idb == NULL) {
		idb = pkg;
		apk_hash_insert(&db->available.packages, pkg);
543
		add_provider(pkg->name, APK_PROVIDER_FROM_PACKAGE(pkg));
544
		foreach_array_item(dep, pkg->provides)
545
			add_provider(dep->name, APK_PROVIDER_FROM_PROVIDES(pkg, dep));
Timo Teräs's avatar
Timo Teräs committed
546
		apk_db_pkg_rdepends(db, pkg);
547
548
	} else {
		idb->repos |= pkg->repos;
549
550
551
552
		if (idb->filename == NULL && pkg->filename != NULL) {
			idb->filename = pkg->filename;
			pkg->filename = NULL;
		}
553
554
555
556
557
		if (idb->ipkg == NULL && pkg->ipkg != NULL) {
			idb->ipkg = pkg->ipkg;
			idb->ipkg->pkg = idb;
			pkg->ipkg = NULL;
		}
558
559
560
561
562
		apk_pkg_free(pkg);
	}
	return idb;
}

563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
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)
580
{
581
	/* APKINDEX.12345678.tar.gz */
582
	apk_blob_push_blob(&to, APK_BLOB_STR("APKINDEX."));
583
	apk_blob_push_hexdump(&to, APK_BLOB_PTR_LEN((char *) repo->csum.data, APK_CACHE_CSUM_BYTES));
584
	apk_blob_push_blob(&to, APK_BLOB_STR(".tar.gz"));
585
	apk_blob_push_blob(&to, APK_BLOB_PTR_LEN("", 1));
586
587
588
	if (APK_BLOB_IS_NULL(to))
		return -ENOBUFS;
	return 0;
589
590
}

591
592
int apk_repo_format_real_url(struct apk_database *db, struct apk_repository *repo,
			     struct apk_package *pkg, char *buf, size_t len)
593
594
595
{
	int r;

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

609
610
611
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
612
	if (repo->url == apk_linked_cache_dir) {
613
614
615
616
617
618
619
		*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);
	}
}
620

621
int apk_cache_download(struct apk_database *db, struct apk_repository *repo,
622
623
		       struct apk_package *pkg, int verify,
		       apk_progress_cb cb, void *cb_ctx)
624
625
626
627
{
	struct apk_istream *is;
	struct apk_bstream *bs;
	struct apk_sign_ctx sctx;
628
629
630
	char url[PATH_MAX];
	char tmpcacheitem[128], *cacheitem = &tmpcacheitem[tmpprefix.len];
	apk_blob_t b = APK_BLOB_BUF(tmpcacheitem);
631
632
	int r, fd;

633
	apk_blob_push_blob(&b, tmpprefix);
634
	if (pkg != NULL)
635
		r = apk_pkg_format_cache_pkg(b, pkg);
636
	else
637
		r = apk_repo_format_cache_index(b, repo);
638
639
640
	if (r < 0)
		return r;

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

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

	if (apk_flags & APK_SIMULATE)
		return 0;
649

650
651
652
	if (cb)
		cb(cb_ctx, 0);

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

676
677
	if (renameat(db->cache_fd, tmpcacheitem, db->cache_fd, cacheitem) < 0)
		return -errno;
678

679
	return 0;
680
681
}

682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
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
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
int apk_db_read_overlay(struct apk_database *db, struct apk_bstream *bs)
{
	struct apk_db_dir_instance *diri = NULL;
	struct hlist_node **diri_node = NULL, **file_diri_node = NULL;
	struct apk_package *pkg;
	struct apk_installed_package *ipkg;
	apk_blob_t token = APK_BLOB_STR("\n"), line, bdir, bfile;

	pkg = apk_pkg_new();
	if (pkg == NULL)
		return -1;

	ipkg = apk_pkg_install(db, pkg);
	if (ipkg == NULL)
		return -1;

	diri_node = hlist_tail_ptr(&ipkg->owned_dirs);

	while (!APK_BLOB_IS_NULL(line = bs->read(bs, token))) {
		if (!apk_blob_rsplit(line, '/', &bdir, &bfile))
			break;

		if (bfile.len == 0) {
			diri = apk_db_diri_new(db, pkg, bdir, &diri_node);
			file_diri_node = &diri->owned_files.first;
		} else {
733
734
			diri = find_diri(ipkg, bdir, diri, &file_diri_node);
			if (diri == NULL) {
735
736
				diri = apk_db_diri_new(db, pkg, bdir, &diri_node);
				file_diri_node = &diri->owned_files.first;
737
			}
Timo Teräs's avatar
Timo Teräs committed
738
			(void) apk_db_file_get(db, diri, bfile, &file_diri_node);
Timo Teräs's avatar
Timo Teräs committed
739
740
741
742
743
744
		}
	}

	return 0;
}

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

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

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

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

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

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

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

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

Timo Teräs's avatar
Timo Teräs committed
892
static int apk_db_write_fdb(struct apk_database *db, struct apk_ostream *os)
893
{
894
	struct apk_installed_package *ipkg;
895
	struct apk_package *pkg;
896
	struct apk_db_dir_instance *diri;
897
	struct apk_db_file *file;
898
	struct hlist_node *c1, *c2;
899
	char buf[1024];
900
901
	apk_blob_t bbuf = APK_BLOB_BUF(buf);
	int r;
902

903
904
	list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
		pkg = ipkg->pkg;
905
906
907
		r = apk_pkg_write_index_entry(pkg, os);
		if (r < 0)
			return r;
908

909
910
		if (ipkg->replaces->num) {
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("r:"));
911
			apk_blob_push_deps(&bbuf, db, ipkg->replaces);
912
913
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
		}
914
915
916
917
918
		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"));
		}
919
		if (ipkg->repository_tag) {
920
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("s:"));
921
			apk_blob_push_blob(&bbuf, db->repo_tags[ipkg->repository_tag].plain_name);
922
923
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
		}
924
		hlist_for_each_entry(diri, c1, &ipkg->owned_dirs, pkg_dirs_list) {
925
926
927
928
929
930
931
932
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("F:"));
			apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN(diri->dir->name, diri->dir->namelen));
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nM:"));
			apk_blob_push_uint(&bbuf, diri->uid, 10);
			apk_blob_push_blob(&bbuf, APK_BLOB_STR(":"));
			apk_blob_push_uint(&bbuf, diri->gid, 10);
			apk_blob_push_blob(&bbuf, APK_BLOB_STR(":"));
			apk_blob_push_uint(&bbuf, diri->mode, 8);
933
934
935
936
937
938
939
			if (ipkg->broken_files || ipkg->broken_script) {
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nf:"));
				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
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
941

942
			hlist_for_each_entry(file, c2, &diri->owned_files, diri_files_list) {
943
944
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("R:"));
				apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN(file->name, file->namelen));
945
946
947
948
949
950
951
952
				if (file->mode != 0 || file->uid != 0 || file->gid != 0) {
					apk_blob_push_blob(&bbuf, APK_BLOB_STR("\na:"));
					apk_blob_push_uint(&bbuf, file->uid, 10);
					apk_blob_push_blob(&bbuf, APK_BLOB_STR(":"));
					apk_blob_push_uint(&bbuf, file->gid, 10);
					apk_blob_push_blob(&bbuf, APK_BLOB_STR(":"));
					apk_blob_push_uint(&bbuf, file->mode, 8);
				}
Timo Teräs's avatar
Timo Teräs committed
953
				if (file->csum.type != APK_CHECKSUM_NONE) {
954
					apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nZ:"));
Timo Teräs's avatar
Timo Teräs committed
955
					apk_blob_push_csum(&bbuf, &file->csum);
956
				}
957
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
958

959
				if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
960
					return -EIO;
961
				bbuf = APK_BLOB_BUF(buf);
962
			}
963
			if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
964
				return -EIO;
965
			bbuf = APK_BLOB_BUF(buf);
966
		}
Timo Teräs's avatar
Timo Teräs committed
967
		os->write(os, "\n", 1);
968
969
970
971
972
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
973
static int apk_db_scriptdb_write(struct apk_database *db, struct apk_ostream *os)
974
{
975
	struct apk_installed_package *ipkg;
976
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
977
978
979
	struct apk_file_info fi;
	char filename[256];
	apk_blob_t bfn;
980
	int r, i;
981
	time_t now = time(NULL);
982
983
984
985
986
987
988

	list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
		pkg = ipkg->pkg;

		for (i = 0; i < APK_SCRIPT_MAX; i