database.c 23.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* database.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
 * Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it 
 * 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 <limits.h>
16
17
18
19
20
21
22
23
#include <unistd.h>
#include <malloc.h>
#include <string.h>

#include "apk_defines.h"
#include "apk_package.h"
#include "apk_database.h"
#include "apk_state.h"
24
#include "apk_applet.h"
25
26
27
28
29
30

struct install_ctx {
	struct apk_database *db;
	struct apk_package *pkg;

	int script;
31
32
33
	struct apk_db_dir_instance *diri;

	struct hlist_node **diri_node;
34
	struct hlist_node **file_dir_node;
35
	struct hlist_node **file_diri_node;
36
37
};

38
static apk_blob_t pkg_name_get_key(apk_hash_item item)
39
{
40
	return APK_BLOB_STR(((struct apk_name *) item)->name);
41
42
43
44
45
}

static const struct apk_hash_ops pkg_name_hash_ops = {
	.node_offset = offsetof(struct apk_name, hash_node),
	.get_key = pkg_name_get_key,
46
47
	.hash_key = apk_blob_hash,
	.compare = apk_blob_compare,
48
49
50
	.delete_item = (apk_hash_delete_f) apk_name_free,
};

51
static apk_blob_t pkg_info_get_key(apk_hash_item item)
52
{
53
	return APK_BLOB_BUF(((struct apk_package *) item)->csum);
54
55
}

56
static unsigned long csum_hash(apk_blob_t csum)
57
{
58
59
	/* Checksum's highest bits have the most "randomness", use that
	 * directly as hash */
60
	return *(unsigned long *) csum.ptr;
61
62
63
64
65
}

static const struct apk_hash_ops pkg_info_hash_ops = {
	.node_offset = offsetof(struct apk_package, hash_node),
	.get_key = pkg_info_get_key,
66
67
	.hash_key = csum_hash,
	.compare = apk_blob_compare,
68
69
70
	.delete_item = (apk_hash_delete_f) apk_pkg_free,
};

71
static apk_blob_t apk_db_dir_get_key(apk_hash_item item)
72
{
73
	return APK_BLOB_STR(((struct apk_db_dir *) item)->dirname);
74
75
76
77
78
}

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,
79
80
	.hash_key = apk_blob_hash,
	.compare = apk_blob_compare,
81
82
83
	.delete_item = (apk_hash_delete_f) free,
};

84
struct apk_name *apk_db_get_name(struct apk_database *db, apk_blob_t name)
85
86
87
88
89
90
91
92
93
94
95
{
	struct apk_name *pn;

	pn = (struct apk_name *) apk_hash_get(&db->available.names, name);
	if (pn != NULL)
		return pn;

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

96
	pn->name = apk_blob_cstr(name);
97
98
99
100
101
102
103
104
	apk_hash_insert(&db->available.names, pn);

	return pn;
}

void apk_name_free(struct apk_name *name)
{
	free(name->name);
105
	free(name->pkgs);
106
107
108
	free(name);
}

109
static void apk_db_dir_put(struct apk_database *db, struct apk_db_dir *dir)
110
111
112
113
114
115
116
117
118
{
	dir->refs--;
	if (dir->refs > 0)
		return;

	db->installed.stats.dirs--;
	rmdir(dir->dirname);

	if (dir->parent != NULL)
119
		apk_db_dir_put(db, dir->parent);
120
121
}

122
123
124
125
126
127
128
129
static struct apk_db_dir *apk_db_dir_get(struct apk_db_dir *dir)
{
	dir->refs++;
	return dir;
}

static struct apk_db_dir *apk_db_dir_get_db(struct apk_database *db,
					    apk_blob_t name)
130
131
132
{
	struct apk_db_dir *dir;
	apk_blob_t bparent;
133
	int i;
134

135
	if (name.len && name.ptr[name.len-1] == '/')
136
137
		name.len--;

138
	dir = (struct apk_db_dir *) apk_hash_get(&db->installed.dirs, name);
139
	if (dir != NULL)
140
		return apk_db_dir_get(dir);
141

142
	db->installed.stats.dirs++;
143
	dir = calloc(1, sizeof(*dir) + name.len + 1);
144
	dir->refs = 1;
145
146
147
148
	memcpy(dir->dirname, name.ptr, name.len);
	dir->dirname[name.len] = 0;
	apk_hash_insert(&db->installed.dirs, dir);

149
150
	if (name.len == 0)
		dir->parent = NULL;
151
	else if (apk_blob_rsplit(name, '/', &bparent, NULL))
152
		dir->parent = apk_db_dir_get_db(db, bparent);
153
	else
154
		dir->parent = apk_db_dir_get_db(db, APK_BLOB_NULL);
155

156
157
158
159
160
161
162
163
164
165
166
	if (dir->parent != NULL)
		dir->flags = dir->parent->flags;

	for (i = 0; i < db->protected_paths->num; i++) {
		if (db->protected_paths->item[i][0] == '-' &&
		    strcmp(&db->protected_paths->item[i][1], dir->dirname) == 0)
			dir->flags &= ~APK_DBDIRF_PROTECTED;
		else if (strcmp(db->protected_paths->item[i], dir->dirname) == 0)
			dir->flags |= APK_DBDIRF_PROTECTED;
	}

167
168
169
	return dir;
}

