rpm.c
author Kristian H?gsberg <krh@redhat.com>
Mon Jun 09 12:47:37 2008 -0400 (2008-06-09)
changeset 230 c1e2aed8dd07
parent 211 cf0ca962262b
child 224 5803b6151d02
permissions -rw-r--r--
Rewrite depsolver to use a series of passes over all packages.

The big change is that we follow one step of the depedency chain for
each package to resolve in each iteration, and repeat until there are
no more possible moves. In contrast the old depsolver would try to
follow the dependency chain completely for one package at a time.

This new approach is simpler and faster, and at the same time more
roboust. Instead of knowing how one newly installed package may
affect other packages (obsoleting, pulling in new packages etc), the
new algorithm just looks at the total list of requires, provides,
obsoletes and conflicts after installing new packages.
krh@212
     1
/*
krh@212
     2
 * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
krh@212
     3
 * Copyright (C) 2008  Red Hat, Inc
krh@212
     4
 *
krh@212
     5
 * This program is free software; you can redistribute it and/or modify
krh@212
     6
 * it under the terms of the GNU General Public License as published by
krh@212
     7
 * the Free Software Foundation; either version 2 of the License, or
krh@212
     8
 * (at your option) any later version.
krh@212
     9
 *
krh@212
    10
 * This program is distributed in the hope that it will be useful,
krh@212
    11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
krh@212
    12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
krh@212
    13
 * GNU General Public License for more details.
krh@212
    14
 *
krh@212
    15
 * You should have received a copy of the GNU General Public License along
krh@212
    16
 * with this program; if not, write to the Free Software Foundation, Inc.,
krh@212
    17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
krh@212
    18
 */
krh@212
    19
krh@74
    20
#include <stdio.h>
krh@84
    21
#include <stddef.h>
krh@74
    22
#include <string.h>
krh@176
    23
#include <errno.h>
krh@74
    24
#include <sys/stat.h>
krh@74
    25
#include <sys/mman.h>
krh@85
    26
#include <sys/types.h>
krh@85
    27
#include <sys/wait.h>
krh@74
    28
#include <fcntl.h>
krh@74
    29
#include <unistd.h>
krh@74
    30
#include <arpa/inet.h>
krh@74
    31
#include <rpm/rpmlib.h>
danw@109
    32
#include <rpm/rpmdb.h>
krh@77
    33
#include <zlib.h>
krh@74
    34
krh@75
    35
#include "razor.h"
krh@91
    36
#include "razor-internal.h"
krh@75
    37
krh@74
    38
#define	RPM_LEAD_SIZE 96
krh@74
    39
krh@74
    40
struct rpm_header {
krh@74
    41
	unsigned char magic[4];
krh@74
    42
	unsigned char reserved[4];
krh@74
    43
	int nindex;
krh@74
    44
	int hsize;
krh@74
    45
};
krh@74
    46
krh@74
    47
struct rpm_header_index {
krh@74
    48
	int tag;
krh@74
    49
	int type;
krh@74
    50
	int offset;
krh@74
    51
	int count;
krh@74
    52
};
krh@74
    53
krh@77
    54
struct razor_rpm {
krh@75
    55
	struct rpm_header *signature;
krh@75
    56
	struct rpm_header *header;
krh@77
    57
	const char **dirs;
krh@75
    58
	const char *pool;
krh@75
    59
	void *map;
krh@75
    60
	size_t size;
krh@77
    61
	void *payload;
krh@75
    62
};
krh@75
    63
krh@85
    64
static struct rpm_header_index *
krh@85
    65
razor_rpm_get_header(struct razor_rpm *rpm, unsigned int tag)
krh@85
    66
{
krh@85
    67
	struct rpm_header_index *index, *end;
krh@85
    68
krh@85
    69
	index = (struct rpm_header_index *) (rpm->header + 1);
krh@85
    70
	end = index + ntohl(rpm->header->nindex);
krh@85
    71
	while (index < end) {
krh@85
    72
		if (ntohl(index->tag) == tag)
krh@85
    73
			return index;
krh@85
    74
		index++;
krh@85
    75
	}
krh@85
    76
krh@85
    77
	return NULL;
krh@85
    78
}
krh@85
    79
krh@87
    80
static const void *
krh@87
    81
razor_rpm_get_indirect(struct razor_rpm *rpm,
krh@87
    82
		       unsigned int tag, unsigned int *count)
krh@87
    83
{
krh@87
    84
	struct rpm_header_index *index;
krh@87
    85
krh@87
    86
	index = razor_rpm_get_header(rpm, tag);
krh@87
    87
	if (index != NULL) {
krh@87
    88
		if (count)
krh@87
    89
			*count = ntohl(index->count);
krh@87
    90
krh@87
    91
		return rpm->pool + ntohl(index->offset);
krh@87
    92
	}
krh@87
    93
krh@87
    94
	return NULL;
krh@87
    95
}
krh@87
    96
