Merge branch 'krh/master'
authorJames Bowes <jbowes@redhat.com>
Fri, 20 Jun 2008 23:04:06 +0000 (19:04 -0400)
committerJames Bowes <jbowes@redhat.com>
Fri, 20 Jun 2008 23:04:47 +0000 (19:04 -0400)
Conflicts:

librazor/razor.h
librazor/rpm.c
razor.c
src/main.c

1  2 
data/bash-completion.sh
librazor/importer.c
librazor/razor-internal.h
librazor/razor.c
librazor/razor.h
librazor/root.c
librazor/rpm.c
src/import-rpmdb.c
src/import-yum.c
src/main.c
src/test-driver.c

Simple merge
index 0000000,10d87c2..eca9ede
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,488 +1,505 @@@
+ /*
+  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
+  * Copyright (C) 2008  Red Hat, Inc
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 2 of the License, or
+  * (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License along
+  * with this program; if not, write to the Free Software Foundation, Inc.,
+  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+  */
+ #define _GNU_SOURCE
+ #include <string.h>
+ #include "razor-internal.h"
+ #include "razor.h"
+ void
+ razor_importer_begin_package(struct razor_importer *importer,
+                            const char *name,
+                            const char *version,
+                            const char *arch)
+ {
+       struct razor_package *p;
+       p = array_add(&importer->set->packages, sizeof *p);
+       p->name = hashtable_tokenize(&importer->table, name);
+       p->flags = 0;
+       p->version = hashtable_tokenize(&importer->table, version);
+       p->arch = hashtable_tokenize(&importer->table, arch);
+       importer->package = p;
+       array_init(&importer->properties);
+ }
+ void
+ razor_importer_finish_package(struct razor_importer *importer)
+ {
+       list_set_array(&importer->package->properties,
+                      &importer->set->property_pool,
+                      &importer->properties,
+                      1);
+       array_release(&importer->properties);
+ }
+ void
++razor_importer_add_details(struct razor_importer *importer,
++                         const char *summary,
++                         const char *description,
++                         const char *url,
++                         const char *license)
++{
++      importer->package->summary = hashtable_tokenize(&importer->details_table, summary);
++      importer->package->description = hashtable_tokenize(&importer->details_table, description);
++      importer->package->url = hashtable_tokenize(&importer->details_table, url);
++      importer->package->license = hashtable_tokenize(&importer->details_table, license);
++}
++
++void
+ razor_importer_add_property(struct razor_importer *importer,
+                           const char *name,
+                           uint32_t flags,
+                           const char *version)
+ {
+       struct razor_property *p;
+       uint32_t *r;
+       p = array_add(&importer->set->properties, sizeof *p);
+       p->name = hashtable_tokenize(&importer->table, name);
+       p->flags = flags;
+       p->version = hashtable_tokenize(&importer->table, version);
+       list_set_ptr(&p->packages, importer->package -
+                    (struct razor_package *) importer->set->packages.data);
+       r = array_add(&importer->properties, sizeof *r);
+       *r = p - (struct razor_property *) importer->set->properties.data;
+       if (((flags & RAZOR_PROPERTY_TYPE_MASK) == RAZOR_PROPERTY_REQUIRES) &&
+           *name == '/') {
+               r = array_add(&importer->file_requires, sizeof *r);
+               *r = p->name;
+       }
+ }
+ void
+ razor_importer_add_file(struct razor_importer *importer, const char *name)
+ {
+       struct import_entry *e;
+       e = array_add(&importer->files, sizeof *e);
+       e->package = importer->package -
+               (struct razor_package *) importer->set->packages.data;
+       e->name = strdup(name);
+ }
+ struct razor_importer *
+ razor_importer_new(void)
+ {
+       struct razor_importer *importer;
+       importer = zalloc(sizeof *importer);
+       importer->set = razor_set_create();
+       hashtable_init(&importer->table, &importer->set->string_pool);
++      hashtable_init(&importer->details_table,
++                     &importer->set->details_string_pool);
++      hashtable_init(&importer->file_table,
++                     &importer->set->file_string_pool);
+       return importer;
+ }
+ /* Destroy an importer without creating the set. */
+ void
+ razor_importer_destroy(struct razor_importer *importer)
+ {
+       /* FIXME: write this */
+ }
+ static int
+ compare_packages(const void *p1, const void *p2, void *data)
+ {
+       const struct razor_package *pkg1 = p1, *pkg2 = p2;
+       struct razor_set *set = data;
+       char *pool = set->string_pool.data;
+       /* FIXME: what if the flags are different? */
+       if (pkg1->name == pkg2->name)
+               return razor_versioncmp(&pool[pkg1->version], &pool[pkg2->version]);
+       else
+               return strcmp(&pool[pkg1->name], &pool[pkg2->name]);
+ }
+ static int
+ compare_properties(const void *p1, const void *p2, void *data)
+ {
+       const struct razor_property *prop1 = p1, *prop2 = p2;
+       struct razor_set *set = data;
+       char *pool = set->string_pool.data;
+       if (prop1->name != prop2->name)
+               return strcmp(&pool[prop1->name], &pool[prop2->name]);
+       else if (prop1->flags != prop2->flags)
+               return prop1->flags - prop2->flags;
+       else
+               return razor_versioncmp(&pool[prop1->version], &pool[prop2->version]);
+ }
+ static uint32_t *
+ uniqueify_properties(struct razor_set *set)
+ {
+       struct razor_property *rp, *up, *rp_end;
+       struct array *pkgs, *p;
+       struct list_head *r;
+       uint32_t *map, *rmap;
+       int i, count, unique;
+       count = set->properties.size / sizeof(struct razor_property);
+       map = razor_qsort_with_data(set->properties.data,
+                                   count,
+                                   sizeof(struct razor_property),
+                                   compare_properties,
+                                   set);
+       rp_end = set->properties.data + set->properties.size;
+       rmap = malloc(count * sizeof *map);
+       pkgs = zalloc(count * sizeof *pkgs);
+       for (rp = set->properties.data, up = rp, i = 0; rp < rp_end; rp++, i++) {
+               if (rp->name != up->name ||
+                   rp->flags != up->flags ||
+                   rp->version != up->version) {
+                       up++;
+                       up->name = rp->name;
+                       up->flags = rp->flags;
+                       up->version = rp->version;
+               }
+               unique = up - (struct razor_property *) set->properties.data;
+               rmap[map[i]] = unique;
+               r = array_add(&pkgs[unique], sizeof *r);
+               *r = rp->packages;
+       }
+       free(map);
+       if (up != rp)
+               up++;
+       set->properties.size = (void *) up - set->properties.data;
+       rp_end = up;
+       for (rp = set->properties.data, p = pkgs; rp < rp_end; rp++, p++) {
+               list_set_array(&rp->packages, &set->package_pool, p, 0);
+               array_release(p);
+       }
+       free(pkgs);
+       return rmap;
+ }
+ static int
+ compare_filenames(const void *p1, const void *p2, void *data)
+ {
+       const struct import_entry *e1 = p1;
+       const struct import_entry *e2 = p2;
+       const char *n1 = e1->name;
+       const char *n2 = e2->name;
+       /* Need to make sure that the contents of a directory
+        * are sorted immediately after it. So "foo/bar" has to
+        * sort before "foo.conf"
+        *
+        * FIXME: this is about 60% slower than strcmp
+        */
+       while (*n1 && *n2) {
+               if (*n1 < *n2)
+                       return *n2 == '/' ? 1 : -1;
+               else if (*n1 > *n2)
+                       return *n1 == '/' ? -1 : 1;
+               n1++;
+               n2++;
+       }
+       if (*n1)
+               return 1;
+       else if (*n2)
+               return -1;
+       else
+               return 0;
+ }
+ static void
+ count_entries(struct import_directory *d)
+ {
+       struct import_directory *p, *end;
+       p = d->files.data;
+       end = d->files.data + d->files.size;
+       d->count = 0;
+       while (p < end) {
+               count_entries(p);
+               d->count += p->count + 1;
+               p++;
+       }
+ }
+ static void
+ serialize_files(struct razor_set *set,
+               struct import_directory *d, struct array *array)
+ {
+       struct import_directory *p, *end;
+       struct razor_entry *e = NULL;
+       uint32_t s;
+       p = d->files.data;
+       end = d->files.data + d->files.size;
+       s = array->size / sizeof *e + d->files.size / sizeof *p;
+       while (p < end) {
+               e = array_add(array, sizeof *e);
+               e->name = p->name;
+               e->flags = 0;
+               e->start = p->count > 0 ? s : 0;
+               s += p->count;
+               list_set_array(&e->packages, &set->package_pool, &p->packages, 0);
+               array_release(&p->packages);
+               p++;
+       }
+       if (e != NULL)
+               e->flags |= RAZOR_ENTRY_LAST;
+       p = d->files.data;
+       end = d->files.data + d->files.size;
+       while (p < end) {
+               serialize_files(set, p, array);
+               p++;
+       }
+ }
+ static void
+ remap_property_package_links(struct array *properties, uint32_t *rmap)
+ {
+       struct razor_property *p, *end;
+       end = properties->data + properties->size;
+       for (p = properties->data; p < end; p++)
+               list_remap_head(&p->packages, rmap);
+ }
+ static void
+ build_file_tree(struct razor_importer *importer)
+ {
+       int count, i, length;
+       struct import_entry *filenames;
+       char *f, *end;
+       uint32_t name, *r;
+       char dirname[256];
+       struct import_directory *d, root;
+       struct razor_entry *e;
+       count = importer->files.size / sizeof (struct import_entry);
+       razor_qsort_with_data(importer->files.data,
+                             count,
+                             sizeof (struct import_entry),
+                             compare_filenames,
+                             NULL);
+       root.name = hashtable_tokenize(&importer->table, "");
+       array_init(&root.files);
+       array_init(&root.packages);
+       root.last = NULL;
+       filenames = importer->files.data;
+       for (i = 0; i < count; i++) {
+               f = filenames[i].name;
+               if (*f != '/')
+                       continue;
+               f++;
+               d = &root;
+               while (*f) {
+                       end = strchr(f, '/');
+                       if (end == NULL)
+                               end = f + strlen(f);
+                       length = end - f;
+                       memcpy(dirname, f, length);
+                       dirname[length] ='\0';
+                       name = hashtable_tokenize(&importer->table, dirname);
+                       if (d->last == NULL || d->last->name != name) {
+                               d->last = array_add(&d->files, sizeof *d);
+                               d->last->name = name;
+                               d->last->last = NULL;
+                               array_init(&d->last->files);
+                               array_init(&d->last->packages);
+                       }
+                       d = d->last;
+                       f = end + 1;
+                       if (*end == '\0')
+                               break;
+               }
+               r = array_add(&d->packages, sizeof *r);
+               *r = filenames[i].package;
+               free(filenames[i].name);
+       }
+       count_entries(&root);
+       e = importer->set->files.data;
+       e->name = root.name;
+       e->flags = RAZOR_ENTRY_LAST;
+       e->start = importer->files.size ? 1 : 0;
+       list_set_empty(&e->packages);
+       serialize_files(importer->set, &root, &importer->set->files);
+       array_release(&importer->files);
+ }
+ static void
+ list_to_array(struct list *list, struct array *array)
+ {
+       uint32_t *item;
+       while (list) {
+                item = array_add(array, sizeof *item);
+                *item = list->data;
+                list = list_next(list);
+       }
+ }
+ static int
+ compare_file_requires(const void *p1, const void *p2, void *data)
+ {
+       uint32_t *f1 = (void *)p1, *f2 = (void *)p2;
+       const char *pool = data;
+       return strcmp(&pool[*f1], &pool[*f2]);
+ }
+ static void
+ find_file_provides(struct razor_importer *importer)
+ {
+       struct razor_property *prop;
+       struct razor_entry *top, *entry;
+       struct razor_package *packages;
+       struct array pkgprops;
+       struct list *pkg;
+       uint32_t *req, *req_start, *req_end;
+       uint32_t *map, *newprop;
+       char *pool;
+       pool = importer->set->string_pool.data;
+       packages = importer->set->packages.data;
+       top = importer->set->files.data;
+       req = req_start = importer->file_requires.data;
+       req_end = importer->file_requires.data + importer->file_requires.size;
+       map = razor_qsort_with_data(req, req_end - req, sizeof *req,
+                                   compare_file_requires, pool);
+       free(map);
+       for (req = req_start; req < req_end; req++) {
+               if (req > req_start && req[0] == req[-1])
+                       continue;
+               entry = razor_set_find_entry(importer->set, top, &pool[*req]);
+               if (!entry)
+                       continue;
+               for (pkg = list_first(&entry->packages, &importer->set->package_pool); pkg; pkg = list_next(pkg)) {
+                       prop = array_add(&importer->set->properties, sizeof *prop);
+                       prop->name = *req;
+                       prop->flags =
+                               RAZOR_PROPERTY_PROVIDES | RAZOR_PROPERTY_EQUAL;
+                       prop->version = hashtable_tokenize(&importer->table, "");
+                       list_set_ptr(&prop->packages, pkg->data);
+                       /* Update property list of pkg */
+                       array_init(&pkgprops);
+                       list_to_array(list_first(&packages[pkg->data].properties, &importer->set->property_pool), &pkgprops);
+                       newprop = array_add(&pkgprops, sizeof *newprop);
+                       *newprop = prop - (struct razor_property *)importer->set->properties.data;
+                       list_set_array(&packages[pkg->data].properties, &importer->set->property_pool, &pkgprops, 1);
+                       array_release(&pkgprops);
+               }
+       }
+       array_release(&importer->file_requires);
+ }
+ static void
+ build_package_file_lists(struct razor_set *set, uint32_t *rmap)
+ {
+       struct razor_package *p, *packages;
+       struct array *pkgs;
+       struct razor_entry *e, *end;
+       struct list *r;
+       uint32_t *q;
+       int i, count;
+       count = set->packages.size / sizeof *p;
+       pkgs = zalloc(count * sizeof *pkgs);
+       end = set->files.data + set->files.size;
+       for (e = set->files.data; e < end; e++) {
+               list_remap_head(&e->packages, rmap);
+               r = list_first(&e->packages, &set->package_pool);
+               while (r) {
+                       q = array_add(&pkgs[r->data], sizeof *q);
+                       *q = e - (struct razor_entry *) set->files.data;
+                       r = list_next(r);
+               }
+       }
+       packages = set->packages.data;
+       for (i = 0; i < count; i++) {
+               list_set_array(&packages[i].files, &set->file_pool, &pkgs[i], 0);
+               array_release(&pkgs[i]);
+       }
+       free(pkgs);
+ }
+ struct razor_set *
+ razor_importer_finish(struct razor_importer *importer)
+ {
+       struct razor_set *set;
+       uint32_t *map, *rmap;
+       int i, count;
+       build_file_tree(importer);
+       find_file_provides(importer);
+       map = uniqueify_properties(importer->set);
+       list_remap_pool(&importer->set->property_pool, map);
+       free(map);
+       count = importer->set->packages.size / sizeof(struct razor_package);
+       map = razor_qsort_with_data(importer->set->packages.data,
+                                   count,
+                                   sizeof(struct razor_package),
+                                   compare_packages,
+                                   importer->set);
+       rmap = malloc(count * sizeof *rmap);
+       for (i = 0; i < count; i++)
+               rmap[map[i]] = i;
+       free(map);
+       list_remap_pool(&importer->set->package_pool, rmap);
+       build_package_file_lists(importer->set, rmap);
+       remap_property_package_links(&importer->set->properties, rmap);
+       free(rmap);
+       set = importer->set;
+       hashtable_release(&importer->table);
+       free(importer);
+       return set;
+ }
index 0000000,bcc5e11..94eac41
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,177 +1,194 @@@
+ #ifndef _RAZOR_INTERNAL_H_
+ #define _RAZOR_INTERNAL_H_
+ #include <stdlib.h>
+ #include <stdint.h>
+ void *zalloc(size_t size);
+ struct array {
+       void *data;
+       int size, alloc;
+ };
+ void array_init(struct array *array);
+ void array_release(struct array *array);
+ void *array_add(struct array *array, int size);
+ struct list_head {
+       uint32_t list_ptr : 24;
+       uint32_t flags    : 8;
+ };
+ struct list {
+       uint32_t data  : 24;
+       uint32_t flags : 8;
+ };
+ void list_set_empty(struct list_head *head);
+ void list_set_ptr(struct list_head *head, uint32_t ptr);
+ void list_set_array(struct list_head *head, struct array *pool, struct array *items, int force_indirect);
+ struct list *list_first(struct list_head *head, struct array *pool);
+ struct list *list_next(struct list *list);
+ void list_remap_pool(struct array *pool, uint32_t *map);
+ void list_remap_head(struct list_head *list, uint32_t *map);
+ struct hashtable {
+       struct array buckets;
+       struct array *pool;
+ };
+ void hashtable_init(struct hashtable *table, struct array *pool);
+ void hashtable_release(struct hashtable *table);
+ uint32_t hashtable_insert(struct hashtable *table, const char *key);
+ uint32_t hashtable_lookup(struct hashtable *table, const char *key);
+ uint32_t hashtable_tokenize(struct hashtable *table, const char *string);
+ struct razor_set_section {
+       uint32_t type;
+       uint32_t offset;
+       uint32_t size;
+ };
+ struct razor_set_header {
+       uint32_t magic;
+       uint32_t version;
+       struct razor_set_section sections[0];
+ };
 -#define RAZOR_MAGIC 0x7a7a7a7a