170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
static struct apk_db_dir_instance *apk_db_diri_new(struct apk_database *db,
						   struct apk_package *pkg,
						   apk_blob_t name,
						   struct hlist_node **after)
{
	struct apk_db_dir_instance *diri;

	diri = calloc(1, sizeof(struct apk_db_dir_instance));
	hlist_add_after(&diri->pkg_dirs_list, after);
	diri->dir = apk_db_dir_get_db(db, name);
	diri->pkg = pkg;

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

static void apk_db_diri_create(struct apk_db_dir_instance *diri)
{
	if (diri->dir->refs == 1) {
		mkdir(diri->dir->dirname, diri->mode);
		chown(diri->dir->dirname, diri->uid, diri->gid);
	}
}

201
202
203
204
205
206
207
static void apk_db_diri_free(struct apk_database *db,
			     struct apk_db_dir_instance *diri)
{
	apk_db_dir_put(db, diri->dir);
	free(diri);
}

208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
static struct apk_db_file *apk_db_file_new(struct apk_db_dir *dir,
					   apk_blob_t name,
					   struct hlist_node **after)
{
	struct apk_db_file *file;

	file = calloc(1, sizeof(*file) + name.len + 1);
	hlist_add_after(&file->dir_files_list, after);
	memcpy(file->filename, name.ptr, name.len);
	file->filename[name.len] = 0;

	return file;
}

static void apk_db_file_set_owner(struct apk_database *db,
				  struct apk_db_file *file,
224
				  struct apk_db_dir_instance *diri,
225
226
				  struct hlist_node **after)
{
227
228
	if (file->diri != NULL) {
		hlist_del(&file->diri_files_list, &file->diri->owned_files);
229
230
231
	} else {
		db->installed.stats.files++;
	}
232
233
	file->diri = diri;
	hlist_add_after(&file->diri_files_list, after);
234
235
236
}

static struct apk_db_file *apk_db_file_get(struct apk_database *db,
237
					   apk_blob_t bfile,
238
239
240
241
242
					   struct install_ctx *ctx)
{
	struct apk_db_dir *dir;
	struct apk_db_file *file;
	struct hlist_node *cur;
243
244

	dir = ctx->diri->dir;
245
246
247
248
249
250
251

	hlist_for_each_entry(file, cur, &dir->files, dir_files_list) {
		if (strncmp(file->filename, bfile.ptr, bfile.len) == 0 &&
		    file->filename[bfile.len] == 0)
			return file;
	}

252
253
	if (ctx->file_dir_node == NULL)
		ctx->file_dir_node = hlist_tail_ptr(&dir->files);
254
255
256
257
258
259
	file = apk_db_file_new(dir, bfile, ctx->file_dir_node);
	ctx->file_dir_node = &file->dir_files_list.next;

	return file;
}

260
261
262
263
264
static void apk_db_file_free(struct apk_db_file *file)
{
	free(file);
}

265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
static struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package *pkg)
{
	struct apk_package *idb;

	idb = apk_hash_get(&db->available.packages, APK_BLOB_BUF(pkg->csum));
	if (idb == NULL) {
		idb = pkg;
		pkg->id = db->pkg_id++;
		apk_hash_insert(&db->available.packages, pkg);
		*apk_package_array_add(&pkg->name->pkgs) = pkg;
	} else {
		idb->repos |= pkg->repos;
		apk_pkg_free(pkg);
	}
	return idb;
}