danw@109
    97
static enum razor_version_relation
danw@109
    98
rpm_to_razor_flags (uint_32 flags)
danw@109
    99
{
danw@109
   100
	switch (flags & (RPMSENSE_LESS | RPMSENSE_EQUAL | RPMSENSE_GREATER)) {
danw@109
   101
	case RPMSENSE_LESS:
danw@109
   102
		return RAZOR_VERSION_LESS;
danw@109
   103
	case RPMSENSE_LESS|RPMSENSE_EQUAL:
danw@109
   104
		return RAZOR_VERSION_LESS_OR_EQUAL;
danw@109
   105
	case RPMSENSE_EQUAL:
danw@109
   106
		return RAZOR_VERSION_EQUAL;
danw@109
   107
	case RPMSENSE_GREATER|RPMSENSE_EQUAL:
danw@109
   108
		return RAZOR_VERSION_GREATER_OR_EQUAL;
danw@109
   109
	case RPMSENSE_GREATER:
danw@109
   110
		return RAZOR_VERSION_GREATER;
danw@109
   111
	}
danw@109
   112
danw@109
   113
	/* FIXME? */
danw@109
   114
	return RAZOR_VERSION_EQUAL;
danw@109
   115
}
danw@109
   116
krh@87
   117
static void
krh@87
   118
import_properties(struct razor_importer *importer, unsigned long type,
krh@87
   119
		  struct razor_rpm *rpm,
krh@87
   120
		  int name_tag, int version_tag, int flags_tag)
krh@87
   121
{
krh@87
   122
	const char *name, *version;
krh@172
   123
	const uint_32 *flags;
krh@172
   124
	uint_32 f;
krh@87
   125
	unsigned int i, count;
krh@87
   126
krh@87
   127
	name = razor_rpm_get_indirect(rpm, name_tag, &count);
krh@87
   128
	if (name == NULL)
krh@87
   129
		return;
krh@87
   130
krh@172
   131
	flags = razor_rpm_get_indirect(rpm, flags_tag, &count);
danw@109
   132
krh@87
   133
	version = razor_rpm_get_indirect(rpm, version_tag, &count);
krh@87
   134
	for (i = 0; i < count; i++) {
krh@172
   135
		f = rpm_to_razor_flags(ntohl(flags[i]));
krh@172
   136
		razor_importer_add_property(importer, name, f, version, type);
krh@87
   137
		name += strlen(name) + 1;
krh@87
   138
		version += strlen(version) + 1;
krh@87
   139
	}
krh@87
   140
}
krh@87
   141
krh@87
   142
static void
krh@87
   143
import_files(struct razor_importer *importer, struct razor_rpm *rpm)
krh@87
   144
{
krh@87
   145
	const char *name;
danw@173
   146
	const uint32_t *index;
krh@87
   147
	unsigned int i, count;
krh@87
   148
	char buffer[256];
krh@87
   149
krh@87
   150
	/* assert: count is the same for all arrays */
krh@87
   151
krh@87
   152
	index = razor_rpm_get_indirect(rpm, RPMTAG_DIRINDEXES, &count);
krh@87
   153
	name = razor_rpm_get_indirect(rpm, RPMTAG_BASENAMES, &count);
krh@87
   154
	for (i = 0; i < count; i++) {
krh@87
   155
		snprintf(buffer, sizeof buffer,
krh@87
   156
			 "%s%s", rpm->dirs[ntohl(*index)], name);
krh@87
   157
		razor_importer_add_file(importer, buffer);
krh@87
   158
		name += strlen(name) + 1;
krh@87
   159
		index++;
krh@87
   160
	}
krh@87
   161
}
krh@87
   162
krh@77
   163
struct razor_rpm *
krh@77
   164