++#define RAZOR_MAGIC           0x7a7a7a7a
++#define RAZOR_DETAILS_MAGIC   0x7a7a7a7b
++#define RAZOR_FILES_MAGIC     0x7a7a7a7c
+ #define RAZOR_VERSION 1
 -#define RAZOR_STRING_POOL     0
 -#define RAZOR_PACKAGES                1
 -#define RAZOR_PROPERTIES      2
 -#define RAZOR_FILES           3
 -#define RAZOR_PACKAGE_POOL    4
 -#define RAZOR_PROPERTY_POOL   5
 -#define RAZOR_FILE_POOL               6
++#define RAZOR_STRING_POOL             0
++#define RAZOR_PACKAGES                        1
++#define RAZOR_PROPERTIES              2
++#define RAZOR_PACKAGE_POOL            3
++#define RAZOR_PROPERTY_POOL           4
++
++#define RAZOR_DETAILS_STRING_POOL     0
++
++#define RAZOR_FILES                   0
++#define RAZOR_FILE_POOL                       1
++#define RAZOR_FILE_STRING_POOL                2
+ struct razor_package {
 -      uint32_t name  : 24;
 -      uint32_t flags : 8;
++      uint name  : 24;
++      uint flags : 8;
+       uint32_t version;
+       uint32_t arch;
++      uint32_t summary;
++      uint32_t description;
++      uint32_t url;
++      uint32_t license;
+       struct list_head properties;
+       struct list_head files;
+ };
++
+ struct razor_property {
+       uint32_t name;
+       uint32_t flags;
+       uint32_t version;
+       struct list_head packages;
+ };
+ struct razor_entry {
+       uint32_t name  : 24;
+       uint32_t flags : 8;
+       uint32_t start;
+       struct list_head packages;
+ };
+ #define RAZOR_ENTRY_LAST      0x80
+ struct razor_set {
+       struct array string_pool;
+       struct array packages;
+       struct array properties;
+       struct array files;
+       struct array package_pool;
+       struct array property_pool;
+       struct array file_pool;
++      struct array file_string_pool;
++      struct array details_string_pool;
+       struct razor_set_header *header;
++      struct razor_set_header *details_header;
++      struct razor_set_header *files_header;
+ };
+ struct import_entry {
+       uint32_t package;
+       char *name;
+ };
+ struct import_directory {
+       uint32_t name, count;
+       struct array files;
+       struct array packages;
+       struct import_directory *last;
+ };
+ struct razor_importer {
+       struct razor_set *set;
+       struct hashtable table;
++      struct hashtable file_table;
++      struct hashtable details_table;
+       struct razor_package *package;
+       struct array properties;
+       struct array files;
+       struct array file_requires;
+ };
+ struct razor_package_iterator {
+       struct razor_set *set;
+       struct razor_package *package, *end;
+       struct list *index;
+       int free_index;
+ };
+ void
+ razor_package_iterator_init_for_property(struct razor_package_iterator *pi,
+                                        struct razor_set *set,
+                                        struct razor_property *property);
+ struct razor_property_iterator {
+       struct razor_set *set;
+       struct razor_property *property, *end;
+       struct list *index;
+ };
+ #define ALIGN(value, base) (((value) + (base - 1)) & ~((base) - 1))
+ struct razor_entry *
+ razor_set_find_entry(struct razor_set *set,
+                    struct razor_entry *dir, const char *pattern);
+ struct razor_merger *
+ razor_merger_create(struct razor_set *set1, struct razor_set *set2);
+ void
+ razor_merger_add_package(struct razor_merger *merger,
+                        struct razor_package *package);
+ struct razor_set *
+ razor_merger_finish(struct razor_merger *merger);
+ /* Utility functions */
+ int razor_create_dir(const char *root, const char *path);
+ int razor_write(int fd, const void *data, size_t size);
+ typedef int (*razor_compare_with_data_func_t)(const void *p1,
+                                             const void *p,
+                                             void *data);
+ uint32_t *
+ razor_qsort_with_data(void *base, size_t nelem, size_t size,
+                     razor_compare_with_data_func_t compare, void *data);
+ #endif /* _RAZOR_INTERNAL_H_ */
index 0000000,210da4c..cddcc1b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,463 +1,588 @@@
+ /*
+  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
+  * Copyright (C) 2008  Red Hat, Inc
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 2 of the License, or
+  * (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License along
+  * with this program; if not, write to the Free Software Foundation, Inc.,
+  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+  */
+ #define _GNU_SOURCE
+ #include <stdlib.h>
+ #include <stddef.h>
+ #include <stdint.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <sys/mman.h>
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <errno.h>
+ #include <ctype.h>
+ #include <fnmatch.h>
+ #include "razor.h"
+ #include "razor-internal.h"
+ void *
+ zalloc(size_t size)
+ {
+       void *p;
+       p = malloc(size);
+       memset(p, 0, size);
+       return p;
+ }
+ struct razor_set_section razor_sections[] = {
+       { RAZOR_STRING_POOL,    offsetof(struct razor_set, string_pool) },
+       { RAZOR_PACKAGES,       offsetof(struct razor_set, packages) },
+       { RAZOR_PROPERTIES,     offsetof(struct razor_set, properties) },
 -      { RAZOR_FILES,          offsetof(struct razor_set, files) },
+       { RAZOR_PACKAGE_POOL,   offsetof(struct razor_set, package_pool) },
+       { RAZOR_PROPERTY_POOL,  offsetof(struct razor_set, property_pool) },
 -      { RAZOR_FILE_POOL,      offsetof(struct razor_set, file_pool) },
+ };
++struct razor_set_section razor_files_sections[] = {
++      { RAZOR_FILES,                  offsetof(struct razor_set, files) },
++      { RAZOR_FILE_POOL,              offsetof(struct razor_set, file_pool) },
++      { RAZOR_FILE_STRING_POOL,       offsetof(struct razor_set, file_string_pool) },
++};
++
++struct razor_set_section razor_details_sections[] = {
++      { RAZOR_DETAILS_STRING_POOL,    offsetof(struct razor_set, details_string_pool) },
++};
+ struct razor_set *
+ razor_set_create(void)
+ {
+       struct razor_set *set;
+       struct razor_entry *e;
+       char *empty;
+       set = zalloc(sizeof *set);
+       e = array_add(&set->files, sizeof *e);
+       empty = array_add(&set->string_pool, 1);
+       *empty = '\0';
+       e->name = 0;
+       e->flags = RAZOR_ENTRY_LAST;
+       e->start = 0;
+       list_set_empty(&e->packages);
+       return set;
+ }
+ struct razor_set *
+ razor_set_open(const char *filename)
+ {
+       struct razor_set *set;
+       struct razor_set_section *s;
+       struct stat stat;
+       struct array *array;
+       int fd;
+       set = zalloc(sizeof *set);
+       fd = open(filename, O_RDONLY);
+       if (fstat(fd, &stat) < 0)
+               return NULL;
+       set->header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (set->header == MAP_FAILED) {
+               free(set);
+               return NULL;
+       }
+       for (s = set->header->sections; ~s->type; s++) {
+               if (s->type >= ARRAY_SIZE(razor_sections))
+                       continue;
+               if (s->type != razor_sections[s->type].type)
+                       continue;
+               array = (void *) set + razor_sections[s->type].offset;
+               array->data = (void *) set->header + s->offset;
+               array->size = s->size;
+               array->alloc = s->size;
+       }
+       close(fd);
+       return set;
+ }
+ void
++razor_set_open_details(struct razor_set *set, const char *filename)
++{
++      struct razor_set_section *s;
++      struct stat stat;
++      struct array *array;
++      int fd;
++
++      fd = open(filename, O_RDONLY);
++      if (fstat(fd, &stat) < 0)
++              return;
++      set->details_header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
++      if (set->details_header == MAP_FAILED)
++              return;
++
++      for (s = set->details_header->sections; ~s->type; s++) {
++              if (s->type >= ARRAY_SIZE(razor_details_sections))
++                      continue;
++              if (s->type != razor_details_sections[s->type].type)
++                      continue;
++              array = (void *) set + razor_details_sections[s->type].offset;
++              array->data = (void *) set->details_header + s->offset;
++              array->size = s->size;
++              array->alloc = s->size;
++      }
++      close(fd);
++}
++
++void
++razor_set_open_files(struct razor_set *set, const char *filename)
++{
++      struct razor_set_section *s;
++      struct stat stat;
++      struct array *array;
++      int fd;
++
++      fd = open(filename, O_RDONLY);
++      if (fstat(fd, &stat) < 0)
++              return;
++      set->files_header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
++      if (set->files_header == MAP_FAILED)
++              return;
++
++      for (s = set->files_header->sections; ~s->type; s++) {
++              if (s->type >= ARRAY_SIZE(razor_files_sections))
++                      continue;
++              if (s->type != razor_files_sections[s->type].type)
++                      continue;
++              array = (void *) set + razor_files_sections[s->type].offset;
++              array->data = (void *) set->files_header + s->offset;
++              array->size = s->size;
++              array->alloc = s->size;
++      }
++      close(fd);
++}
++
++void
+ razor_set_destroy(struct razor_set *set)
+ {
+       unsigned int size;
+       struct array *a;
+       int i;
+       if (set->header) {
+               for (i = 0; set->header->sections[i].type; i++)
+                       ;
+               size = set->header->sections[i].type;
+               munmap(set->header, size);
+       } else {
+               for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
+                       a = (void *) set + razor_sections[i].offset;
+                       free(a->data);
+               }
+       }
++      if (set->details_header) {
++              for (i = 0; set->details_header->sections[i].type; i++)
++                      ;
++              size = set->details_header->sections[i].type;
++              munmap(set->details_header, size);
++      } else {
++              for (i = 0; i < ARRAY_SIZE(razor_details_sections); i++) {
++                      a = (void *) set + razor_details_sections[i].offset;
++                      free(a->data);
++              }
++      }
++
++      if (set->files_header) {
++              for (i = 0; set->files_header->sections[i].type; i++)
++                      ;
++              size = set->files_header->sections[i].type;
++              munmap(set->files_header, size);
++      } else {
++              for (i = 0; i < ARRAY_SIZE(razor_files_sections); i++) {
++                      a = (void *) set + razor_files_sections[i].offset;
++                      free(a->data);
++              }
++      }
++
+       free(set);
+ }
 -int
 -razor_set_write_to_fd(struct razor_set *set, int fd)