static int apk_db_index_read(struct apk_database *db, struct apk_istream *is, int repo)
283
284
{
	struct apk_package *pkg = NULL;
285
	struct apk_db_dir_instance *diri = NULL;
286
	struct apk_db_file *file = NULL;
287
	struct hlist_node **diri_node = NULL;
288
	struct hlist_node **file_dir_node = NULL;
289
	struct hlist_node **file_diri_node = NULL;
290
291
292

	char buf[1024];
	apk_blob_t l, r;
293
	int n, field, ret;
294

295
	ret = -1;
296
297
	r = APK_BLOB_PTR_LEN(buf, 0);
	while (1) {
Timo Teräs's avatar
Timo Teräs committed
298
		n = is->read(is, &r.ptr[r.len], sizeof(buf) - r.len);
299
300
301
302
303
		if (n <= 0)
			break;
		r.len += n;

		while (apk_blob_splitstr(r, "\n", &l, &r)) {
304
305
306
307
308
309
310
311
312
313
314
			if (l.len < 2 || l.ptr[1] != ':') {
				if (pkg == NULL)
					continue;

				if (repo != -1)
					pkg->repos |= BIT(repo);
				else
					apk_pkg_set_state(db, pkg, APK_STATE_INSTALL);

				if (apk_db_pkg_add(db, pkg) != pkg && repo == -1) {
					apk_error("Installed database load failed");
315
316
					return -1;
				}
317
318
319
320
321
322
323
324
325
326
327
328
				pkg = NULL;
				continue;
			}

			/* Get field */
			field = l.ptr[0];
			l.ptr += 2;
			l.len -= 2;

			/* If no package, create new */
			if (pkg == NULL) {
				pkg = apk_pkg_new();
329
330
				diri = NULL;
				diri_node = &pkg->owned_dirs.first;
331
				file_dir_node = NULL;
332
				file_diri_node = NULL;
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
			}

			/* Standard index line? */
			if (apk_pkg_add_info(db, pkg, field, l) == 0)
				continue;

			if (repo != -1) {
				apk_error("Invalid index entry '%c'", field);
				return -1;
			}

			/* Check FDB special entries */
			switch (field) {
			case 'F':
				if (pkg->name == NULL) {
348
349
350
					apk_error("FDB directory entry before package entry");
					return -1;
				}
351
352
353
354
				diri = apk_db_diri_new(db, pkg, l, diri_node);
				diri_node = &diri->pkg_dirs_list.next;
				file_dir_node = hlist_tail_ptr(&diri->dir->files);
				file_diri_node = &diri->owned_files.first;
355
				break;
356
			case 'M':
357
				if (diri == NULL) {
358
359
360
					apk_error("FDB directory metadata entry before directory entry");
					return -1;
				}
361
362
				sscanf(l.ptr, "%d:%d:%o",
				       &diri->uid, &diri->gid, &diri->mode);
363
364
				break;
			case 'R':
365
				if (diri == NULL) {
366
367
368
					apk_error("FDB file entry before directory entry");
					return -1;
				}
369
				file = apk_db_file_new(diri->dir, l, file_dir_node);
370
				file_dir_node = &file->dir_files_list.next;
371
372
				apk_db_file_set_owner(db, file, diri, file_diri_node);
				file_diri_node = &file->diri_files_list.next;
373
				break;
374
			case 'Z':
375
376
377
378
379
380
381
382
383
				if (file == NULL) {
					apk_error("FDB checksum entry before file entry");
					return -1;
				}
				if (apk_hexdump_parse(APK_BLOB_BUF(file->csum), l)) {
					apk_error("Not a valid checksum");
					return -1;
				}
				break;
384
385
386
387
388
389
390
391
392
			default:
				apk_error("FDB entry '%c' unsupported", n);
				return -1;
			}
		}

		memcpy(&buf[0], r.ptr, r.len);
		r = APK_BLOB_PTR_LEN(buf, r.len);
	}
393
	ret = 0;
394

395
	return ret;
396
397
}