razor_rpm_open(const char *filename)
krh@74
   165
{
krh@77
   166
	struct razor_rpm *rpm;
krh@74
   167
	struct rpm_header_index *base, *index;
krh@75
   168
	struct stat buf;
krh@87
   169
	unsigned int count, i, nindex, hsize;
krh@77
   170
	const char *name;
krh@87
   171
	int fd;
krh@74
   172
krh@77
   173
	rpm = malloc(sizeof *rpm);
krh@75
   174
	memset(rpm, 0, sizeof *rpm);
krh@74
   175
krh@75
   176
	fd = open(filename, O_RDONLY);
krh@75
   177
	if (fd < 0) {
krh@75
   178
		fprintf(stderr, "couldn't open %s\n", filename);
krh@77
   179
		return NULL;
krh@75
   180
	}
krh@88
   181
krh@88
   182
	if (fstat(fd, &buf) < 0) {
krh@88
   183
		fprintf(stderr, "failed to stat %s (%m)\n", filename);
krh@88
   184
		return NULL;
krh@88
   185
	}
krh@88
   186
krh@75
   187
	rpm->size = buf.st_size;
krh@75
   188
	rpm->map = mmap(NULL, rpm->size, PROT_READ, MAP_PRIVATE, fd, 0);
krh@75
   189
	if (rpm->map == MAP_FAILED) {
krh@75
   190
		fprintf(stderr, "couldn't mmap %s\n", filename);
krh@77
   191
		return NULL;
krh@75
   192
	}
krh@75
   193
	close(fd);
krh@75
   194
krh@75
   195
	rpm->signature = rpm->map + RPM_LEAD_SIZE;
krh@75
   196
	nindex = ntohl(rpm->signature->nindex);
krh@75
   197
	hsize = ntohl(rpm->signature->hsize);
krh@75
   198
	rpm->header = (void *) (rpm->signature + 1) +
krh@75
   199
		ALIGN(nindex * sizeof *index + hsize, 8);
krh@77
   200
	nindex = ntohl(rpm->header->nindex);
krh@77
   201
	hsize = ntohl(rpm->header->hsize);
krh@77
   202
	rpm->payload = (void *) (rpm->header + 1) +
krh@77
   203
		nindex * sizeof *index + hsize;
krh@75
   204
krh@75
   205
	base = (struct rpm_header_index *) (rpm->header + 1);
krh@75
   206
	rpm->pool = (void *) base + nindex * sizeof *index;
krh@75
   207
krh@87
   208
	/* Look up dir names now so we can index them directly. */
krh@87
   209
	name = razor_rpm_get_indirect(rpm, RPMTAG_DIRNAMES, &count);
krh@185
   210
	if (name) {
krh@185
   211
		rpm->dirs = calloc(count, sizeof *rpm->dirs);
krh@185
   212
		for (i = 0; i < count; i++) {
krh@185
   213
			rpm->dirs[i] = name;
krh@185
   214
			name += strlen(name) + 1;
krh@185
   215
		}
krh@185
   216
	} else {
krh@185
   217
		name = razor_rpm_get_indirect(rpm, RPMTAG_OLDFILENAMES,
krh@185
   218
					      &count);
krh@185
   219
		if (name) {
krh@185
   220
			fprintf(stderr, "old filenames not supported\n");
krh@185
   221
			return NULL;
krh@185
   222
		}
krh@77
   223
	}
krh@77
   224
krh@77
   225
	return rpm;
krh@77
   226
}
krh@77
   227
krh@77
   228
struct cpio_file_header {
krh@77
   229
	char magic[6];
krh@77
   230
	char inode[8];
krh@77
   231
	char mode[8];
krh@77
   232
	char uid[8];
krh@77
   233
	char gid[8];
krh@77
   234
	char nlink[8];
krh@77
   235
	char mtime[8];
krh@77
   236
	char filesize[8];
krh@77
   237
	char devmajor[8];
krh@77
   238
	char devminor[8];
krh@77
   239
	char rdevmajor[8];
krh@77
   240
	char rdevminor[8];
krh@77
   241
	char namesize[8];
krh@77
   242
	char checksum[8];
krh@77
   243
	char filename[0];
krh@77
   244
};
krh@77
   245
krh@77
   246
/* gzip flags */
krh@77
   247
#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
krh@77
   248
#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
krh@77
   249
#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
krh@77
   250
#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
krh@77
   251
#define COMMENT      0x10 /* bit 4 set: file comment present */
krh@77
   252
#define RESERVED     0xE0 /* bits 5..7: reserved */
krh@77
   253
krh@86
   254
struct installer {
krh@86
   255
	const char *root;
krh@86
   256
	struct razor_rpm *rpm;
krh@86
   257
	z_stream stream;
krh@86
   258
	unsigned char buffer[32768];
krh@86
   259
	size_t rest, length;
krh@86
   260
};
krh@86
   261
krh@77
   262
static int
krh@86
   263
installer_inflate(struct installer *installer)
krh@86
   264
{
krh@86
   265
	size_t length;
krh@86
   266
	int err;
krh@86
   267
krh@211
   268
	if (installer->rest > sizeof installer->buffer)
krh@86
   269
		length = sizeof installer->buffer;
krh@86
   270
	else
krh@86
   271
		length = installer->rest;
krh@86
   272
krh@86
   273
	installer->stream.next_out = installer->buffer;
krh@211
   274
	installer->stream.avail_out = length;
krh@86
   275
	err = inflate(&installer->stream, Z_SYNC_FLUSH);
krh@86
   276
	if (err != Z_OK && err != Z_STREAM_END) {
krh@86
   277
		fprintf(stderr, "inflate error: %d (%m)\n", err);
krh@86
   278
		return -1;
krh@86
   279
	}
krh@86
   280
krh@86
   281
	installer->rest -= length;
krh@86
   282
	installer->length = length;
krh@86
   283
krh@86
   284
	return 0;
krh@86
   285
}
krh@86
   286