++static int
++razor_set_write_sections_to_fd(struct razor_set *set, int fd, int magic,
++                             struct razor_set_section *sections,
++                             size_t array_size)
+ {
+       char data[4096];
+       struct razor_set_header *header = (struct razor_set_header *) data;
+       struct array *a;
+       uint32_t offset;
+       int i;
+       memset(data, 0, sizeof data);
 -      header->magic = RAZOR_MAGIC;
++      header->magic = magic;
+       header->version = RAZOR_VERSION;
+       offset = sizeof data;
 -      for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
 -              if (razor_sections[i].type != i)
++      for (i = 0; i < array_size; i++) {
++              if (sections[i].type != i)
+                       continue;
 -              a = (void *) set + razor_sections[i].offset;
++              a = (void *) set + sections[i].offset;
+               header->sections[i].type = i;
+               header->sections[i].offset = offset;
+               header->sections[i].size = a->size;
+               offset += ALIGN(a->size, 4096);
+       }
+       header->sections[i].type = ~0;
+       header->sections[i].offset = 0;
+       header->sections[i].size = 0;
+       razor_write(fd, data, sizeof data);
+       memset(data, 0, sizeof data);
 -      for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
 -              if (razor_sections[i].type != i)
++      for (i = 0; i < array_size; i++) {
++              if (sections[i].type != i)
+                       continue;
 -              a = (void *) set + razor_sections[i].offset;
++              a = (void *) set + sections[i].offset;
+               razor_write(fd, a->data, a->size);
+               razor_write(fd, data, ALIGN(a->size, 4096) - a->size);
+       }
+       return 0;
+ }
+ int
 -razor_set_write(struct razor_set *set, const char *filename)
