database.c 57.1 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-2009 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
32

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

37
38
39
40
41
42
43
44
#if defined(__x86_64__)
#define APK_DEFAULT_ARCH	"x86_64"
#elif defined(__i386__)
#define APK_DEFAULT_ARCH	"x86"
#else
#define APK_DEFAULT_ARCH	"noarch"
#endif

45
46
47
48
49
enum {
	APK_DISALLOW_RMDIR = 0,
	APK_ALLOW_RMDIR = 1
};

Natanael Copa's avatar
Natanael Copa committed
50
51
int apk_verbosity = 1;
unsigned int apk_flags = 0;
52
const char *apk_arch = APK_DEFAULT_ARCH;
53

54
const char * const apkindex_tar_gz = "APKINDEX.tar.gz";
Timo Teräs's avatar
Timo Teräs committed
55
56
57
static const char * const apk_static_cache_dir = "var/lib/apk";
static const char * const apk_linked_cache_dir = "etc/apk/cache";

58
59
60
struct install_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
61
	struct apk_installed_package *ipkg;
62
63

	int script;
64
	char **script_args;
65
	int script_pending : 1;
66

67
	struct apk_db_dir_instance *diri;
Timo Teräs's avatar
Timo Teräs committed
68
	struct apk_checksum data_csum;
69
	struct apk_sign_ctx sctx;
70
	struct apk_name_array *replaces;
71

72
73
74
75
76
	apk_progress_cb cb;
	void *cb_ctx;
	size_t installed_size;
	size_t current_file_size;

77
78
	struct hlist_node **diri_node;
	struct hlist_node **file_diri_node;
79
80
};

81
static apk_blob_t pkg_name_get_key(apk_hash_item item)
82
{
83
	return APK_BLOB_STR(((struct apk_name *) item)->name);
84
85
}

86
87
88
static void pkg_name_free(struct apk_name *name)
{
	free(name->name);
Timo Teräs's avatar
Timo Teräs committed
89
90
	apk_package_array_free(&name->pkgs);
	apk_name_array_free(&name->rdepends);
Timo Teräs's avatar
Timo Teräs committed
91
	apk_name_array_free(&name->rinstall_if);
92
93
94
	free(name);
}

95
96
97
static const struct apk_hash_ops pkg_name_hash_ops = {
	.node_offset = offsetof(struct apk_name, hash_node),
	.get_key = pkg_name_get_key,
98
99
	.hash_key = apk_blob_hash,
	.compare = apk_blob_compare,
100
	.delete_item = (apk_hash_delete_f) pkg_name_free,
101
102
};

103
static apk_blob_t pkg_info_get_key(apk_hash_item item)
104
{
Timo Teräs's avatar
Timo Teräs committed
105
	return APK_BLOB_CSUM(((struct apk_package *) item)->csum);
106
107
}

108
static unsigned long csum_hash(apk_blob_t csum)
109
{
110
111
	/* Checksum's highest bits have the most "randomness", use that
	 * directly as hash */
112
	return *(unsigned long *) csum.ptr;
113
114
115
116
117
}

static const struct apk_hash_ops pkg_info_hash_ops = {
	.node_offset = offsetof(struct apk_package, hash_node),
	.get_key = pkg_info_get_key,
118
119
	.hash_key = csum_hash,
	.compare = apk_blob_compare,
120
121
122
	.delete_item = (apk_hash_delete_f) apk_pkg_free,
};

123
static apk_blob_t apk_db_dir_get_key(apk_hash_item item)
124
{
Timo Teräs's avatar
Timo Teräs committed
125
126
	struct apk_db_dir *dir = (struct apk_db_dir *) item;
	return APK_BLOB_PTR_LEN(dir->name, dir->namelen);
127
128
129
130
131
}

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,
132
133
	.hash_key = apk_blob_hash,
	.compare = apk_blob_compare,
134
135
136
	.delete_item = (apk_hash_delete_f) free,
};

137
138
139
140
141
142
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)
143
{
144
145
	struct apk_db_file_hash_key *key = (struct apk_db_file_hash_key *) _key.ptr;

Timo Teräs's avatar
Timo Teräs committed
146
	return apk_blob_hash_seed(key->filename, apk_blob_hash(key->dirname));
147
148
149
150
151
152
}

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
153
154
	return apk_blob_hash_seed(APK_BLOB_PTR_LEN(dbf->name, dbf->namelen),
				  dbf->diri->dir->hash);