krh@86
   287
static int
krh@211
   288
installer_align(struct installer *installer, size_t size)
krh@211
   289
{
krh@211
   290
	unsigned char buffer[4];
krh@211
   291
	int err;
krh@211
   292
krh@211
   293
	installer->stream.next_out = buffer;
krh@211
   294
	installer->stream.avail_out =
krh@211
   295
		(size - installer->stream.total_out) & (size - 1);
krh@211
   296
krh@211
   297
	if (installer->stream.avail_out == 0)
krh@211
   298
		return 0;
krh@211
   299
krh@211
   300
	err = inflate(&installer->stream, Z_SYNC_FLUSH);
krh@211
   301
	if (err != Z_OK && err != Z_STREAM_END) {
krh@211
   302
		fprintf(stderr, "inflate error: %d (%m)\n", err);
krh@211
   303
		return -1;
krh@211
   304
	}
krh@211
   305
krh@211
   306
	return 0;
krh@211
   307
}
krh@211
   308
krh@211
   309
static int
krh@211
   310
create_path(struct installer *installer, const char *path, unsigned int mode)
krh@77
   311
{
krh@91
   312
	char buffer[PATH_MAX];
krh@176
   313
	struct stat buf;
krh@176
   314
	int fd, ret;
krh@77
   315
krh@91
   316
	if (razor_create_dir(installer->root, path) < 0)
krh@91
   317
		return -1;
krh@77
   318
krh@211
   319
	snprintf(buffer, sizeof buffer, "%s%s", installer->root, path);
krh@77
   320
krh@77
   321
	switch (mode >> 12) {
krh@77
   322
	case REG:
krh@176
   323
		/* FIXME: handle the case where a file is already there. */
krh@86
   324
		fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, mode & 0x1ff);
krh@86
   325
		if (fd < 0){
krh@86
   326
			fprintf(stderr, "failed to create file %s\n", buffer);
krh@86
   327
			return -1;
krh@86
   328
		}
krh@86
   329
		while (installer->rest > 0) {
krh@86
   330
			if (installer_inflate(installer)) {
krh@86
   331
				fprintf(stderr, "failed to inflate\n");
krh@86
   332
				return -1;
krh@86
   333
			}
krh@91
   334
			if (razor_write(fd, installer->buffer,
krh@91
   335
					installer->length)) {
krh@86
   336
				fprintf(stderr, "failed to write payload\n");
krh@86
   337
				return -1;
krh@86
   338
			}
krh@86
   339
		}
krh@86
   340
		if (close(fd) < 0) {
krh@86
   341
			fprintf(stderr, "failed to close %s: %m\n", buffer);
krh@86
   342
			return -1;
krh@86
   343
		}
krh@86
   344
		return 0;
krh@86
   345
	case XDIR:
krh@176
   346
		ret = mkdir(buffer, mode & 0x1ff);
krh@176
   347
		if (ret == 0 || errno != EEXIST)
krh@176
   348
			return ret;
krh@176
   349
		if (stat(buffer, &buf) || !S_ISDIR(buf.st_mode)) {
krh@176
   350
			/* FIXME: also check that mode match. */
krh@176
   351
			fprintf(stderr,
krh@176
   352
				"%s exists but is not a directory\n", buffer);
krh@176
   353
			return -1;
krh@176
   354
		}
krh@176
   355
		return 0;
krh@86
   356
	case PIPE:
krh@86
   357
	case CDEV:
krh@86
   358
	case BDEV:
krh@86
   359
	case SOCK:
krh@86
   360
		printf("%s: unhandled file type %d\n", buffer, mode >> 12);
krh@86
   361
		return 0;
krh@86
   362
	case LINK:
krh@86
   363
		if (installer_inflate(installer)) {
krh@86
   364
			fprintf(stderr, "failed to inflate\n");
krh@86
   365
			return -1;
krh@86
   366
		}
krh@86
   367
		if (installer->length >= sizeof installer->buffer) {
krh@86
   368
			fprintf(stderr, "link name too long\n");
krh@86
   369
			return -1;
krh@86
   370
		}
krh@86
   371
		installer->buffer[installer->length] = '\0';
krh@86
   372
		if (symlink((const char *) installer->buffer, buffer)) {
krh@86
   373
			fprintf(stderr, "failed to create symlink, %m\n");
krh@86
   374
			return -1;
krh@86
   375
		}
krh@86
   376
		return 0;
krh@77
   377
	default:
krh@86
   378
		printf("%s: unknown file type %d\n", buffer, mode >> 12);
krh@86
   379
		return 0;
krh@77
   380
	}