++razor_set_write_to_fd(struct razor_set *set, int fd,
++                    enum razor_repo_file_type type)
++{
++      switch (type) {
++      case RAZOR_REPO_FILE_MAIN:
++              return razor_set_write_sections_to_fd(set, fd, RAZOR_MAGIC,
++                                                    razor_sections,
++                                                    ARRAY_SIZE(razor_sections));
++
++      case RAZOR_REPO_FILE_DETAILS:
++              return razor_set_write_sections_to_fd(set, fd, RAZOR_DETAILS_MAGIC,
++                                                    razor_details_sections,
++                                                    ARRAY_SIZE(razor_details_sections));
++      case RAZOR_REPO_FILE_FILES:
++              return razor_set_write_sections_to_fd(set, fd, RAZOR_FILES_MAGIC,
++                                                    razor_files_sections,
++                                                    ARRAY_SIZE(razor_files_sections));
++      default:
++              return -1;
++      }
++}
++
++int
++razor_set_write(struct razor_set *set, const char *filename,
++              enum razor_repo_file_type type)
+ {
+       int fd, status;
+       fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+       if (fd < 0)
+               return -1;
 -      status = razor_set_write_to_fd(set, fd);
++      status = razor_set_write_to_fd(set, fd, type);
+       if (status) {
+           close(fd);
+           return status;
+       }
+       return close(fd);
+ }
 -
