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