Timo Teräs's avatar
Timo Teräs committed
398
static int apk_db_write_fdb(struct apk_database *db, struct apk_ostream *os)
399
400
{
	struct apk_package *pkg;
401
	struct apk_db_dir_instance *diri;
402
	struct apk_db_file *file;
403
	struct hlist_node *c1, *c2;
404
	char buf[1024];
405
	apk_blob_t blob;
406
	int n = 0;
407

408
	list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) {
409
410
		blob = apk_pkg_format_index_entry(pkg, sizeof(buf), buf);
		if (blob.ptr)
Timo Teräs's avatar
Timo Teräs committed
411
			os->write(os, blob.ptr, blob.len - 1);
412

413
414
415
416
417
418
		hlist_for_each_entry(diri, c1, &pkg->owned_dirs, pkg_dirs_list) {
			n += snprintf(&buf[n], sizeof(buf)-n,
				      "F:%s\n"
				      "M:%d:%d:%o\n",
				      diri->dir->dirname,
				      diri->uid, diri->gid, diri->mode);
419

420
			hlist_for_each_entry(file, c2, &diri->owned_files, diri_files_list) {
421
				n += snprintf(&buf[n], sizeof(buf)-n,
422
423
424
425
426
427
428
429
					      "R:%s\n",
					      file->filename);
				if (csum_valid(file->csum)) {
					n += snprintf(&buf[n], sizeof(buf)-n, "Z:");
					n += apk_hexdump_format(sizeof(buf)-n, &buf[n],
								APK_BLOB_BUF(file->csum));
					n += snprintf(&buf[n], sizeof(buf)-n, "\n");
				}
430

431
432
433
				if (os->write(os, buf, n) != n)
					return -1;
				n = 0;
434
			}
435
		}
Timo Teräs's avatar
Timo Teräs committed
436
		os->write(os, "\n", 1);
437
438
439
440
441
442
443
444
445
446
447
	}

	return 0;
}

struct apk_script_header {
	csum_t csum;
	unsigned int type;
	unsigned int size;
};

Timo Teräs's avatar
Timo Teräs committed
448
static int apk_db_scriptdb_write(struct apk_database *db, struct apk_ostream *os)
449
450
451
452
{
	struct apk_package *pkg;
	struct apk_script *script;
	struct apk_script_header hdr;
453
	struct hlist_node *c2;
454

455
	list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) {
456
457
458
459
460
		hlist_for_each_entry(script, c2, &pkg->scripts, script_list) {
			memcpy(hdr.csum, pkg->csum, sizeof(csum_t));
			hdr.type = script->type;
			hdr.size = script->size;

Timo Teräs's avatar
Timo Teräs committed
461
462
463
464
465
			if (os->write(os, &hdr, sizeof(hdr)) != sizeof(hdr))
				return -1;

			if (os->write(os, script->script, script->size) != script->size)
				return -1;
466
467
468
469
470
471
		}
	}

	return 0;
}

472
static int apk_db_scriptdb_read(struct apk_database *db, struct apk_istream *is)
473
474
475
476
{
	struct apk_package *pkg;
	struct apk_script_header hdr;

477
	while (is->read(is, &hdr, sizeof(hdr)) == sizeof(hdr)) {
478
		pkg = apk_db_get_pkg(db, hdr.csum);
479
480
		if (pkg != NULL)
			apk_pkg_add_script(pkg, is, hdr.type, hdr.size);
481
482
483
484
485
	}

	return 0;
}

486
int apk_db_create(const char *root)
487
{
488
	apk_blob_t deps = APK_BLOB_STR("busybox, alpine-baselayout, "
489
				       "apk-tools, alpine-conf\n");
490
	int fd;
491

492
493
	fchdir(apk_cwd_fd);
	chdir(root);
494

495
496
497
498
499
500
501
502
	mkdir("tmp", 01777);
	mkdir("dev", 0755);
	mknod("dev/null", 0666, makedev(1, 3));
	mkdir("var", 0755);
	mkdir("var/lib", 0755);
	mkdir("var/lib/apk", 0755);

	fd = creat("var/lib/apk/world", 0600);
503
504
505
506
507
508
	if (fd < 0)
		return -1;
	write(fd, deps.ptr, deps.len);
	close(fd);

	return 0;
509
510
}

511
static int apk_db_read_state(struct apk_database *db)
512
{
513
	struct apk_istream *is;
Timo Teräs's avatar
Timo Teräs committed
514
	apk_blob_t blob;
515
516

	if (db->root == NULL)
517
		return 0;
518
519
520
521
522
523
524
525
526

	/* Read:
	 * 1. installed repository
	 * 2. source repositories
	 * 3. master dependencies
	 * 4. package statuses
	 * 5. files db
	 * 6. script db
	 */
527
528
	fchdir(db->root_fd);

Timo Teräs's avatar
Timo Teräs committed
529
530
	blob = apk_blob_from_file("var/lib/apk/world");
	if (APK_BLOB_IS_NULL(blob)) {
531
		apk_error("Please run 'apk create' to initialize root");
532
		return -1;
533
	}
Timo Teräs's avatar
Timo Teräs committed
534
535
	apk_deps_parse(db, &db->world, blob);
	free(blob.ptr);
536

537
	is = apk_istream_from_file("var/lib/apk/installed");
Timo Teräs's avatar
Timo Teräs committed
538
	if (is != NULL) {
539
		apk_db_index_read(db, is, -1);
Timo Teräs's avatar
Timo Teräs committed
540
		is->close(is);
541
542
	}

543
544
	is = apk_istream_from_file("var/lib/apk/scripts");
	if (is != NULL) {
545
		apk_db_scriptdb_read(db, is);
546
		is->close(is);
547
548
549
550
551
	}

	return 0;
}