+ void
+ razor_build_evr(char *evr_buf, int size, const char *epoch,
+               const char *version, const char *release)
+ {
+       int len;
+       if (!version || !*version) {
+               *evr_buf = '\0';
+               return;
+       }
+       if (epoch && *epoch && strcmp(epoch, "0") != 0) {
+               len = snprintf(evr_buf, size, "%s:", epoch);
+               evr_buf += len;
+               size -= len;
+       }
+       len = snprintf(evr_buf, size, "%s", version);
+       evr_buf += len;
+       size -= len;
+       if (release && *release)
+               snprintf(evr_buf, size, "-%s", release);
+ }
+ int
+ razor_versioncmp(const char *s1, const char *s2)
+ {
+       const char *p1, *p2;
+       long n1, n2;
+       int res;
+       n1 = strtol(s1, (char **) &p1, 10);
+       n2 = strtol(s2, (char **) &p2, 10);
+       /* Epoch; if one but not the other has an epoch set, default
+        * the epoch-less version to 0. */
+       res = (*p1 == ':') - (*p2 == ':');
+       if (res < 0) {
+               n1 = 0;
+               p1 = s1;
+               p2++;
+       } else if (res > 0) {
+               p1++;
+               n2 = 0;
+               p2 = s2;
+       }
+       if (n1 != n2)
+               return n1 - n2;
+       while (*p1 && *p2) {
+               if (*p1 != *p2)
+                       return *p1 - *p2;
+               p1++;
+               p2++;
+               if (isdigit(*p1) && isdigit(*p2))
+                       return razor_versioncmp(p1, p2);
+       }
+       return *p1 - *p2;
+ }
+ struct razor_package *
+ razor_set_get_package(struct razor_set *set, const char *package)
+ {
+       struct razor_package_iterator *pi;
+       struct razor_package *p;
+       const char *name, *version, *arch;
+       pi = razor_package_iterator_create(set);
+       while (razor_package_iterator_next(pi, &p, &name, &version, &arch)) {
+               if (strcmp(package, name) == 0)
+                       break;
+       }
+       razor_package_iterator_destroy(pi);
+       return p;
+ }
++void
++razor_package_get_details(struct razor_set *set, struct razor_package *package,
++                        const char **summary, const char **description,
++                        const char **url, const char **license)
++{
++      const char *pool = set->details_string_pool.data;
++
++      *summary = &pool[package->summary];
++      *description = &pool[package->description];
++      *url = &pool[package->url];
++      *license = &pool[package->license];
++}
++
+ struct razor_entry *
+ razor_set_find_entry(struct razor_set *set,
+                    struct razor_entry *dir, const char *pattern)
+ {
+       struct razor_entry *e;
+       const char *n, *pool = set->string_pool.data;
+       int len;
+       e = (struct razor_entry *) set->files.data + dir->start;
+       do {
+               n = pool + e->name;
+               if (strcmp(pattern + 1, n) == 0)
+                       return e;
+               len = strlen(n);
+               if (e->start != 0 && strncmp(pattern + 1, n, len) == 0 &&
+                   pattern[len + 1] == '/') {
+                       return razor_set_find_entry(set, e, pattern + len + 1);
+               }
+       } while (!((e++)->flags & RAZOR_ENTRY_LAST));
+       return NULL;
+ }
+ static void
+ list_dir(struct razor_set *set, struct razor_entry *dir,
+        char *prefix, const char *pattern)
+ {
+       struct razor_entry *e;
+       const char *n, *pool = set->string_pool.data;
+       e = (struct razor_entry *) set->files.data + dir->start;
+       do {
+               n = pool + e->name;
+               if (pattern && pattern[0] && fnmatch(pattern, n, 0) != 0)
+                       continue;
+               printf("%s/%s\n", prefix, n);
+               if (e->start) {
+                       char *sub = prefix + strlen (prefix);
+                       *sub = '/';
+                       strcpy (sub + 1, n);
+                       list_dir(set, e, prefix, pattern);
+                       *sub = '\0';
+               }
+       } while (!((e++)->flags & RAZOR_ENTRY_LAST));
+ }
+ void
+ razor_set_list_files(struct razor_set *set, const char *pattern)
+ {
+       struct razor_entry *e;
+       char buffer[512], *p, *base;
+       if (pattern == NULL || !strcmp (pattern, "/")) {
+               buffer[0] = '\0';
+               list_dir(set, set->files.data, buffer, NULL);
+               return;
+       }
+       strcpy(buffer, pattern);
+       e = razor_set_find_entry(set, set->files.data, buffer);
+       if (e && e->start > 0) {
+               base = NULL;
+       } else {
+               p = strrchr(buffer, '/');
+               if (p) {
+                       *p = '\0';
+                       base = p + 1;
+               } else {
+                       base = NULL;
+               }
+       }
+       e = razor_set_find_entry(set, set->files.data, buffer);
+       if (e->start != 0)
+               list_dir(set, e, buffer, base);
+ }
+ static struct list *
+ list_package_files(struct razor_set *set, struct list *r,
+                  struct razor_entry *dir, uint32_t end,
+                  char *prefix)
+ {
+       struct razor_entry *e, *f, *entries;
+       uint32_t next, file;
+       char *pool;
+       int len;
+       entries = (struct razor_entry *) set->files.data;
+       pool = set->string_pool.data;
+       e = entries + dir->start;
+       do {
+               if (entries + r->data == e) {
+                       printf("%s/%s\n", prefix, pool + e->name);
+                       r = list_next(r);
+                       if (!r)
+                               return NULL;
+                       if (r->data >= end)
+                               return r;
+               }
+       } while (!((e++)->flags & RAZOR_ENTRY_LAST));
+       e = entries + dir->start;
+       do {
+               if (e->start == 0)
+                       continue;
+               if (e->flags & RAZOR_ENTRY_LAST)
+                       next = end;
+               else {
+                       f = e + 1;
+                       while (f->start == 0 && !(f->flags & RAZOR_ENTRY_LAST))
+                               f++;
+                       if (f->start == 0)
+                               next = end;
+                       else
+                               next = f->start;
+               }
+               file = r->data;
+               if (e->start <= file && file < next) {
+                       len = strlen(prefix);
+                       prefix[len] = '/';
+                       strcpy(prefix + len + 1, pool + e->name);
+                       r = list_package_files(set, r, e, next, prefix);
+                       prefix[len] = '\0';
+               }
+       } while (!((e++)->flags & RAZOR_ENTRY_LAST) && r != NULL);
+       return r;
+ }
+ void
+ razor_set_list_package_files(struct razor_set *set, const char *name)
+ {
+       struct razor_package *package;
+       struct list *r;
+       uint32_t end;
+       char buffer[512];
+       package = razor_set_get_package(set, name);
+       r = list_first(&package->files, &set->file_pool);
+       end = set->files.size / sizeof (struct razor_entry);
+       buffer[0] = '\0';
+       list_package_files(set, r, set->files.data, end, buffer);
+ }
+ /* The diff order matters.  We should sort the packages so that a
+  * REMOVE of a package comes before the INSTALL, and so that all
+  * requires for a package have been installed before the package.
+  **/
+ void
+ razor_set_diff(struct razor_set *set, struct razor_set *upstream,
+              razor_package_callback_t callback, void *data)
+ {
+       struct razor_package_iterator *pi1, *pi2;
+       struct razor_package *p1, *p2;
+       const char *name1, *name2, *version1, *version2, *arch1, *arch2;
+       int res;
+       pi1 = razor_package_iterator_create(set);
+       pi2 = razor_package_iterator_create(upstream);
+       razor_package_iterator_next(pi1, &p1, &name1, &version1, &arch1);
+       razor_package_iterator_next(pi2, &p2, &name2, &version2, &arch2);
+       while (p1 || p2) {
+               if (p1 && p2) {
+                       res = strcmp(name1, name2);
+                       if (res == 0)
+                               res = razor_versioncmp(version1, version2);
+               } else {
+                       res = 0;
+               }
+               if (p2 == NULL || res < 0)
+                       callback(name1, version1, NULL, arch1, data);
+               else if (p1 == NULL || res > 0)
+                       callback(name2, NULL, version2, arch2, data);
+               if (p1 != NULL && res <= 0)
+                       razor_package_iterator_next(pi1, &p1,
+                                                   &name1, &version1, &arch1);
+               if (p2 != NULL && res >= 0)
+                       razor_package_iterator_next(pi2, &p2,
+                                                   &name2, &version2, &arch2);
+       }
+       razor_package_iterator_destroy(pi1);
+       razor_package_iterator_destroy(pi2);
+ }
@@@ -26,27 -26,31 +26,37 @@@ struct razor_set
  struct razor_package;
  struct razor_property;
  
 +enum razor_repo_file_type {
 +      RAZOR_REPO_FILE_MAIN,
 +      RAZOR_REPO_FILE_DETAILS,
 +      RAZOR_REPO_FILE_FILES
 +};
 +