krh@77
   381
}
krh@77
   382
krh@85
   383
static int
krh@86
   384
run_script(struct installer *installer,
krh@86
   385
	   unsigned int program_tag, unsigned int script_tag)
krh@85
   386
{
krh@85
   387
	int pid, status, fd[2];
krh@86
   388
	const char *script = NULL, *program = NULL;
krh@85
   389
krh@87
   390
	program = razor_rpm_get_indirect(installer->rpm, program_tag, NULL);
krh@87
   391
	script = razor_rpm_get_indirect(installer->rpm, script_tag, NULL);
krh@86
   392
	if (program == NULL && script == NULL) {
krh@202
   393
		return 0;
krh@86
   394
	} else if (program == NULL) {
krh@86
   395
		program = "/bin/sh";
krh@85
   396
	}
krh@85
   397
krh@85
   398
	if (pipe(fd) < 0) {
krh@85
   399
		fprintf(stderr, "failed to create pipe\n");
krh@85
   400
		return -1;
krh@85
   401
	}
krh@85
   402
	pid = fork();
krh@85
   403
	if (pid < 0) {
krh@85
   404
		fprintf(stderr, "failed to fork, %m\n");
krh@85
   405
	} else if (pid == 0) {
krh@85
   406
		if (dup2(fd[0], STDIN_FILENO) < 0) {
krh@85
   407
			fprintf(stderr, "failed redirect stdin, %m\n");
krh@85
   408
			return -1;
krh@85
   409
		}
krh@85
   410
		if (close(fd[0]) < 0 || close(fd[1]) < 0) {
krh@85
   411
			fprintf(stderr, "failed to close pipe, %m\n");
krh@89
   412
			return -1;
krh@85
   413
		}
krh@86
   414
		if (chroot(installer->root) < 0) {
krh@86
   415
			fprintf(stderr, "failed to chroot to %s, %m\n",
krh@86
   416
				installer->root);
krh@85
   417
			return -1;
krh@85
   418
		}
krh@86
   419
		printf("executing program %s in chroot %s\n",
krh@86
   420
		       program, installer->root);
krh@86
   421
		if (execl(program, program, NULL)) {
krh@86
   422
			fprintf(stderr, "failed to exec %s, %m\n", program);
krh@89
   423
			exit(-1);
krh@85
   424
		}
krh@85
   425
	} else {
krh@184
   426
		if (script && razor_write(fd[1], script, strlen(script)) < 0) {
krh@85
   427
			fprintf(stderr, "failed to pipe script, %m\n");
krh@85
   428
			return -1;
krh@85
   429
		}
krh@85
   430
		if (close(fd[0]) || close(fd[1])) {
krh@85
   431
			fprintf(stderr, "failed to close pipe, %m\n");
krh@85
   432
			return -1;
krh@85
   433
		}
krh@85
   434
		if (wait(&status) < 0) {
krh@85
   435
			fprintf(stderr, "wait for child failed, %m");
krh@85
   436
			return -1;
krh@85
   437
		}
krh@199
   438
		if (status)
krh@199
   439
			printf("script exited with status %d\n", status);
krh@85
   440
	}
krh@85
   441
krh@85
   442
	return 0;
krh@85
   443
}
krh@85
   444
krh@86
   445
static int
krh@86
   446
installer_init(struct installer *installer)
krh@77
   447
{
krh@86
   448
	unsigned char *gz_header;
krh@86
   449
	int method, flags, err;
krh@77
   450
krh@86
   451
	gz_header = installer->rpm->payload;
krh@77
   452
	if (gz_header[0] != 0x1f || gz_header[1] != 0x8b) {
krh@77
   453
		fprintf(stderr, "payload section doesn't have gz header\n");
krh@77
   454
		return -1;
krh@77
   455
	}
krh@77
   456
krh@77
   457
	method = gz_header[2];
krh@77
   458
	flags = gz_header[3];
krh@77
   459
krh@77
   460
	if (method != Z_DEFLATED || flags != 0) {
krh@77
   461
		fprintf(stderr,
krh@77
   462
			"unknown payload compression method or flags set\n");
krh@77
   463
		return -1;
krh@77
   464
	}
krh@77
   465
krh@86
   466
	installer->stream.zalloc = NULL;
krh@86
   467
	installer->stream.zfree = NULL;
krh@86
   468
	installer->stream.opaque = NULL;
krh@85
   469
krh@86
   470
	installer->stream.next_in  = gz_header + 10;
krh@86
   471
	installer->stream.avail_in =
krh@86
   472
		(installer->rpm->map + installer->rpm->size) -
krh@86
   473
		(void *) installer->stream.next_in;
krh@86
   474
	installer->stream.next_out = NULL;
krh@86
   475
	installer->stream.avail_out = 0;
krh@77
   476
krh@86
   477
	err = inflateInit2(&installer->stream, -MAX_WBITS);
krh@77
   478
	if (err != Z_OK) {
krh@77
   479
		fprintf(stderr, "inflateInit error: %d\n", err);
krh@77
   480
		return -1;
krh@77
   481
	}
krh@77
   482
krh@86
   483
	return 0;
krh@86
   484
}
krh@77
   485