155
156
157
158
159
160
}

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
161
	struct apk_db_dir *dir = dbf->diri->dir;
162
163
	int r;

Timo Teräs's avatar
Timo Teräs committed
164
165
	r = apk_blob_compare(key->filename,
			     APK_BLOB_PTR_LEN(dbf->name, dbf->namelen));
166
167
168
	if (r != 0)
		return r;

Timo Teräs's avatar
Timo Teräs committed
169
170
171
	r = apk_blob_compare(key->dirname,
			     APK_BLOB_PTR_LEN(dir->name, dir->namelen));
	return r;
172
173
174
175
}

static const struct apk_hash_ops file_hash_ops = {
	.node_offset = offsetof(struct apk_db_file, hash_node),
176
177
178
	.hash_key = apk_db_file_hash_key,
	.hash_item = apk_db_file_hash_item,
	.compare_item = apk_db_file_compare_item,
179
180
181
	.delete_item = (apk_hash_delete_f) free,
};

Timo Teräs's avatar
Timo Teräs committed
182
183
184
185
186
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);
}

187
struct apk_name *apk_db_get_name(struct apk_database *db, apk_blob_t name)
188
189
{
	struct apk_name *pn;
190
	unsigned long hash = apk_hash_from_key(&db->available.names, name);
191

192
	pn = (struct apk_name *) apk_hash_get_hashed(&db->available.names, name, hash);
193
194
195
196
197
198
199
	if (pn != NULL)
		return pn;

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

200
	pn->name = apk_blob_cstr(name);
201
	pn->id = db->name_id++;
Timo Teräs's avatar
Timo Teräs committed
202
203
	apk_package_array_init(&pn->pkgs);
	apk_name_array_init(&pn->rdepends);
Timo Teräs's avatar
Timo Teräs committed
204
	apk_name_array_init(&pn->rinstall_if);
205
	apk_hash_insert_hashed(&db->available.names, pn, hash);
206
207
208
209

	return pn;
}

210
211
static void apk_db_dir_unref(struct apk_database *db, struct apk_db_dir *dir,
			     int allow_rmdir)
212
213
214
215
216
217
{
	dir->refs--;
	if (dir->refs > 0)
		return;

	db->installed.stats.dirs--;
218
219
	if (allow_rmdir)
		unlinkat(db->root_fd, dir->name, AT_REMOVEDIR);
220
221

	if (dir->parent != NULL)
222
		apk_db_dir_unref(db, dir->parent, allow_rmdir);
223
224
}

225
static struct apk_db_dir *apk_db_dir_ref(struct apk_db_dir *dir)
226
227
228
229
230
{
	dir->refs++;
	return dir;
}

231
232
struct apk_db_dir *apk_db_dir_query(struct apk_database *db,
				    apk_blob_t name)
233
234
235
236
{
	return (struct apk_db_dir *) apk_hash_get(&db->installed.dirs, name);
}

237
238
static struct apk_db_dir *apk_db_dir_get(struct apk_database *db,
					 apk_blob_t name)