- enum razor_property_type {
-       RAZOR_PROPERTY_REQUIRES,
-       RAZOR_PROPERTY_PROVIDES,
-       RAZOR_PROPERTY_CONFLICTS,
-       RAZOR_PROPERTY_OBSOLETES
+ enum razor_property_flags {
+       RAZOR_PROPERTY_LESS             = 1 << 0,
+       RAZOR_PROPERTY_GREATER          = 1 << 1,
+       RAZOR_PROPERTY_EQUAL            = 1 << 2,
+       RAZOR_PROPERTY_RELATION_MASK    =
+               RAZOR_PROPERTY_LESS |
+               RAZOR_PROPERTY_GREATER |
+               RAZOR_PROPERTY_EQUAL,
+       RAZOR_PROPERTY_REQUIRES         = 0 << 3,
+       RAZOR_PROPERTY_PROVIDES         = 1 << 3,
+       RAZOR_PROPERTY_CONFLICTS        = 2 << 3,
+       RAZOR_PROPERTY_OBSOLETES        = 3 << 3,
+       RAZOR_PROPERTY_TYPE_MASK        = 3 << 3,
+               
+       RAZOR_PROPERTY_PRE              = 1 << 5,
+       RAZOR_PROPERTY_POST             = 1 << 6,
+       RAZOR_PROPERTY_PREUN            = 1 << 7,
+       RAZOR_PROPERTY_POSTUN           = 1 << 8
  };
  
- enum razor_version_relation {
-       RAZOR_VERSION_LESS,
-       RAZOR_VERSION_LESS_OR_EQUAL,
-       RAZOR_VERSION_EQUAL,
-       RAZOR_VERSION_GREATER_OR_EQUAL,
-       RAZOR_VERSION_GREATER
- };
- extern const char * const razor_version_relations[];
+ const char *
+ razor_property_relation_to_string(struct razor_property *p);
+ const char *
+ razor_property_type_to_string(struct razor_property *p);
  
  struct razor_set *razor_set_create(void);
  struct razor_set *razor_set_open(const char *filename);
@@@ -186,16 -149,10 +165,15 @@@ void razor_importer_begin_package(struc
                                  const char *name,
                                  const char *version,
                                  const char *arch);
 +void razor_importer_add_details(struct razor_importer *importer,
 +                              const char *summary,
 +                              const char *description,
 +                              const char *url,
 +                              const char *license);
  void razor_importer_add_property(struct razor_importer *importer,
                                 const char *name,
-                                enum razor_version_relation relation,
-                                const char *version,
-                                enum razor_property_type type);
+                                uint32_t flags,
+                                const char *version);
  void razor_importer_add_file(struct razor_importer *importer,
                             const char *name);
  void razor_importer_finish_package(struct razor_importer *importer);