552
553
554
555
556
557
558
559
static int add_protected_path(void *ctx, apk_blob_t blob)
{
	struct apk_database *db = (struct apk_database *) ctx;

	*apk_string_array_add(&db->protected_paths) = apk_blob_cstr(blob);
	return 0;
}

560
561
int apk_db_open(struct apk_database *db, const char *root)
{
562
	apk_blob_t blob;
563

564
565
566
567
	memset(db, 0, sizeof(*db));
	apk_hash_init(&db->available.names, &pkg_name_hash_ops, 1000);
	apk_hash_init(&db->available.packages, &pkg_info_hash_ops, 4000);
	apk_hash_init(&db->installed.dirs, &dir_hash_ops, 1000);
568
	list_init(&db->installed.packages);
569
570
571
572
573

	if (root != NULL) {
		db->root = strdup(root);
		db->root_fd = open(root, O_RDONLY);
		if (db->root_fd < 0) {
574
			apk_error("%s: %s", root, strerror(errno));
575
576
577
578
			free(db->root);
			return -1;
		}
	}
579
580
581
582
583
584
585
586
587
588
589
590
591
592

	blob = APK_BLOB_STR("etc:-etc/init.d");
	apk_blob_for_each_segment(blob, ":", add_protected_path, db);

	if (apk_db_read_state(db) != 0)
		return -1;

	fchdir(db->root_fd);
	blob = apk_blob_from_file("etc/apk/repositories");
	if (!APK_BLOB_IS_NULL(blob)) {
		apk_blob_for_each_segment(blob, "\n", apk_db_add_repository, db);
		free(blob.ptr);
	}

Timo Teräs's avatar
Timo Teräs committed
593
	if (apk_repository != NULL)
594
		apk_db_add_repository(db, APK_BLOB_STR(apk_repository));
595

596
	return 0;
597
598
}

599
600
601
602
603
struct write_ctx {
	struct apk_database *db;
	int fd;
};

604
static int apk_db_write_config(struct apk_database *db)
605
{
Timo Teräs's avatar
Timo Teräs committed
606
	struct apk_ostream *os;
607
	char buf[1024];
Timo Teräs's avatar
Timo Teräs committed
608
	int n;
609
610

	if (db->root == NULL)
611
		return 0;
612

613
614
	fchdir(db->root_fd);

Timo Teräs's avatar
Timo Teräs committed
615
616
	os = apk_ostream_to_file("var/lib/apk/world", 0600);
	if (os == NULL)
617
618
		return -1;
	n = apk_deps_format(buf, sizeof(buf), db->world);
Timo Teräs's avatar
Timo Teräs committed
619
620
	os->write(os, buf, n);
	os->close(os);
621

Timo Teräs's avatar
Timo Teräs committed
622
623
	os = apk_ostream_to_file("var/lib/apk/installed", 0600);
	if (os == NULL)
624
		return -1;
Timo Teräs's avatar
Timo Teräs committed
625
626
	apk_db_write_fdb(db, os);
	os->close(os);
627

Timo Teräs's avatar
Timo Teräs committed
628
629
	os = apk_ostream_to_file("var/lib/apk/scripts", 0600);
	if (os == NULL)
630
		return -1;
Timo Teräs's avatar
Timo Teräs committed
631
632
	apk_db_scriptdb_write(db, os);
	os->close(os);
633
634
635
636

	return 0;
}

637
void apk_db_close(struct apk_database *db)
638
{
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
	struct apk_package *pkg;
	struct apk_db_dir_instance *diri;
	struct apk_db_file *file;
	struct hlist_node *dc, *dn, *fc, *fn;
	int i;

	list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) {
		hlist_for_each_entry_safe(diri, dc, dn, &pkg->owned_dirs, pkg_dirs_list) {
			hlist_for_each_entry_safe(file, fc, fn, &diri->owned_files, diri_files_list)
				apk_db_file_free(file);
			apk_db_diri_free(db, diri);
		}
	}


	for (i = 0; i < db->num_repos; i++)
		free(db->repos[i].url);
	for (i = 0; i < db->protected_paths->num; i++)
		free(db->protected_paths->item[i]);
	free(db->protected_paths);
	free(db->world);