239
240
241
{
	struct apk_db_dir *dir;
	apk_blob_t bparent;
242
	unsigned long hash = apk_hash_from_key(&db->installed.dirs, name);
243
	int i;
244

245
	if (name.len && name.ptr[name.len-1] == '/')
246
247
		name.len--;

248
	dir = (struct apk_db_dir *) apk_hash_get_hashed(&db->installed.dirs, name, hash);
249
	if (dir != NULL)
250
		return apk_db_dir_ref(dir);
251

252
	db->installed.stats.dirs++;
253
254
	dir = malloc(sizeof(*dir) + name.len + 1);
	memset(dir, 0, sizeof(*dir));
255
	dir->refs = 1;
256
	dir->rooted_name[0] = '/';
Timo Teräs's avatar
Timo Teräs committed
257
258
259
260
	memcpy(dir->name, name.ptr, name.len);
	dir->name[name.len] = 0;
	dir->namelen = name.len;
	dir->hash = hash;
261
	apk_hash_insert_hashed(&db->installed.dirs, dir, hash);
262

263
264
	if (name.len == 0)
		dir->parent = NULL;
265
	else if (apk_blob_rsplit(name, '/', &bparent, NULL))
266
		dir->parent = apk_db_dir_get(db, bparent);
267
	else
268
		dir->parent = apk_db_dir_get(db, APK_BLOB_NULL);
269

270
271
272
273
	if (dir->parent != NULL)
		dir->flags = dir->parent->flags;

	for (i = 0; i < db->protected_paths->num; i++) {
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
		int flags = dir->flags, j;

		flags |= APK_DBDIRF_PROTECTED;
		for (j = 0; ; j++) {
			switch (db->protected_paths->item[i][j]) {
			case '-':
				flags &= ~(APK_DBDIRF_PROTECTED |
					   APK_DBDIRF_SYMLINKS_ONLY);
				continue;
			case '*':
				flags |= APK_DBDIRF_SYMLINKS_ONLY |
					 APK_DBDIRF_PROTECTED;
				continue;
			}
			break;
		}

		if (strcmp(&db->protected_paths->item[i][j], dir->name) == 0)
			dir->flags = flags;
293
294
	}

295
296
297
	return dir;
}

298
299
300
static struct apk_db_dir_instance *apk_db_diri_new(struct apk_database *db,
						   struct apk_package *pkg,
						   apk_blob_t name,
301
						   struct hlist_node ***after)
302
303
304
305
{
	struct apk_db_dir_instance *diri;

	diri = calloc(1, sizeof(struct apk_db_dir_instance));
306
	if (diri != NULL) {
307
308
		hlist_add_after(&diri->pkg_dirs_list, *after);
		*after = &diri->pkg_dirs_list.next;
309
		diri->dir = apk_db_dir_get(db, name);
310
311
		diri->pkg = pkg;
	}
312
313
314
315
316
317
318
319
320
321
322
323

	return diri;
}

static void apk_db_diri_set(struct apk_db_dir_instance *diri, mode_t mode,
			    uid_t uid, gid_t gid)
{
	diri->mode = mode;
	diri->uid = uid;
	diri->gid = gid;
}

324
static void apk_db_diri_mkdir(struct apk_database *db, struct apk_db_dir_instance *diri)
325
{
326
327
328
329
	if (mkdirat(db->root_fd, diri->dir->name, diri->mode) == 0) {
		if (fchownat(db->root_fd, diri->dir->name, diri->uid, diri->gid, 0) != 0)
			;
	}
330
331
}

332
static void apk_db_diri_free(struct apk_database *db,
333
334
			     struct apk_db_dir_instance *diri,
			     int allow_rmdir)
335
{
336
	apk_db_dir_unref(db, diri->dir, allow_rmdir);
337
338
339
	free(diri);
}

340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
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;

	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
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
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;
}

377
static struct apk_db_file *apk_db_file_get(struct apk_database *db,
378
					   struct apk_db_dir_instance *diri,
379
380
					   apk_blob_t name,
					   struct hlist_node ***after)
381
382
{
	struct apk_db_file *file;
383
	struct apk_db_file_hash_key key;
Timo Teräs's avatar
Timo Teräs committed
384
385
	struct apk_db_dir *dir = diri->dir;
	unsigned long hash;
386
387

	key = (struct apk_db_file_hash_key) {
Timo Teräs's avatar
Timo Teräs committed
388
		.dirname = APK_BLOB_PTR_LEN(dir->name, dir->namelen),
389
390
		.filename = name,
	};
391

Timo Teräs's avatar
Timo Teräs committed
392
393
394
	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);
395
396
397
	if (file != NULL)
		return file;

Timo Teräs's avatar
Timo Teräs committed
398
	file = apk_db_file_new(diri, name, after);
Timo Teräs's avatar
Timo Teräs committed
399
	apk_hash_insert_hashed(&db->installed.files, file, hash);
400
	db->installed.stats.files++;
401
402
403
404

	return file;
}