diff --cc librazor/root.c
index 0000000,8c032ef..dc3b230
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,168 +1,168 @@@
+ #include <stdlib.h>
+ #include <stdint.h>
+ #include <stdio.h>
+ #include <sys/stat.h>
+ #include <dirent.h>
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include "razor.h"
+ #include "razor-internal.h"
+ static const char system_repo_filename[] = "system.repo";
+ static const char next_repo_filename[] = "system-next.repo";
+ static const char razor_root_path[] = "/var/lib/razor";
+ struct razor_root {
+       struct razor_set *system;
+       struct razor_set *next;
+       int fd;
+       char path[PATH_MAX];
+       char new_path[PATH_MAX];
+ };
+ int
+ razor_root_create(const char *root)
+ {
+       struct stat buf;
+       struct razor_set *set;
+       char path[PATH_MAX];
+       if (stat(root, &buf) < 0) {
+               if (mkdir(root, 0777) < 0) {
+                       fprintf(stderr,
+                               "could not create install root \"%s\"\n",
+                               root);
+                       return -1;
+               }
+               fprintf(stderr, "created install root \"%s\"\n", root);
+       } else if (!S_ISDIR(buf.st_mode)) {
+               fprintf(stderr,
+                       "install root \"%s\" exists, but is not a directory\n",
+                       root);
+               return -1;
+       }
+       snprintf(path, sizeof path, "%s/%s",
+                razor_root_path, system_repo_filename);
+       if (razor_create_dir(root, path) < 0) {
+               fprintf(stderr, "could not create %s%s\n",
+                       root, razor_root_path);
+               return -1;
+       }
+       set = razor_set_create();
+       snprintf(path, sizeof path, "%s%s/%s",
+                root, razor_root_path, system_repo_filename);
+       if (stat(path, &buf) == 0) {
+               fprintf(stderr,
+                       "a razor install root is already initialized\n");
+               return -1;
+       }
 -      if (razor_set_write(set, path) < 0) {
++      if (razor_set_write(set, path, RAZOR_REPO_FILE_MAIN) < 0) {
+               fprintf(stderr, "could not write initial package set\n");
+               return -1;
+       }
+       razor_set_destroy(set);
+       return 0;
+ }
+ struct razor_root *
+ razor_root_open(const char *root, int flags)
+ {
+       struct razor_root *image;
+       image = malloc(sizeof *image);
+       if (image == NULL)
+               return NULL;
+       /* Create the new next repo file up front to ensure exclusive
+        * access. */
+       snprintf(image->new_path, sizeof image->new_path,
+                "%s%s/%s", root, razor_root_path, next_repo_filename);
+       image->fd = open(image->new_path,
+                        O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666);
+       if (image->fd < 0) {
+               fprintf(stderr, "failed to get lock file, "
+                       "maybe previous operation crashed?\n");
+               /* FIXME: Use fcntl advisory locking on the system
+                * package set file to figure out whether previous
+                * operation crashed or is still in progress. */
+               free(image);
+               return NULL;
+       }
+       snprintf(image->path, sizeof image->path,
+                "%s%s/%s", root, razor_root_path, system_repo_filename);
+       image->system = razor_set_open(image->path);
+       if (image->system == NULL) {
+               unlink(image->new_path);
+               close(image->fd);
+               free(image);
+               return NULL;
+       }
+       return image;
+ }
+ struct razor_set *
+ razor_root_open_read_only(const char *root)
+ {
+       char path[PATH_MAX];
+       snprintf(path, sizeof path, "%s%s/%s",
+                root, razor_root_path, system_repo_filename);
+       return razor_set_open(path);
+ }
+ struct razor_transaction *
+ razor_root_create_transaction(struct razor_root *image,
+                             struct razor_set *upstream)
+ {
+       /* FIXME: This should take a number of upstream repos. */
+       return razor_transaction_create(image->system, upstream);
+ }
+ int
+ razor_root_close(struct razor_root *image)
+ {
+       unlink(image->new_path);
+       close(image->fd);
+       free(image);
+       return 0;
+ }
+ void
+ razor_root_update(struct razor_root *root, struct razor_set *next)
+ {
 -      razor_set_write_to_fd(next, root->fd);
++      razor_set_write_to_fd(next, root->fd, RAZOR_REPO_FILE_MAIN);
+       root->next = next;
+       /* Sync the new repo file so the new package set is on disk
+        * before we start upgrading. */
+       fsync(root->fd);
+       printf("wrote %s\n", root->new_path);
+ }
+ int
+ razor_root_commit(struct razor_root *image)
+ {
+       /* Make it so. */
+       rename(image->new_path, image->path);
+       printf("renamed %s to %s\n", image->new_path, image->path);
+       close(image->fd);
+       free(image);
+       return 0;
+ }
+ void
+ razor_root_diff(struct razor_root *root,
+               razor_package_callback_t callback, void *data)
+ {
+       return razor_set_diff(root->system, root->next, callback, data);
+ }
diff --cc librazor/rpm.c
--- 1/rpm.c
@@@ -592,8 -781,8 +781,8 @@@ razor_rpm_close(struct razor_rpm *rpm
  int
  razor_importer_add_rpm(struct razor_importer *importer, struct razor_rpm *rpm)
  {
 -      const char *name, *version, *release, *arch;
 +      const char *name, *version, *release, *arch, *summary;
-       const uint_32 *epoch;
+       const uint32_t *epoch;
        char evr[128], buf[16];
  
        name = razor_rpm_get_indirect(rpm, RPMTAG_NAME, NULL);
index 0000000,22d0855..a373786
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,161 +1,170 @@@
+ /*
+  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
+  * Copyright (C) 2008  Red Hat, Inc
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 2 of the License, or
+  * (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License along
+  * with this program; if not, write to the Free Software Foundation, Inc.,
+  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+  */
+ #include <stdio.h>
+ #include <stddef.h>
+ #include <string.h>
+ #include <fcntl.h>
+ #include <rpm/rpmlib.h>
+ #include <rpm/rpmdb.h>
+ #include "razor.h"
+ union rpm_entry {
+       void *p;
+       char *string;
+       char **list;
+       uint_32 *flags;
+       uint_32 integer;
+ };
+ static uint32_t
+ rpm_to_razor_flags(uint32_t flags)
+ {
+       uint32_t razor_flags;
+       razor_flags = 0;
+       if (flags & RPMSENSE_LESS)
+               razor_flags |= RAZOR_PROPERTY_LESS;
+       if (flags & RPMSENSE_EQUAL)
+               razor_flags |= RAZOR_PROPERTY_EQUAL;
+       if (flags & RPMSENSE_GREATER)
+               razor_flags |= RAZOR_PROPERTY_GREATER;
+       if (flags & RPMSENSE_SCRIPT_PRE)
+               razor_flags |= RAZOR_PROPERTY_PRE;
+       if (flags & RPMSENSE_SCRIPT_POST)
+               razor_flags |= RAZOR_PROPERTY_POST;
+       if (flags & RPMSENSE_SCRIPT_PREUN)
+               razor_flags |= RAZOR_PROPERTY_PREUN;
+       if (flags & RPMSENSE_SCRIPT_POSTUN)
+               razor_flags |= RAZOR_PROPERTY_POSTUN;
+       return razor_flags;
+ }
+ static void
+ add_properties(struct razor_importer *importer,
+              uint32_t type_flags,
+              Header h, int_32 name_tag, int_32 version_tag, int_32 flags_tag)
+ {
+       union rpm_entry names, versions, flags;
+       int_32 i, type, count;
+       headerGetEntry(h, name_tag, &type, &names.p, &count);
+       headerGetEntry(h, version_tag, &type, &versions.p, &count);
+       headerGetEntry(h, flags_tag, &type, &flags.p, &count);
+       for (i = 0; i < count; i++)
+               razor_importer_add_property(importer,
+                                           names.list[i],
+                                           rpm_to_razor_flags (flags.flags[i]) | type_flags,
+                                           versions.list[i]);
+ }
+ struct razor_set *
+ razor_set_create_from_rpmdb(void)
+ {
+       struct razor_importer *importer;
+       rpmdbMatchIterator iter;
+       Header h;
+       int_32 type, count, i;
+       union rpm_entry name, epoch, version, release, arch;
++      union rpm_entry summary, description, url, license;
+       union rpm_entry basenames, dirnames, dirindexes;
+       char filename[PATH_MAX], evr[128], buf[16];
+       rpmdb db;
+       rpmReadConfigFiles(NULL, NULL);
+       if (rpmdbOpen("", &db, O_RDONLY, 0644) != 0) {
+               fprintf(stderr, "cannot open rpm database\n");
+               exit(1);
+       }
+       importer = razor_importer_new();
+       iter = rpmdbInitIterator(db, 0, NULL, 0);
+       while (h = rpmdbNextIterator(iter), h != NULL) {
+               headerGetEntry(h, RPMTAG_NAME, &type, &name.p, &count);
+               headerGetEntry(h, RPMTAG_EPOCH, &type, &epoch.p, &count);
+               headerGetEntry(h, RPMTAG_VERSION, &type, &version.p, &count);
+               headerGetEntry(h, RPMTAG_RELEASE, &type, &release.p, &count);
+               headerGetEntry(h, RPMTAG_ARCH, &type, &arch.p, &count);
++              headerGetEntry(h, RPMTAG_SUMMARY, &type, &summary.p, &count);
++              headerGetEntry(h, RPMTAG_DESCRIPTION, &type, &description.p,
++                             &count);
++              headerGetEntry(h, RPMTAG_URL, &type, &url.p, &count);
++              headerGetEntry(h, RPMTAG_LICENSE, &type, &license.p, &count);
+               if (epoch.flags != NULL) {
+                       snprintf(buf, sizeof buf, "%u", *epoch.flags);
+                       razor_build_evr(evr, sizeof evr,
+                                       buf, version.string, release.string);
+               } else {
+                       razor_build_evr(evr, sizeof evr,
+                                       NULL, version.string, release.string);
+               }
+               razor_importer_begin_package(importer,
+                                            name.string, evr, arch.string);
++              razor_importer_add_details(importer, summary.string,
++                                         description.string, url.string,
++                                         license.string);
+               add_properties(importer, RAZOR_PROPERTY_REQUIRES, h,
+                              RPMTAG_REQUIRENAME,
+                              RPMTAG_REQUIREVERSION,
+                              RPMTAG_REQUIREFLAGS);
+               add_properties(importer, RAZOR_PROPERTY_PROVIDES, h,
+                              RPMTAG_PROVIDENAME,
+                              RPMTAG_PROVIDEVERSION,
+                              RPMTAG_PROVIDEFLAGS);
+               add_properties(importer, RAZOR_PROPERTY_OBSOLETES, h,
+                              RPMTAG_OBSOLETENAME,
+                              RPMTAG_OBSOLETEVERSION,
+                              RPMTAG_OBSOLETEFLAGS);
+               add_properties(importer, RAZOR_PROPERTY_CONFLICTS, h,
+                              RPMTAG_CONFLICTNAME,
+                              RPMTAG_CONFLICTVERSION,
+                              RPMTAG_CONFLICTFLAGS);
+               headerGetEntry(h, RPMTAG_BASENAMES, &type,
+                              &basenames.p, &count);
+               headerGetEntry(h, RPMTAG_DIRNAMES, &type,
+                              &dirnames.p, &count);
+               headerGetEntry(h, RPMTAG_DIRINDEXES, &type,
+                              &dirindexes.p, &count);
+               for (i = 0; i < count; i++) {
+                       snprintf(filename, sizeof filename, "%s%s",
+                                dirnames.list[dirindexes.flags[i]],
+                                basenames.list[i]);
+                       razor_importer_add_file(importer, filename);
+               }
+               razor_importer_finish_package(importer);
+       }
+       rpmdbClose(db);
+       return razor_importer_finish(importer);
+ }
--- 1/yum.c
@@@ -56,9 -53,9 +57,10 @@@ struct yum_context 
  
        struct razor_importer *importer;
        struct import_property_context *current_property_context;
 -      char name[256], arch[64], buffer[512], *p;
 +      char name[256], arch[64], summary[512], description[4096];
 +      char url[256], license[64], buffer[512], *p;
        char pkgid[128];
+       uint32_t property_type;
        int state;
  };
  
@@@ -130,17 -115,18 +129,21 @@@ yum_primary_start_element(void *data, c
        } else if (strcmp(name, "checksum") == 0) {
                ctx->p = ctx->pkgid;
                ctx->state = YUM_STATE_CHECKSUM;
 +      } else if (strcmp(name, "rpm:license") == 0) {
 +              ctx->p = ctx->license;
 +              ctx->state = YUM_STATE_LICENSE;
        } else if (strcmp(name, "rpm:requires") == 0) {
                ctx->state = YUM_STATE_REQUIRES;
+               ctx->property_type = RAZOR_PROPERTY_REQUIRES;
        } else if (strcmp(name, "rpm:provides") == 0) {
                ctx->state = YUM_STATE_PROVIDES;
+               ctx->property_type = RAZOR_PROPERTY_PROVIDES;
        } else if (strcmp(name, "rpm:obsoletes") == 0) {
                ctx->state = YUM_STATE_OBSOLETES;
+               ctx->property_type = RAZOR_PROPERTY_OBSOLETES;
        } else if (strcmp(name, "rpm:conflicts") == 0) {
                ctx->state = YUM_STATE_CONFLICTS;
+               ctx->property_type = RAZOR_PROPERTY_CONFLICTS;
        } else if (strcmp(name, "rpm:entry") == 0 &&
                   ctx->state != YUM_STATE_BEGIN) {
                n = NULL;
diff --cc src/main.c
--- 1/main.c
@@@ -484,8 -477,9 +484,9 @@@ command_remove(int argc, const char *ar
                return 1;
  
        set = razor_transaction_finish(trans);
 -      razor_set_write(set, updated_repo_filename);
 +      razor_set_write(set, updated_repo_filename, RAZOR_REPO_FILE_MAIN);
        razor_set_destroy(set);
+       razor_set_destroy(upstream);
        printf("wrote system-updated.repo\n");
  
        return 0;
@@@ -260,16 -262,12 +262,16 @@@ end_transaction(struct test_context *ct
                pkg = razor_set_get_package(ctx->repo_set,
                                            ctx->install_pkgs[i]);
                razor_transaction_install_package(ctx->trans, pkg);
-       }               
+       }
        for (i = 0; i < ctx->n_remove_pkgs; i++) {
 -              pkg = razor_set_get_package(ctx->repo_set,
 +              pkg = razor_set_get_package(ctx->system_set,
                                            ctx->remove_pkgs[i]);
 +              if (!pkg)
 +                      pkg = razor_set_get_package(ctx->repo_set,
 +                                                  ctx->remove_pkgs[i]);
 +
                razor_transaction_remove_package(ctx->trans, pkg);
-       }               
+       }
  
        errors = razor_transaction_resolve(ctx->trans);
        printf("\n");