661
662
663
	apk_hash_free(&db->available.names);
	apk_hash_free(&db->available.packages);
	apk_hash_free(&db->installed.dirs);
664
665
	if (db->root != NULL) {
		close(db->root_fd);
666
		free(db->root);
667
	}
668
669
670
671
}

struct apk_package *apk_db_get_pkg(struct apk_database *db, csum_t sum)
{
672
673
	return apk_hash_get(&db->available.packages,
			    APK_BLOB_PTR_LEN((void*) sum, sizeof(csum_t)));
674
675
}

676
struct apk_package *apk_db_pkg_add_file(struct apk_database *db, const char *file)
677
678
679
680
{
	struct apk_package *info;

	info = apk_pkg_read(db, file);
681
682
683
	if (info != NULL)
		apk_db_pkg_add(db, info);
	return info;
684
685
686
687
}

static int write_index_entry(apk_hash_item item, void *ctx)
{
Timo Teräs's avatar
Timo Teräs committed
688
	struct apk_ostream *os = (struct apk_ostream *) ctx;
689
690
691
692
	char buf[1024];
	apk_blob_t blob;

	blob = apk_pkg_format_index_entry(item, sizeof(buf), buf);
Timo Teräs's avatar
Timo Teräs committed
693
694
695
696
697
	if (APK_BLOB_IS_NULL(blob))
		return 0;

	if (os->write(os, blob.ptr, blob.len) != blob.len)
		return -1;
698
699
700
701

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
702
void apk_db_index_write(struct apk_database *db, struct apk_ostream *os)
703
{
Timo Teräs's avatar
Timo Teräs committed
704
	apk_hash_foreach(&db->available.packages, write_index_entry, (void *) os);
705
706
}

707
int apk_db_add_repository(apk_database_t _db, apk_blob_t repository)
708
{
709
710
	struct apk_database *db = _db.db;
	struct apk_istream *is;
711
	char tmp[256];
712
	int r;
713
714
715
716
717
718

	if (db->num_repos >= APK_MAX_REPOS)
		return -1;

	r = db->num_repos++;
	db->repos[r] = (struct apk_repository){
719
		.url = apk_blob_cstr(repository)
720
721
	};

Timo Teräs's avatar
Timo Teräs committed
722
723
	snprintf(tmp, sizeof(tmp), "%s/APK_INDEX.gz", db->repos[r].url);
	is = apk_istream_from_file_gz(tmp);
724
	if (is == NULL) {
725
726
727
		apk_error("Failed to open index file %s", tmp);
		return -1;
	}
728
729
	apk_db_index_read(db, is, r);
	is->close(is);
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760

	return 0;
}

int apk_db_recalculate_and_commit(struct apk_database *db)
{
	struct apk_state *state;
	int r;

	state = apk_state_new(db);
	r = apk_state_satisfy_deps(state, db->world);
	if (r == 0) {
		r = apk_state_commit(state, db);
		if (r != 0) {
			apk_error("Failed to commit changes");
			return r;
		}
		apk_db_write_config(db);

		apk_message("OK: %d packages, %d dirs, %d files",
			    db->installed.stats.packages,
			    db->installed.stats.dirs,
			    db->installed.stats.files);
	} else {
		apk_error("Failed to build installation graph");
	}
	apk_state_unref(state);

	return r;
}

761
static int apk_db_install_archive_entry(void *_ctx,
762
					const struct apk_file_info *ae,
763
					struct apk_istream *is)
764
{
765
	struct install_ctx *ctx = (struct install_ctx *) _ctx;
766
	struct apk_database *db = ctx->db;
767
768
769
	struct apk_package *pkg = ctx->pkg, *opkg;
	apk_blob_t name = APK_BLOB_STR(ae->name), bdir, bfile;
	struct apk_db_dir_instance *diri = ctx->diri;
770
	struct apk_db_file *file;
771
772
	struct apk_file_info fi;
	char alt_name[PATH_MAX];
773
	const char *p;
Timo Teräs's avatar
Timo Teräs committed
774
	int r = 0, type = APK_SCRIPT_INVALID;
775

Timo Teräs's avatar
Timo Teräs committed
776
777
778
779
780
781
782
783
	/* Package metainfo and script processing */
	if (ae->name[0] == '.') {
		/* APK 2.0 format */
		if (strcmp(ae->name, ".INSTALL") != 0)
			return 0;
		type = APK_SCRIPT_GENERIC;
	} else if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
		/* APK 1.0 format */
784
785
786
787
788
789
790
791
792
		p = &ae->name[11];
		if (strncmp(p, pkg->name->name, strlen(pkg->name->name)) != 0)
			return 0;
		p += strlen(pkg->name->name) + 1;
		if (strncmp(p, pkg->version, strlen(pkg->version)) != 0)
			return 0;
		p += strlen(pkg->version) + 1;

		type = apk_script_type(p);
793
		if (type == APK_SCRIPT_INVALID)
794
			return 0;
Timo Teräs's avatar
Timo Teräs committed
795
	}
796

Timo Teräs's avatar
Timo Teräs committed
797
798
	/* Handle script */
	if (type != APK_SCRIPT_INVALID) {
799
		apk_pkg_add_script(pkg, is, type, ae->size);
800

Timo Teräs's avatar
Timo Teräs committed
801
802
		if (type == APK_SCRIPT_GENERIC ||
		    type == ctx->script) {
803
			r = apk_pkg_run_script(pkg, db->root_fd, ctx->script);
804
805
806
807
808
809
810
811
			if (r != 0)
				apk_error("%s-%s: Failed to execute pre-install/upgrade script",
					  pkg->name->name, pkg->version);
		}

		return r;
	}

Timo Teräs's avatar
Timo Teräs committed
812
	/* Installable entry */
813
	if (!S_ISDIR(ae->mode)) {
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
		if (!apk_blob_rsplit(name, '/', &bdir, &bfile))
			return 0;

		if (bfile.len > 6 && memcmp(bfile.ptr, ".keep_", 6) == 0)
			return 0;

		r = strlen(diri->dir->dirname);
		r = strlen(bdir.ptr);
		r = 0;

		/* Make sure the file is part of the cached directory tree */
		if (diri == NULL ||
		    strncmp(diri->dir->dirname, bdir.ptr, bdir.len) != 0 ||
		    diri->dir->dirname[bdir.len] != 0) {
			struct hlist_node *n;
829

830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
			hlist_for_each_entry(diri, n, &pkg->owned_dirs, pkg_dirs_list) {
				if (strncmp(diri->dir->dirname, bdir.ptr, bdir.len) == 0 &&
				    diri->dir->dirname[bdir.len] == 0)
					break;
			}
			if (diri == NULL) {
				apk_error("%s: File '%*s' entry without directory entry.\n",
					  pkg->name->name, name.len, name.ptr);
				return -1;
			}
			ctx->diri = diri;
		}

		file = apk_db_file_get(db, name, ctx);
		if (file == NULL) {
			apk_error("%s: Failed to create fdb entry for '%*s'\n",
				  pkg->name->name, name.len, name.ptr);
847
848
849
			return -1;
		}

850
851
852
853
854
855
856
857
858
		if (file->diri != NULL) {
			opkg = file->diri->pkg;
			if (opkg->name != pkg->name &&
			    strcmp(opkg->name->name, "busybox") != 0) {
				apk_error("%s: Trying to overwrite %s owned by %s.\n",
					  pkg->name->name, ae->name, opkg->name->name);
				return -1;
			}
		}
859

860
861
862
863
		if (ctx->file_diri_node == NULL)
			ctx->file_diri_node = hlist_tail_ptr(&diri->owned_files);
		apk_db_file_set_owner(db, file, diri, ctx->file_diri_node);
		ctx->file_diri_node = &file->diri_files_list.next;
864

865
		if ((diri->dir->flags & APK_DBDIRF_PROTECTED) &&
866
867
868
869
870
871
872
		    csum_valid(file->csum) &&
		    apk_file_get_info(ae->name, &fi) == 0 &&
		    memcmp(file->csum, fi.csum, sizeof(csum_t)) != 0) {
			/* Protected file, which is modified locally.
			 * Extract to separate place */
			snprintf(alt_name, sizeof(alt_name),
				 "%s/%s.apk-new",
873
				 diri->dir->dirname, file->filename);
874
875
876
877
878
			r = apk_archive_entry_extract(ae, is, alt_name);
		} else {
			r = apk_archive_entry_extract(ae, is, NULL);
		}
		memcpy(file->csum, ae->csum, sizeof(csum_t));
879
880
881
	} else {
		if (name.ptr[name.len-1] == '/')
			name.len--;
882
883
884
885
886
887
888
889
890
891
892

		if (ctx->diri_node == NULL)
			ctx->diri_node = hlist_tail_ptr(&pkg->owned_dirs);
		ctx->diri = diri = apk_db_diri_new(db, pkg, name,
						   ctx->diri_node);
		ctx->diri_node = &diri->pkg_dirs_list.next;
		ctx->file_dir_node = NULL;
		ctx->file_diri_node = NULL;

		apk_db_diri_set(diri, ae->mode & 0777, ae->uid, ae->gid);
		apk_db_diri_create(diri);
893
894
895
896
897
898
899
900
	}

	return r;
}