Timo Teräs's avatar
Timo Teräs committed
405
406
407
static void apk_db_pkg_rdepends(struct apk_database *db, struct apk_package *pkg)
{
	int i, j;
Timo Teräs's avatar
Timo Teräs committed
408
	struct apk_name *rname;
Timo Teräs's avatar
Timo Teräs committed
409
410

	for (i = 0; i < pkg->depends->num; i++) {
Timo Teräs's avatar
Timo Teräs committed
411
		rname = pkg->depends->item[i].name;
Timo Teräs's avatar
Timo Teräs committed
412
413
		for (j = 0; j < rname->rdepends->num; j++)
			if (rname->rdepends->item[j] == pkg->name)
Timo Teräs's avatar
Timo Teräs committed
414
				goto rdeps_done;
Timo Teräs's avatar
Timo Teräs committed
415
416
		*apk_name_array_add(&rname->rdepends) = pkg->name;
	}
Timo Teräs's avatar
Timo Teräs committed
417
418
419
420
421
422
423
424
425
426
rdeps_done:
	for (i = 0; i < pkg->install_if->num; i++) {
		rname = pkg->install_if->item[i].name;
		for (j = 0; j < rname->rinstall_if->num; j++)
			if (rname->rinstall_if->item[j] == pkg->name)
				goto riif_done;
		*apk_name_array_add(&rname->rinstall_if) = pkg->name;
	}
riif_done:
	return;
Timo Teräs's avatar
Timo Teräs committed
427
428
}

429
struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package *pkg)
430
431
432
{
	struct apk_package *idb;

433
	if (pkg->license == NULL)
434
		pkg->license = apk_blob_atomize(APK_BLOB_NULL);
435

Timo Teräs's avatar
Timo Teräs committed
436
	idb = apk_hash_get(&db->available.packages, APK_BLOB_CSUM(pkg->csum));
437
438
439
440
	if (idb == NULL) {
		idb = pkg;
		apk_hash_insert(&db->available.packages, pkg);
		*apk_package_array_add(&pkg->name->pkgs) = pkg;
Timo Teräs's avatar
Timo Teräs committed
441
		apk_db_pkg_rdepends(db, pkg);
442
443
	} else {
		idb->repos |= pkg->repos;
444
445
446
447
		if (idb->filename == NULL && pkg->filename != NULL) {
			idb->filename = pkg->filename;
			pkg->filename = NULL;
		}
448
449
450
451
452
		if (idb->ipkg == NULL && pkg->ipkg != NULL) {
			idb->ipkg = pkg->ipkg;
			idb->ipkg->pkg = idb;
			pkg->ipkg = NULL;
		}
453
454
455
456
457
		apk_pkg_free(pkg);
	}
	return idb;
}

458
void apk_cache_format_index(apk_blob_t to, struct apk_repository *repo)
459
{
460
	/* APKINDEX.12345678.tar.gz */
461
	apk_blob_push_blob(&to, APK_BLOB_STR("APKINDEX."));
462
463
	apk_blob_push_hexdump(&to, APK_BLOB_PTR_LEN((char *) repo->csum.data,
						    APK_CACHE_CSUM_BYTES));
464
	apk_blob_push_blob(&to, APK_BLOB_STR(".tar.gz"));
465
466
467
	apk_blob_push_blob(&to, APK_BLOB_PTR_LEN("", 1));
}

468
int apk_cache_download(struct apk_database *db, const char *url, apk_blob_t *arch,
469
470
471
472
473
		       const char *item, const char *cacheitem, int verify)
{
	char fullurl[PATH_MAX];
	int r;

474
475
476
477
478
479
480
481
	if (arch != NULL)
		snprintf(fullurl, sizeof(fullurl), "%s%s" BLOB_FMT "/%s",
			 url, url[strlen(url)-1] == '/' ? "" : "/",
			 BLOB_PRINTF(*arch), item);
	else
		snprintf(fullurl, sizeof(fullurl), "%s%s/%s",
			 url, url[strlen(url)-1] == '/' ? "" : "/",
			 item);
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
	apk_message("fetch %s", fullurl);

	if (apk_flags & APK_SIMULATE)
		return 0;

	r = apk_url_download(fullurl, db->cachetmp_fd, cacheitem);
	if (r < 0)
		return r;

	if (verify != APK_SIGN_NONE) {
		struct apk_istream *is;
		struct apk_sign_ctx sctx;

		apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL, db->keys_fd);
		is = apk_bstream_gunzip_mpart(
			apk_bstream_from_file(db->cachetmp_fd, cacheitem),
			apk_sign_ctx_mpart_cb, &sctx);

500
		r = apk_tar_parse(is, apk_sign_ctx_verify_tar, &sctx, FALSE, &db->id_cache);
501
502
503
504
505
506
507
		is->close(is);
		apk_sign_ctx_free(&sctx);
		if (r != 0) {
			unlinkat(db->cachetmp_fd, cacheitem, 0);
			return r;
		}
	}