krh@86
   486
static int
krh@86
   487
installer_finish(struct installer *installer)
krh@86
   488
{
krh@86
   489
	int err;
krh@77
   490
krh@86
   491
	err = inflateEnd(&installer->stream);
krh@77
   492
krh@77
   493
	if (err != Z_OK) {
krh@77
   494
		fprintf(stderr, "inflateEnd error: %d\n", err);
krh@77
   495
		return -1;
krh@77
   496
	}	    
krh@77
   497
krh@86
   498
	return 0;
krh@86
   499
}
krh@86
   500
krh@211
   501
static unsigned long
krh@211
   502
fixed_hex_to_ulong(const char *hex, int length)
krh@211
   503
{
krh@211
   504
	long l;
krh@211
   505
	int i;
krh@211
   506
krh@211
   507
	for (i = 0, l = 0; i < length; i++) {
krh@211
   508
		if (hex[i] < 'a')
krh@211
   509
			l = l * 16 + hex[i] - '0';
krh@211
   510
		else
krh@211
   511
			l = l * 16 + hex[i] - 'a' + 10;
krh@211
   512
	}
krh@211
   513
krh@211
   514
	return l;
krh@211
   515
}
krh@211
   516
krh@86
   517
int
krh@86
   518
razor_rpm_install(struct razor_rpm *rpm, const char *root)
krh@86
   519
{
krh@86
   520
	struct installer installer;
krh@86
   521
	struct cpio_file_header *header;
krh@86
   522
	struct stat buf;
krh@211
   523
	unsigned int mode;
krh@211
   524
	char *path;
krh@211
   525
	size_t filesize;
krh@86
   526
krh@86
   527
	installer.rpm = rpm;
krh@86
   528
	installer.root = root;
krh@86
   529
krh@86
   530
	/* FIXME: Only do this before a transaction, not per rpm. */
krh@86
   531
	if (stat(root, &buf) < 0 || !S_ISDIR(buf.st_mode)) {
krh@86
   532
		fprintf(stderr,
krh@86
   533
			"root installation directory \"%s\" does not exist\n",
krh@86
   534
			root);
krh@86
   535
		return -1;
krh@86
   536
	}
krh@86
   537
krh@86
   538
	if (installer_init(&installer))
krh@86
   539
		return -1;
krh@86
   540
krh@86
   541
	run_script(&installer, RPMTAG_PREINPROG, RPMTAG_PREIN);
krh@86
   542
krh@211
   543
	while (installer.stream.avail_in > 0) {
krh@211
   544
		installer.rest = sizeof *header;
krh@211
   545
		if (installer_inflate(&installer))
krh@211
   546
			return -1;
krh@211
   547
		
krh@211
   548
		header = (struct cpio_file_header *) installer.buffer;
krh@211
   549
		mode = fixed_hex_to_ulong(header->mode, sizeof header->mode);
krh@211
   550
		filesize = fixed_hex_to_ulong(header->filesize,
krh@211
   551
					      sizeof header->filesize);
krh@86
   552
krh@211
   553
		installer.rest = fixed_hex_to_ulong(header->namesize,
krh@211
   554
						    sizeof header->namesize);
krh@86
   555
krh@211
   556
		if (installer_inflate(&installer) ||
krh@211
   557
		    installer_align(&installer, 4))
krh@86
   558
			return -1;
krh@86
   559
krh@211
   560
		path = (char *) installer.buffer;
krh@211
   561
		/* This convention is so lame... */
krh@211
   562
		if (strcmp(path, "TRAILER!!!") == 0)
krh@211
   563
			break;
krh@211
   564
krh@211
   565
		installer.rest = filesize;
krh@211
   566
		if (create_path(&installer, path + 1, mode) < 0)
krh@211
   567
			return -1;
krh@211
   568
		if (installer_align(&installer, 4))
krh@211
   569
			return -1;
krh@86
   570
	}
krh@86
   571
krh@86
   572
	if (installer_finish(&installer))
krh@86
   573
		return -1;
krh@86
   574
krh@86
   575
	run_script(&installer, RPMTAG_POSTINPROG, RPMTAG_POSTIN);
krh@85
   576
krh@75
   577
	return 0;
krh@74
   578
}
krh@74
   579
krh@77
   580
int
krh@77
   581