static void apk_db_purge_pkg(struct apk_database *db,
			     struct apk_package *pkg)
{
901
	struct apk_db_dir_instance *diri;
902
	struct apk_db_file *file;
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
	struct hlist_node *dc, *dn, *fc, *fn;
	char name[1024];

	hlist_for_each_entry_safe(diri, dc, dn, &pkg->owned_dirs, pkg_dirs_list) {
		hlist_for_each_entry_safe(file, fc, fn, &diri->owned_files, diri_files_list) {
			file->diri = NULL;
			snprintf(name, sizeof(name), "%s/%s",
				 diri->dir->dirname,
				 file->filename);
			unlink(name);
			__hlist_del(fc, &diri->owned_files.first);

			db->installed.stats.files--;
		}
		apk_db_dir_put(db, diri->dir);
		__hlist_del(dc, &pkg->owned_dirs.first);
919
	}
920
	apk_pkg_set_state(db, pkg, APK_STATE_NO_INSTALL);
921
922
923
924
925
926
}

int apk_db_install_pkg(struct apk_database *db,
		       struct apk_package *oldpkg,
		       struct apk_package *newpkg)
{
927
	struct apk_bstream *bs;
928
929
930
	struct install_ctx ctx;
	csum_t csum;
	char file[256];
Timo Teräs's avatar
Timo Teräs committed
931
	int r;
932

933
	if (fchdir(db->root_fd) < 0)
934
935
936
937
938
		return errno;

