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

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

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

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

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

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

49
50
static 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
71
72
73
74

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

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

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

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
	apk_blob_t bparent;
297
	unsigned long hash = apk_hash_from_key(&db->installed.dirs, name);
298
	char *relative_name;
299
	int i;
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
341
342
343
	relative_name = strrchr(dir->rooted_name, '/') + 1;
	for (i = 0; i < ppaths->num; i++) {
		struct apk_protected_path *ppath = &ppaths->item[i];
		char *slash;
344

345
346
347
348
349
		slash = strchr(ppath->relative_pattern, '/');
		if (slash != NULL) {
			*slash = 0;
			if (fnmatch(ppath->relative_pattern, relative_name, FNM_PATHNAME) != 0) {
				*slash = '/';
350
351
				continue;
			}
352
353
354
355
356
357
358
359
360
361
			*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;
362

363
364
365
			dir->protected = ppath->protected;
			dir->symlinks_only = ppath->symlinks_only;
		}
366
		dir->has_protected_children |= ppath->protected;
367
368
	}

369
370
371
	return dir;
}

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

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

	return diri;
}

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

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

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

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

421
	apk_db_dir_unref(db, diri->dir, allow_rmdir);
422
423
424
	free(diri);
}

425
426
427
428
429
430
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;

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

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

465
466
467
468
469
470
471
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;
}

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

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

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

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

	return file;
}

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

505
506
507
508
	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
509
				goto rdeps_done;
Timo Teräs's avatar
Timo Teräs committed
510
		*apk_name_array_add(&rname->rdepends) = pkg->name;
511
rdeps_done: ;
Timo Teräs's avatar
Timo Teräs committed
512
	}
513
514
515
516
	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
517
518
				goto riif_done;
		*apk_name_array_add(&rname->rinstall_if) = pkg->name;
519
riif_done: ;
Timo Teräs's avatar
Timo Teräs committed
520
521
	}
	return;
Timo Teräs's avatar
Timo Teräs committed
522
523
}

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

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

534
	if (pkg->license == NULL)
535
		pkg->license = apk_blob_atomize(APK_BLOB_NULL);
536

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

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

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

601
602
603
604
605
606
607
608
609
610
611
612
	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;
}
613

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

626
627
628
629
630
631
int apk_cache_download(struct apk_database *db, struct apk_repository *repo,
		       struct apk_package *pkg, int verify)
{
	struct apk_istream *is;
	struct apk_bstream *bs;
	struct apk_sign_ctx sctx;
632
633
634
	char url[PATH_MAX];
	char tmpcacheitem[128], *cacheitem = &tmpcacheitem[tmpprefix.len];
	apk_blob_t b = APK_BLOB_BUF(tmpcacheitem);
635
636
	int r, fd;

637
	apk_blob_push_blob(&b, tmpprefix);
638
	if (pkg != NULL)
639
		r = apk_pkg_format_cache_pkg(b, pkg);
640
	else
641
		r = apk_repo_format_cache_index(b, repo);
642
643
644
	if (r < 0)
		return r;

645
646
647
	r = apk_repo_format_real_url(db, repo, pkg, url, sizeof(url));
	if (r < 0)
		return r;
648

649
650
651
652
	apk_message("fetch %s", url);

	if (apk_flags & APK_SIMULATE)
		return 0;
653

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

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

680
	return 0;
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
707
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
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
733
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 {
734
735
			diri = find_diri(ipkg, bdir, diri, &file_diri_node);
			if (diri == NULL) {
736
737
				diri = apk_db_diri_new(db, pkg, bdir, &diri_node);
				file_diri_node = &diri->owned_files.first;
738
			}
Timo Teräs's avatar
Timo Teräs committed
739
			(void) apk_db_file_get(db, diri, bfile, &file_diri_node);
Timo Teräs's avatar
Timo Teräs committed
740
741
742
743
744
745
		}
	}

	return 0;
}

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

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

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

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

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

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

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

879
	return 0;
880
881
}

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

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

899
900
		if (ipkg->replaces->num) {
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("r:"));
901
			apk_blob_push_deps(&bbuf, db, ipkg->replaces);
902
903
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
		}
904
905
906
907
908
		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"));
		}
909
		if (ipkg->repository_tag) {
910
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("s:"));
911
912
913
			apk_blob_push_blob(&bbuf, *db->repo_tags[ipkg->repository_tag].name);
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
		}
914
		hlist_for_each_entry(diri, c1, &ipkg->owned_dirs, pkg_dirs_list) {
915
916
917
918
919
920
921
922
923
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("F:"));
			apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN(diri->dir->name, diri->dir->namelen));
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nM:"));
			apk_blob_push_uint(&bbuf, diri->uid, 10);
			apk_blob_push_blob(&bbuf, APK_BLOB_STR(":"));
			apk_blob_push_uint(&bbuf, diri->gid, 10);
			apk_blob_push_blob(&bbuf, APK_BLOB_STR(":"));
			apk_blob_push_uint(&bbuf, diri->mode, 8);
			apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
924

925
			hlist_for_each_entry(file, c2, &diri->owned_files, diri_files_list) {
926
927
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("R:"));
				apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN(file->name, file->namelen));
928
929
930
931
932
933
934
935
				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
936
				if (file->csum.type != APK_CHECKSUM_NONE) {
937
					apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nZ:"));
Timo Teräs's avatar
Timo Teräs committed
938
					apk_blob_push_csum(&bbuf, &file->csum);
939
				}
940
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
941

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

	return 0;
}

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

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

		for (i = 0; i < APK_SCRIPT_MAX; i++) {
			if (ipkg->script[i].ptr == NULL)
				continue;
972

Timo Teräs's avatar
Timo Teräs committed
973
974
			fi = (struct apk_file_info) {
				.name = filename,
975
				.size = ipkg->script[i].len,
Timo Teräs's avatar
Timo Teräs committed
976
				.mode = 0755 | S_IFREG,
977
				.mtime = now,
Timo Teräs's avatar
Timo Teräs committed
978
979
980
981
982
983
			};
			/* The scripts db expects file names in format:
			 * pkg-version.<hexdump of package checksum>.action */
			bfn = APK_BLOB_BUF(filename);
			apk_blob_push_blob(&bfn, APK_BLOB_STR(pkg->name->name));
			apk_blob_push_blob(&bfn, APK_BLOB_STR("-"));
984
			apk_blob_push_blob(&bfn, *pkg->version);
Timo Teräs's avatar
Timo Teräs committed
985
986
987
			apk_blob_push_blob(&bfn, APK_BLOB_STR("."));
			apk_blob_push_csum(&bfn, &pkg->csum);
			apk_blob_push_blob(&bfn, APK_BLOB_STR("."));
988
			apk_blob_push_blob(&bfn, APK_BLOB_STR(apk_script_types[i]));
Timo Teräs's avatar
Timo Teräs committed
989
990
			apk_blob_push_blob(&bfn, APK_BLOB_PTR_LEN("", 1));

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

997
	return apk_tar_write_entry(os, NULL,