razor_rpm_close(struct razor_rpm *rpm)
krh@74
   582
{
krh@77
   583
	int err;
krh@77
   584
krh@77
   585
	free(rpm->dirs);
krh@77
   586
	err = munmap(rpm->map, rpm->size);
krh@77
   587
	free(rpm);
krh@77
   588
krh@77
   589
	return err;
krh@75
   590
}
krh@74
   591
krh@75
   592
int
krh@77
   593
razor_importer_add_rpm(struct razor_importer *importer, struct razor_rpm *rpm)
krh@75
   594
{
krh@192
   595
	const char *name, *version, *release, *arch;
krh@148
   596
	const uint_32 *epoch;
krh@148
   597
	char evr[128], buf[16];
krh@75
   598
krh@87
   599
	name = razor_rpm_get_indirect(rpm, RPMTAG_NAME, NULL);
danw@143
   600
	epoch = razor_rpm_get_indirect(rpm, RPMTAG_EPOCH, NULL);
krh@87
   601
	version = razor_rpm_get_indirect(rpm, RPMTAG_VERSION, NULL);
krh@87
   602
	release = razor_rpm_get_indirect(rpm, RPMTAG_RELEASE, NULL);
krh@192
   603
	arch = razor_rpm_get_indirect(rpm, RPMTAG_ARCH, NULL);
krh@87
   604
krh@149
   605
	if (epoch) {
krh@148
   606
		snprintf(buf, sizeof buf, "%u", ntohl(*epoch));
krh@148
   607
		razor_build_evr(evr, sizeof evr, buf, version, release);
krh@148
   608
	} else {
krh@148
   609
		razor_build_evr(evr, sizeof evr, NULL, version, release);
krh@148
   610
	}
krh@192
   611
	razor_importer_begin_package(importer, name, evr, arch);
krh@87
   612
krh@87
   613
	import_properties(importer, RAZOR_PROPERTY_REQUIRES, rpm,
krh@87
   614
			  RPMTAG_REQUIRENAME,
krh@87
   615
			  RPMTAG_REQUIREVERSION,
krh@87
   616
			  RPMTAG_REQUIREFLAGS);
krh@87
   617
krh@87
   618
	import_properties(importer, RAZOR_PROPERTY_PROVIDES, rpm,
krh@87
   619
			  RPMTAG_PROVIDENAME,
krh@87
   620
			  RPMTAG_PROVIDEVERSION,
krh@87
   621
			  RPMTAG_PROVIDEFLAGS);
krh@87
   622
krh@87
   623
	import_properties(importer, RAZOR_PROPERTY_OBSOLETES, rpm,
krh@87
   624
			  RPMTAG_OBSOLETENAME,
krh@87
   625
			  RPMTAG_OBSOLETEVERSION,
krh@87
   626
			  RPMTAG_OBSOLETEFLAGS);
krh@87
   627
krh@87
   628
	import_properties(importer, RAZOR_PROPERTY_CONFLICTS, rpm,
krh@87
   629
			  RPMTAG_CONFLICTNAME,
krh@87
   630
			  RPMTAG_CONFLICTVERSION,
krh@87
   631
			  RPMTAG_CONFLICTFLAGS);
krh@87
   632
krh@77
   633
	import_files(importer, rpm);
krh@74
   634
krh@75
   635
	razor_importer_finish_package(importer);
krh@74
   636
krh@75
   637
	return 0;
krh@74
   638
}
danw@109
   639
danw@109
   640
union rpm_entry {
danw@109
   641
	void *p;
danw@109
   642
	char *string;
danw@109
   643
	char **list;
danw@109
   644
	uint_32 *flags;
krh@148
   645
	uint_32 integer;
danw@109
   646
};
danw@109
   647
danw@109
   648
static void
danw@109
   649
add_properties(struct razor_importer *importer,
danw@109
   650
	       enum razor_property_type property_type,
danw@109
   651
	       Header h, int_32 name_tag, int_32 version_tag, int_32 flags_tag)
danw@109
   652
{
danw@109
   653
	union rpm_entry names, versions, flags;
danw@109
   654
	int_32 i, type, count;
danw@109
   655
danw@109
   656
	headerGetEntry(h, name_tag, &type, &names.p, &count);
danw@109
   657
	headerGetEntry(h, version_tag, &type, &versions.p, &count);
danw@109
   658
	headerGetEntry(h, flags_tag, &type, &flags.p, &count);
danw@109
   659
danw@109
   660
	for (i = 0; i < count; i++)
danw@109
   661
		razor_importer_add_property(importer,
danw@109
   662
					    names.list[i],
danw@109
   663
					    rpm_to_razor_flags (flags.flags[i]),
danw@109
   664
					    versions.list[i],
danw@109
   665
					    property_type);
danw@109
   666
}
danw@109
   667
danw@109
   668
struct razor_set *
danw@109
   669