	/* Purge the old package if there */
	if (oldpkg != NULL) {
		if (newpkg == NULL) {
939
			r = apk_pkg_run_script(oldpkg, db->root_fd,
940
941
942
943
944
945
					       APK_SCRIPT_PRE_DEINSTALL);
			if (r != 0)
				return r;
		}
		apk_db_purge_pkg(db, oldpkg);
		if (newpkg == NULL) {
946
			apk_pkg_run_script(oldpkg, db->root_fd,
947
948
949
950
951
952
					   APK_SCRIPT_POST_DEINSTALL);
			return 0;
		}
	}

	/* Install the new stuff */
953
954
955
956
	if (newpkg->filename == NULL) {
		snprintf(file, sizeof(file),
			 "%s/%s-%s.apk",
			 db->repos[0].url, newpkg->name->name, newpkg->version);
Timo Teräs's avatar
Timo Teräs committed
957
		bs = apk_bstream_from_url(file);
958
	} else
Timo Teräs's avatar
Timo Teräs committed
959
		bs = apk_bstream_from_file(newpkg->filename);
960

Timo Teräs's avatar
Timo Teräs committed
961
	if (bs == NULL) {
962
		apk_error("%s: %s", file, strerror(errno));
963
		return errno;
964
	}
965
966
967
968
969
970
971

	ctx = (struct install_ctx) {
		.db = db,
		.pkg = newpkg,
		.script = (oldpkg == NULL) ?
			APK_SCRIPT_PRE_INSTALL : APK_SCRIPT_PRE_UPGRADE,
	};
972
	if (apk_parse_tar_gz(bs, apk_db_install_archive_entry, &ctx) != 0)
973
974
		goto err_close;

Timo Teräs's avatar
Timo Teräs committed
975
	bs->close(bs, csum, NULL);
976

977
	apk_pkg_set_state(db, newpkg, APK_STATE_INSTALL);
978
979
980
981
982

	if (memcmp(csum, newpkg->csum, sizeof(csum)) != 0)
		apk_warning("%s-%s: checksum does not match",
			    newpkg->name->name, newpkg->version);

983
	r = apk_pkg_run_script(newpkg, db->root_fd,
984
985
			       (oldpkg == NULL) ?
			       APK_SCRIPT_POST_INSTALL : APK_SCRIPT_POST_UPGRADE);
986
	if (r != 0) {
987
988
		apk_error("%s-%s: Failed to execute post-install/upgrade script",
			  newpkg->name->name, newpkg->version);
989
990
991
	} else if (apk_quiet) {
		write(STDOUT_FILENO, ".", 1);
	}
992
993
	return r;
err_close:
Timo Teräs's avatar
Timo Teräs committed
994
	bs->close(bs, NULL, NULL);
995
996
	return -1;
}