508

509
510
511
512
	if (db->cachetmp_fd != db->cache_fd) {
		if (renameat(db->cachetmp_fd, cacheitem, db->cache_fd, cacheitem) < 0)
			return -errno;
	}
513

514
	return 0;
515
516
}

517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
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
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
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;
	struct apk_db_file *file;
	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 {
569
570
			diri = find_diri(ipkg, bdir, diri, &file_diri_node);
			if (diri == NULL) {
571
572
				diri = apk_db_diri_new(db, pkg, bdir, &diri_node);
				file_diri_node = &diri->owned_files.first;
573
			}
Timo Teräs's avatar
Timo Teräs committed
574
575
576
577
578
579
580
			file = apk_db_file_get(db, diri, bfile, &file_diri_node);
		}
	}

	return 0;
}

581
int apk_db_index_read(struct apk_database *db, struct apk_bstream *bs, int repo)
582
583
{
	struct apk_package *pkg = NULL;
584
	struct apk_installed_package *ipkg = NULL;
585
	struct apk_db_dir_instance *diri = NULL;
586
	struct apk_db_file *file = NULL;
587
588
	struct hlist_node **diri_node = NULL;
	struct hlist_node **file_diri_node = NULL;
589
	apk_blob_t token = APK_BLOB_STR("\n"), l;
590
	int field, r;
591

592
593
594
595
	while (!APK_BLOB_IS_NULL(l = bs->read(bs, token))) {
		if (l.len < 2 || l.ptr[1] != ':') {
			if (pkg == NULL)
				continue;
596

597
			if (repo >= 0)
598
				pkg->repos |= BIT(repo);
599

600
			if (apk_db_pkg_add(db, pkg) == NULL) {
601
602
				apk_error("Installed database load failed");
				return -1;
603
			}
604
			pkg = NULL;
605
			ipkg = NULL;
606
607
			continue;
		}
608

609
610
611
612
613
614
615
616
		/* Get field */
		field = l.ptr[0];
		l.ptr += 2;
		l.len -= 2;

		/* If no package, create new */
		if (pkg == NULL) {
			pkg = apk_pkg_new();
617
			ipkg = NULL;
618
619
620
			diri = NULL;
			file_diri_node = NULL;
		}
621

622
		/* Standard index line? */
623
624
625
626
627
628
629
630
631
		r = apk_pkg_add_info(db, pkg, field, l);
		if (r == 0) {
			if (repo == -1 && field == 'S') {
				/* 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);
			}
632
633
			continue;
		}
634
635
		if (repo != -1 || ipkg == NULL)
			continue;
636

637
638
639
640
641
		/* Check FDB special entries */
		switch (field) {
		case 'F':
			if (pkg->name == NULL) {
				apk_error("FDB directory entry before package entry");
642
643
				return -1;
			}
644
645
646
647
648
649
650
651
			diri = apk_db_diri_new(db, pkg, l, &diri_node);
			file_diri_node = &diri->owned_files.first;
			break;
		case 'M':
			if (diri == NULL) {
				apk_error("FDB directory metadata entry before directory entry");
				return -1;
			}
652
653
654
655
656
			diri->uid = apk_blob_pull_uint(&l, 10);
			apk_blob_pull_char(&l, ':');
			diri->gid = apk_blob_pull_uint(&l, 10);
			apk_blob_pull_char(&l, ':');
			diri->mode = apk_blob_pull_uint(&l, 8);
657
658
659
660
661
662
			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
663
			file = apk_db_file_get(db, diri, l, &file_diri_node);
664
665
666
667
668
669
			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
670
			apk_blob_pull_csum(&l, &file->csum);
671
672
			break;
		default:
673
674
675
676
677
678
679
680
			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;
681
		}
682
683
684
685
		if (APK_BLOB_IS_NULL(l)) {
			apk_error("FDB format error in entry '%c'", field);
			return -1;
		}
686
687
	}

688
	return 0;
689
690
}

Timo Teräs's avatar
Timo Teräs committed
691
static int apk_db_write_fdb(struct apk_database *db, struct apk_ostream *os)
692
{
693
	struct apk_installed_package *ipkg;
694
	struct apk_package *pkg;
695
	struct apk_db_dir_instance *diri;
696
	struct apk_db_file *file;
697
	struct hlist_node *c1, *c2;
698
	char buf[1024];
699
700
	apk_blob_t bbuf = APK_BLOB_BUF(buf);
	int r;
701

702
703
	list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
		pkg = ipkg->pkg;
704
705
706
		r = apk_pkg_write_index_entry(pkg, os);
		if (r < 0)
			return r;
707

708
		hlist_for_each_entry(diri, c1, &ipkg->owned_dirs, pkg_dirs_list) {
709
710
711
712
713
714
715
716
717
			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"));
718

719
			hlist_for_each_entry(file, c2, &diri->owned_files, diri_files_list) {
720
721
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("R:"));
				apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN(file->name, file->namelen));
Timo Teräs's avatar
Timo Teräs committed
722
				if (file->csum.type != APK_CHECKSUM_NONE) {
723
					apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nZ:"));
Timo Teräs's avatar
Timo Teräs committed
724
					apk_blob_push_csum(&bbuf, &file->csum);
725
				}
726
				apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
727

728
				if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
729
					return -1;
730
				bbuf = APK_BLOB_BUF(buf);
731
			}
732
			if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
733
				return -1;
734
			bbuf = APK_BLOB_BUF(buf);
735
		}
Timo Teräs's avatar
Timo Teräs committed
736
		os->write(os, "\n", 1);
737
738
739
740
741
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
742
static int apk_db_scriptdb_write(struct apk_database *db, struct apk_ostream *os)
743
{
744
	struct apk_installed_package *ipkg;
745
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
746
747
748
	struct apk_file_info fi;
	char filename[256];
	apk_blob_t bfn;
749
	int r, i;
750
	time_t now = time(NULL);
751
752
753
754
755
756
757

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

Timo Teräs's avatar
Timo Teräs committed
759
760
			fi = (struct apk_file_info) {
				.name = filename,
761
				.size = ipkg->script[i].len,
Timo Teräs's avatar
Timo Teräs committed
762
				.mode = 0755 | S_IFREG,
763
				.mtime = now,
Timo Teräs's avatar
Timo Teräs committed
764
765
766
767
768
769
			};
			/* 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("-"));
770
			apk_blob_push_blob(&bfn, *pkg->version);
Timo Teräs's avatar
Timo Teräs committed
771
772
773
			apk_blob_push_blob(&bfn, APK_BLOB_STR("."));
			apk_blob_push_csum(&bfn, &pkg->csum);
			apk_blob_push_blob(&bfn, APK_BLOB_STR("."));
774
			apk_blob_push_blob(&bfn, APK_BLOB_STR(apk_script_types[i]));
Timo Teräs's avatar
Timo Teräs committed
775
776
			apk_blob_push_blob(&bfn, APK_BLOB_PTR_LEN("", 1));

777
			r = apk_tar_write_entry(os, &fi, ipkg->script[i].ptr);
Timo Teräs's avatar
Timo Teräs committed
778
779
			if (r < 0)
				return r;
780
781
782
		}
	}

783
	return apk_tar_write_entry(os, NULL, NULL);
784
785
}

Timo Teräs's avatar
Timo Teräs committed
786
static int apk_db_scriptdb_read_v1(struct apk_database *db, struct apk_istream *is)
787
788
{
	struct apk_package *pkg;
Timo Teräs's avatar
Timo Teräs committed
789
790
791
792
793
794
	struct {
		unsigned char md5sum[16];
		unsigned int type;
		unsigned int size;
	} hdr;
	struct apk_checksum csum;
795

796
	while (is->read(is, &hdr, sizeof(hdr)) == sizeof(hdr)) {
Timo Teräs's avatar
Timo Teräs committed
797
798
799
800
		memcpy(csum.data, hdr.md5sum, sizeof(hdr.md5sum));
		csum.type = APK_CHECKSUM_MD5;

		pkg = apk_db_get_pkg(db, &csum);
801
802
		if (pkg != NULL && pkg->ipkg != NULL)
			apk_ipkg_add_script(pkg->ipkg, is, hdr.type, hdr.size);
Timo Teräs's avatar
Timo Teräs committed
803
804
		else
			apk_istream_skip(is, hdr.size);
805
806
807
808
809
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
static int apk_read_script_archive_entry(void *ctx,
					 const struct apk_file_info *ae,
					 struct apk_istream *is)
{
	struct apk_database *db = (struct apk_database *) ctx;
	struct apk_package *pkg;
	char *fncsum, *fnaction;
	struct apk_checksum csum;
	apk_blob_t blob;
	int type;

	if (!S_ISREG(ae->mode))
		return 0;

	/* The scripts db expects file names in format:
	 * pkgname-version.<hexdump of package checksum>.action */
	fnaction = memrchr(ae->name, '.', strlen(ae->name));
	if (fnaction == NULL || fnaction == ae->name)
		return 0;
	fncsum = memrchr(ae->name, '.', fnaction - ae->name - 1);
	if (fncsum == NULL)
		return 0;
	fnaction++;
	fncsum++;

	/* Parse it */
	type = apk_script_type(fnaction);
	if (type == APK_SCRIPT_INVALID)
		return 0;
	blob = APK_BLOB_PTR_PTR(fncsum, fnaction - 2);
	apk_blob_pull_csum(&blob, &csum);

	/* Attach script */
	pkg = apk_db_get_pkg(db, &csum);
844
845
	if (pkg != NULL && pkg->ipkg != NULL)
		apk_ipkg_add_script(pkg->ipkg, is, type, ae->size);
Timo Teräs's avatar
Timo Teräs committed
846
847
848
849

	return 0;
}

850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
static int parse_triggers(void *ctx, apk_blob_t blob)
{
	struct apk_installed_package *ipkg = ctx;

	if (blob.len == 0)
		return 0;

	*apk_string_array_add(&ipkg->triggers) = apk_blob_cstr(blob);
	return 0;
}

static void apk_db_triggers_write(struct apk_database *db, struct apk_ostream *os)
{
	struct apk_installed_package *ipkg;
	char buf[APK_BLOB_CHECKSUM_BUF];
	apk_blob_t bfn;
	int i;

	list_for_each_entry(ipkg, &db->installed.triggers, trigger_pkgs_list) {
		bfn = APK_BLOB_BUF(buf);
		apk_blob_push_csum(&bfn, &ipkg->pkg->csum);
871
872
		bfn = apk_blob_pushed(APK_BLOB_BUF(buf), bfn);
		os->write(os, bfn.ptr, bfn.len);
873

874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
		for (i = 0; i < ipkg->triggers->num; i++) {
			os->write(os, " ", 1);
			apk_ostream_write_string(os, ipkg->triggers->item[i]);
		}
		os->write(os, "\n", 1);
	}
}

static void apk_db_triggers_read(struct apk_database *db, struct apk_bstream *bs)
{
	struct apk_checksum csum;
	struct apk_package *pkg;
	struct apk_installed_package *ipkg;
	apk_blob_t l;

	while (!APK_BLOB_IS_NULL(l = bs->read(bs, APK_BLOB_STR("\n")))) {
		apk_blob_pull_csum(&l, &csum);
		apk_blob_pull_char(&l, ' ');

		pkg = apk_db_get_pkg(db, &csum);
		if (pkg == NULL || pkg->ipkg == NULL)
			continue;

		ipkg = pkg->ipkg;
		apk_blob_for_each_segment(l, " ", parse_triggers, ipkg);
Timo Teräs's avatar
Timo Teräs committed
899
900
		if (ipkg->triggers->num != 0 &&
		    !list_hashed(&ipkg->trigger_pkgs_list))
901
902
903
904
905
			list_add_tail(&ipkg->trigger_pkgs_list,
				      &db->installed.triggers);
	}
}

Timo Teräs's avatar
Timo Teräs committed
906
static int apk_db_read_state(struct apk_database *db, int flags)
907
{
908
	struct apk_istream *is;
909
	struct apk_bstream *bs;
Timo Teräs's avatar
Timo Teräs committed
910
	apk_blob_t blob;
911
	int i, r;
912
913
914
915
916
917
918
919
920

	/* Read:
	 * 1. installed repository
	 * 2. source repositories
	 * 3. master dependencies
	 * 4. package statuses
	 * 5. files db
	 * 6. script db
	 */
Timo Teräs's avatar
Timo Teräs committed
921
	if (!(flags & APK_OPENF_NO_WORLD)) {
922
		blob = apk_blob_from_file(db->root_fd, "var/lib/apk/world");
Timo Teräs's avatar
Timo Teräs committed
923
924
925
926
		if (APK_BLOB_IS_NULL(blob))
			return -ENOENT;
		apk_deps_parse(db, &db->world, blob);
		free(blob.ptr);
927

Timo Teräs's avatar
Timo Teräs committed
928
		for (i = 0; i < db->world->num; i++)
Timo Teräs's avatar
Timo Teräs committed
929
930
			db->world->item[i].name->flags |= APK_NAME_TOPLEVEL;
	}
931

Timo Teräs's avatar
Timo Teräs committed
932
	if (!(flags & APK_OPENF_NO_INSTALLED)) {
933
		bs = apk_bstream_from_file(db->root_fd, "var/lib/apk/installed");
934
		if (bs != NULL) {
935
			r = apk_db_index_read(db, bs, -1);
936
			bs->close(bs, NULL);
937
938
			if (r != 0)
				return -1;
Timo Teräs's avatar
Timo Teräs committed
939
		}
940

941
942
943
944
945
		bs = apk_bstream_from_file(db->root_fd, "var/lib/apk/triggers");
		if (bs != NULL) {
			apk_db_triggers_read(db, bs);
			bs->close(bs, NULL);
		}
946
947
	}

Timo Teräs's avatar
Timo Teräs committed
948
	if (!(flags & APK_OPENF_NO_SCRIPTS)) {
949
		is = apk_istream_from_file(db->root_fd, "var/lib/apk/scripts.tar");
Timo Teräs's avatar
Timo Teräs committed
950
		if (is != NULL) {
951
			apk_tar_parse(is, apk_read_script_archive_entry, db,
952
				      FALSE, &db->id_cache);
Timo Teräs's avatar
Timo Teräs committed
953
		} else {
954
			is = apk_istream_from_file(db->root_fd, "var/lib/apk/scripts");
Timo Teräs's avatar
Timo Teräs committed
955
956
			if (is != NULL)
				apk_db_scriptdb_read_v1(db, is);
Timo Teräs's avatar
Timo Teräs committed
957
		}
Timo Teräs's avatar
Timo Teräs committed
958
959
		if (is != NULL)
			is->close(is);
960
961
962
963
964
	}

	return 0;
}

965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
struct index_write_ctx {
	struct apk_ostream *os;
	int count;
	int force;
};

static int write_index_entry(apk_hash_item item, void *ctx)
{
	struct index_write_ctx *iwctx = (struct index_write_ctx *) ctx;
	struct apk_package *pkg = (struct apk_package *) item;
	int r;

	if (!iwctx->force && pkg->filename == NULL)
		return 0;

	r = apk_pkg_write_index_entry(pkg, iwctx->os);
	if (r < 0)
		return r;

	if (iwctx->os->write(iwctx->os, "\n", 1) != 1)
		return -1;

	iwctx->count++;
	return 0;
}

static int apk_db_index_write_nr_cache(struct apk_database *db)
{
	struct index_write_ctx ctx = { NULL, 0, TRUE };
994
	struct apk_installed_package *ipkg;
995
996
997
998
999
1000
	struct apk_ostream *os;
	int r;

	if (!apk_db_cache_active(db))
		return 0;

For faster browsing, not all history is shown. View entire blame