razor_set_create_from_rpmdb(void)
danw@109
   670
{
danw@109
   671
	struct razor_importer *importer;
danw@109
   672
	rpmdbMatchIterator iter;
danw@109
   673
	Header h;
danw@109
   674
	int_32 type, count, i;
krh@192
   675
	union rpm_entry name, epoch, version, release, arch;
danw@109
   676
	union rpm_entry basenames, dirnames, dirindexes;
krh@148
   677
	char filename[PATH_MAX], evr[128], buf[16];
danw@109
   678
	rpmdb db;
danw@109
   679
danw@109
   680
	rpmReadConfigFiles(NULL, NULL);
danw@109
   681
danw@109
   682
	if (rpmdbOpen("", &db, O_RDONLY, 0644) != 0) {
danw@109
   683
		fprintf(stderr, "cannot open rpm database\n");
danw@109
   684
		exit(1);
danw@109
   685
	}
danw@109
   686
danw@109
   687
	importer = razor_importer_new();
danw@109
   688
danw@109
   689
	iter = rpmdbInitIterator(db, 0, NULL, 0);
danw@109
   690
	while (h = rpmdbNextIterator(iter), h != NULL) {
danw@109
   691
		headerGetEntry(h, RPMTAG_NAME, &type, &name.p, &count);
danw@143
   692
		headerGetEntry(h, RPMTAG_EPOCH, &type, &epoch.p, &count);
danw@109
   693
		headerGetEntry(h, RPMTAG_VERSION, &type, &version.p, &count);
danw@109
   694
		headerGetEntry(h, RPMTAG_RELEASE, &type, &release.p, &count);
krh@192
   695
		headerGetEntry(h, RPMTAG_ARCH, &type, &arch.p, &count);
krh@148
   696
krh@148
   697
		if (epoch.flags != NULL) {
krh@148
   698
			snprintf(buf, sizeof buf, "%u", *epoch.flags);
krh@148
   699
			razor_build_evr(evr, sizeof evr,
krh@148
   700
					buf, version.string, release.string);
krh@148
   701
		} else {
krh@148
   702
			razor_build_evr(evr, sizeof evr,
krh@148
   703
					NULL, version.string, release.string);
krh@148
   704
		}
krh@148
   705
krh@192
   706
		razor_importer_begin_package(importer,
krh@192
   707
					     name.string, evr, arch.string);
danw@109
   708
danw@109
   709
		add_properties(importer, RAZOR_PROPERTY_REQUIRES, h,
danw@109
   710
			       RPMTAG_REQUIRENAME,
danw@109
   711
			       RPMTAG_REQUIREVERSION,
danw@109
   712
			       RPMTAG_REQUIREFLAGS);
danw@109
   713
danw@109
   714
		add_properties(importer, RAZOR_PROPERTY_PROVIDES, h,
danw@109
   715
			       RPMTAG_PROVIDENAME,
danw@109
   716
			       RPMTAG_PROVIDEVERSION,
danw@109
   717
			       RPMTAG_PROVIDEFLAGS);
danw@109
   718
danw@109
   719
		add_properties(importer, RAZOR_PROPERTY_OBSOLETES, h,
danw@109
   720
			       RPMTAG_OBSOLETENAME,
danw@109
   721
			       RPMTAG_OBSOLETEVERSION,
danw@109
   722
			       RPMTAG_OBSOLETEFLAGS);
danw@109
   723
danw@109
   724
		add_properties(importer, RAZOR_PROPERTY_CONFLICTS, h,
danw@109
   725
			       RPMTAG_CONFLICTNAME,
danw@109
   726
			       RPMTAG_CONFLICTVERSION,
danw@109
   727
			       RPMTAG_CONFLICTFLAGS);
danw@109
   728
danw@109
   729
		headerGetEntry(h, RPMTAG_BASENAMES, &type,
danw@109
   730
			       &basenames.p, &count);
danw@109
   731
		headerGetEntry(h, RPMTAG_DIRNAMES, &type,
danw@109
   732
			       &dirnames.p, &count);
danw@109
   733
		headerGetEntry(h, RPMTAG_DIRINDEXES, &type,
danw@109
   734
			       &dirindexes.p, &count);
danw@109
   735
		for (i = 0; i < count; i++) {
danw@109
   736
			snprintf(filename, sizeof filename, "%s%s",
danw@109
   737
				 dirnames.list[dirindexes.flags[i]],
danw@109
   738
				 basenames.list[i]);
danw@109
   739
			razor_importer_add_file(importer, filename);
danw@109
   740
		}
danw@109
   741
danw@109
   742
		razor_importer_finish_package(importer);
danw@109
   743
	}
danw@109
   744
danw@109
   745
	rpmdbClose(db);
danw@109
   746
danw@109
   747
	return razor_importer_finish(importer);
danw@109
   748
}