diff options
author | James Bowes <jbowes@redhat.com> | 2008-06-20 19:04:06 -0400 |
---|---|---|
committer | James Bowes <jbowes@redhat.com> | 2008-06-20 19:04:47 -0400 |
commit | c778afefaa189c6aa8437fc00512f140a6a68efe (patch) | |
tree | 2b62b297754d70db03ff990c5d196f696aa34976 /librazor | |
parent | 08973b4c9c22a4dcbed220daa38fcf510ba9146d (diff) | |
parent | e557f97da15b1682b2e9854672b019485bc48362 (diff) |
Merge branch 'krh/master'
Conflicts:
librazor/razor.h
librazor/rpm.c
razor.c
src/main.c
Diffstat (limited to 'librazor')
-rw-r--r-- | librazor/.gitignore | 5 | ||||
-rw-r--r-- | librazor/Makefile.am | 37 | ||||
-rw-r--r-- | librazor/importer.c | 505 | ||||
-rw-r--r-- | librazor/iterator.c | 265 | ||||
-rw-r--r-- | librazor/merger.c | 525 | ||||
-rw-r--r-- | librazor/razor-internal.h | 194 | ||||
-rw-r--r-- | librazor/razor.c | 588 | ||||
-rw-r--r-- | librazor/razor.h | 218 | ||||
-rw-r--r-- | librazor/root.c | 168 | ||||
-rw-r--r-- | librazor/rpm.c | 828 | ||||
-rw-r--r-- | librazor/transaction.c | 912 | ||||
-rw-r--r-- | librazor/types.c | 246 | ||||
-rw-r--r-- | librazor/util.c | 167 |
13 files changed, 4658 insertions, 0 deletions
diff --git a/librazor/.gitignore b/librazor/.gitignore new file mode 100644 index 0000000..7fe452b --- /dev/null +++ b/librazor/.gitignore @@ -0,0 +1,5 @@ +.deps +.libs +*.lo +*.la + diff --git a/librazor/Makefile.am b/librazor/Makefile.am new file mode 100644 index 0000000..a858968 --- /dev/null +++ b/librazor/Makefile.am @@ -0,0 +1,37 @@ +## Process this file with automake to produce Makefile.in + +INCLUDES = \ + -I$(top_builddir)/src -I$(top_srcdir)/src \ + -DPACKAGE_LIBEXEC_DIR=\""$(libexecdir)"\" \ + -DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\" \ + -DPACKAGE_DATA_DIR=\""$(datadir)"\" \ + -DPACKAGE_BIN_DIR=\""$(bindir)"\" \ + -DPACKAGE_LOCALSTATE_DIR=\""$(localstatedir)"\" \ + -DPACKAGE_LOCALE_DIR=\""$(localedir)"\" \ + -DPACKAGE_LIB_DIR=\""$(libdir)"\" + +lib_LTLIBRARIES = librazor.la + +librazorincludedir = $(includedir)/razor + +librazorinclude_HEADERS = \ + razor.h + +librazor_la_SOURCES = \ + razor-internal.h \ + razor.h \ + razor.c \ + root.c \ + types.c \ + util.c \ + rpm.c \ + iterator.c \ + importer.c \ + merger.c \ + transaction.c + +librazor_la_LIBADD = $(ZLIB_LIBS) + +clean-local : + rm -f *~ + diff --git a/librazor/importer.c b/librazor/importer.c new file mode 100644 index 0000000..eca9ede --- /dev/null +++ b/librazor/importer.c @@ -0,0 +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; +} diff --git a/librazor/iterator.c b/librazor/iterator.c new file mode 100644 index 0000000..909b954 --- /dev/null +++ b/librazor/iterator.c @@ -0,0 +1,265 @@ +/* + * 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" + +static struct razor_package_iterator * +razor_package_iterator_create_with_index(struct razor_set *set, + struct list *index) +{ + struct razor_package_iterator *pi; + + pi = zalloc(sizeof *pi); + pi->set = set; + pi->index = index; + + return pi; +} + +struct razor_package_iterator * +razor_package_iterator_create(struct razor_set *set) +{ + struct razor_package_iterator *pi; + + pi = zalloc(sizeof *pi); + pi->set = set; + pi->end = set->packages.data + set->packages.size; + pi->package = set->packages.data; + + return pi; +} + +void +razor_package_iterator_init_for_property(struct razor_package_iterator *pi, + struct razor_set *set, + struct razor_property *property) +{ + memset(pi, 0, sizeof *pi); + pi->set = set; + pi->index = list_first(&property->packages, &set->package_pool); +} + +struct razor_package_iterator * +razor_package_iterator_create_for_property(struct razor_set *set, + struct razor_property *property) +{ + struct list *index; + + index = list_first(&property->packages, &set->package_pool); + return razor_package_iterator_create_with_index(set, index); +} + +struct razor_package_iterator * +razor_package_iterator_create_for_file(struct razor_set *set, + const char *filename) +{ + struct razor_entry *entry; + struct list *index; + + entry = razor_set_find_entry(set, set->files.data, filename); + if (entry == NULL) + return NULL; + + index = list_first(&entry->packages, &set->package_pool); + return razor_package_iterator_create_with_index(set, index); +} + +int +razor_package_iterator_next(struct razor_package_iterator *pi, + struct razor_package **package, + const char **name, + const char **version, + const char **arch) +{ + char *pool; + int valid; + struct razor_package *p, *packages; + + if (pi->package) { + p = pi->package++; + valid = p < pi->end; + } else if (pi->index) { + packages = pi->set->packages.data; + p = &packages[pi->index->data]; + pi->index = list_next(pi->index); + valid = 1; + } else + valid = 0; + + if (valid) { + pool = pi->set->string_pool.data; + *package = p; + *name = &pool[p->name]; + *version = &pool[p->version]; + *arch = &pool[p->arch]; + } else { + *package = NULL; + } + + return valid; +} + +void +razor_package_iterator_destroy(struct razor_package_iterator *pi) +{ + if (pi->free_index) + free(pi->index); + + free(pi); +} + +struct razor_property_iterator * +razor_property_iterator_create(struct razor_set *set, + struct razor_package *package) +{ + struct razor_property_iterator *pi; + + pi = zalloc(sizeof *pi); + pi->set = set; + + if (package) { + pi->index = list_first(&package->properties, + &set->property_pool); + } else { + pi->property = set->properties.data; + pi->end = set->properties.data + set->properties.size; + } + + return pi; +} + +int +razor_property_iterator_next(struct razor_property_iterator *pi, + struct razor_property **property, + const char **name, + uint32_t *flags, + const char **version) +{ + char *pool; + int valid; + struct razor_property *p, *properties; + + if (pi->property) { + p = pi->property++; + valid = p < pi->end; + } else if (pi->index) { + properties = pi->set->properties.data; + p = &properties[pi->index->data]; + pi->index = list_next(pi->index); + valid = 1; + } else + valid = 0; + + if (valid) { + pool = pi->set->string_pool.data; + *property = p; + *name = &pool[p->name]; + *flags = p->flags; + *version = &pool[p->version]; + } else { + *property = NULL; + } + + return valid; +} + +void +razor_property_iterator_destroy(struct razor_property_iterator *pi) +{ + free(pi); +} + +struct razor_package_query { + struct razor_set *set; + char *vector; + int count; +}; + +struct razor_package_query * +razor_package_query_create(struct razor_set *set) +{ + struct razor_package_query *pq; + int count; + + pq = zalloc(sizeof *pq); + pq->set = set; + count = set->packages.size / sizeof(struct razor_package); + pq->vector = zalloc(count * sizeof(char)); + + return pq; +} + +void +razor_package_query_add_package(struct razor_package_query *pq, + struct razor_package *p) +{ + struct razor_package *packages; + + packages = pq->set->packages.data; + pq->count += pq->vector[p - packages] ^ 1; + pq->vector[p - packages] = 1; +} + +void +razor_package_query_add_iterator(struct razor_package_query *pq, + struct razor_package_iterator *pi) +{ + struct razor_package *packages, *p; + const char *name, *version, *arch; + + packages = pq->set->packages.data; + while (razor_package_iterator_next(pi, &p, &name, &version, &arch)) { + pq->count += pq->vector[p - packages] ^ 1; + pq->vector[p - packages] = 1; + } +} + +struct razor_package_iterator * +razor_package_query_finish(struct razor_package_query *pq) +{ + struct razor_package_iterator *pi; + struct razor_set *set; + struct list *index; + int i, j, count; + + set = pq->set; + count = set->packages.size / sizeof(struct razor_package); + index = zalloc(pq->count * sizeof *index); + + for (i = 0, j = 0; i < count; i++) { + if (!pq->vector[i]) + continue; + + index[j].data = i; + if (j == pq->count - 1) + index[j].flags = 0x80; + j++; + } + + free(pq); + + pi = razor_package_iterator_create_with_index(set, index); + pi->free_index = 1; + + return pi; +} diff --git a/librazor/merger.c b/librazor/merger.c new file mode 100644 index 0000000..636903a --- /dev/null +++ b/librazor/merger.c @@ -0,0 +1,525 @@ +/* + * 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 <string.h> +#include "razor-internal.h" +#include "razor.h" + +#define UPSTREAM_SOURCE 0x80 + +struct source { + struct razor_set *set; + uint32_t *property_map; + uint32_t *file_map; +}; + +struct razor_merger { + struct razor_set *set; + struct hashtable table; + struct source source1; + struct source source2; +}; + +struct razor_merger * +razor_merger_create(struct razor_set *set1, struct razor_set *set2) +{ + struct razor_merger *merger; + int count; + size_t size; + + merger = zalloc(sizeof *merger); + merger->set = razor_set_create(); + hashtable_init(&merger->table, &merger->set->string_pool); + + merger->source1.set = set1; + count = set1->properties.size / sizeof (struct razor_property); + size = count * sizeof merger->source1.property_map[0]; + merger->source1.property_map = zalloc(size); + count = set1->files.size / sizeof (struct razor_entry); + size = count * sizeof merger->source1.file_map[0]; + merger->source1.file_map = zalloc(size); + + merger->source2.set = set2; + count = set2->properties.size / sizeof (struct razor_property); + size = count * sizeof merger->source2.property_map[0]; + merger->source2.property_map = zalloc(size); + count = set2->files.size / sizeof (struct razor_entry); + size = count * sizeof merger->source2.file_map[0]; + merger->source2.file_map = zalloc(size); + + return merger; +} + +void +razor_merger_add_package(struct razor_merger *merger, + struct razor_package *package) +{ + char *pool; + struct list *r; + struct razor_package *p; + struct razor_set *set1; + struct source *source; + uint32_t flags; + + set1 = merger->source1.set; + if (set1->packages.data <= (void *) package && + (void *) package < set1->packages.data + set1->packages.size) { + source = &merger->source1; + flags = 0; + } else { + source = &merger->source2; + flags = UPSTREAM_SOURCE; + } + + pool = source->set->string_pool.data; + p = array_add(&merger->set->packages, sizeof *p); + p->name = hashtable_tokenize(&merger->table, &pool[package->name]); + p->flags = flags; + p->version = hashtable_tokenize(&merger->table, + &pool[package->version]); + p->arch = hashtable_tokenize(&merger->table, + &pool[package->arch]); + + p->properties = package->properties; + r = list_first(&package->properties, &source->set->property_pool); + while (r) { + source->property_map[r->data] = 1; + r = list_next(r); + } + + p->files = package->files; + r = list_first(&package->files, &source->set->file_pool); + while (r) { + source->file_map[r->data] = 1; + r = list_next(r); + } +} + +static uint32_t +add_property(struct razor_merger *merger, + const char *name, uint32_t flags, const char *version) +{ + struct razor_property *p; + + p = array_add(&merger->set->properties, sizeof *p); + p->name = hashtable_tokenize(&merger->table, name); + p->flags = flags; + p->version = hashtable_tokenize(&merger->table, version); + + return p - (struct razor_property *) merger->set->properties.data; +} + +static void +merge_properties(struct razor_merger *merger) +{ + struct razor_property *p1, *p2; + struct razor_set *set1, *set2; + uint32_t *map1, *map2; + int i, j, cmp, count1, count2; + char *pool1, *pool2; + + set1 = merger->source1.set; + set2 = merger->source2.set; + map1 = merger->source1.property_map; + map2 = merger->source2.property_map; + + i = 0; + j = 0; + pool1 = set1->string_pool.data; + pool2 = set2->string_pool.data; + + count1 = set1->properties.size / sizeof *p1; + count2 = set2->properties.size / sizeof *p2; + while (i < count1 || j < count2) { + if (i < count1 && map1[i] == 0) { + i++; + continue; + } + if (j < count2 && map2[j] == 0) { + j++; + continue; + } + p1 = (struct razor_property *) set1->properties.data + i; + p2 = (struct razor_property *) set2->properties.data + j; + if (i < count1 && j < count2) + cmp = strcmp(&pool1[p1->name], &pool2[p2->name]); + else if (i < count1) + cmp = -1; + else + cmp = 1; + if (cmp == 0) + cmp = p1->flags - p2->flags; + if (cmp == 0) + cmp = razor_versioncmp(&pool1[p1->version], + &pool2[p2->version]); + if (cmp < 0) { + map1[i++] = add_property(merger, + &pool1[p1->name], + p1->flags, + &pool1[p1->version]); + } else if (cmp > 0) { + map2[j++] = add_property(merger, + &pool2[p2->name], + p2->flags, + &pool2[p2->version]); + } else { + map1[i++] = map2[j++] = + add_property(merger, + &pool1[p1->name], + p1->flags, + &pool1[p1->version]); + } + } +} + +static void +emit_properties(struct list_head *properties, struct array *source_pool, + uint32_t *map, struct array *pool) +{ + uint32_t r; + struct list *p, *q; + + r = pool->size / sizeof *q; + p = list_first(properties, source_pool); + while (p) { + q = array_add(pool, sizeof *q); + q->data = map[p->data]; + q->flags = p->flags; + p = list_next(p); + } + + list_set_ptr(properties, r); +} + +static uint32_t +add_file(struct razor_merger *merger, const char *name) +{ + struct razor_entry *e; + + e = array_add(&merger->set->files, sizeof *e); + e->name = hashtable_tokenize(&merger->table, name); + e->flags = 0; + e->start = 0; + + return e - (struct razor_entry *)merger->set->files.data; +} + +/* FIXME. Blah */ +static int +fix_file_map(uint32_t *map, + struct razor_entry *files, + struct razor_entry *top) +{ + uint32_t e; + int found_file = 0; + + e = top->start; + do { + if (files[e].start) + fix_file_map(map, files, &files[e]); + if (map[e]) + found_file = 1; + } while (!(files[e++].flags & RAZOR_ENTRY_LAST)); + + if (found_file) + map[top - files] = 1; + return found_file; +} + +struct merge_directory { + uint32_t merged, dir1, dir2; +}; + +static void +merge_one_directory(struct razor_merger *merger, struct merge_directory *md) +{ + struct razor_entry *root1, *root2, *mroot, *e1, *e2; + struct razor_set *set1, *set2; + struct array merge_stack; + struct merge_directory *child_md, *end_md; + uint32_t *map1, *map2, start, last; + int cmp; + char *pool1, *pool2; + + set1 = merger->source1.set; + set2 = merger->source2.set; + map1 = merger->source1.file_map; + map2 = merger->source2.file_map; + pool1 = set1->string_pool.data; + pool2 = set2->string_pool.data; + root1 = (struct razor_entry *) set1->files.data; + root2 = (struct razor_entry *) set2->files.data; + + array_init(&merge_stack); + + start = merger->set->files.size / sizeof (struct razor_entry); + last = 0; + e1 = md->dir1 ? root1 + md->dir1 : NULL; + e2 = md->dir2 ? root2 + md->dir2 : NULL; + while (e1 || e2) { + if (!e2 && !map1[e1 - root1]) { + if ((e1++)->flags & RAZOR_ENTRY_LAST) + e1 = NULL; + continue; + } + if (!e1 && !map2[e2 - root2]) { + if ((e2++)->flags & RAZOR_ENTRY_LAST) + e2 = NULL; + continue; + } + if (e1 && !map1[e1 - root1] && + e2 && !map1[e2 - root2]) { + if ((e1++)->flags & RAZOR_ENTRY_LAST) + e1 = NULL; + if ((e2++)->flags & RAZOR_ENTRY_LAST) + e2 = NULL; + continue; + } + + if (!e1) + cmp = 1; + else if (!e2) + cmp = -1; + else { + cmp = strcmp (&pool1[e1->name], + &pool2[e2->name]); + } + + if (cmp < 0) { + if (map1[e1 - root1]) { + map1[e1 - root1] = last = + add_file(merger, &pool1[e1->name]); + if (e1->start) { + child_md = array_add(&merge_stack, sizeof (struct merge_directory)); + child_md->merged = last; + child_md->dir1 = e1->start; + child_md->dir2 = 0; + } + } + if ((e1++)->flags & RAZOR_ENTRY_LAST) + e1 = NULL; + } else if (cmp > 0) { + if (map2[e2 - root2]) { + map2[e2 - root2] = last = + add_file(merger, &pool2[e2->name]); + if (e2->start) { + child_md = array_add(&merge_stack, sizeof (struct merge_directory)); + child_md->merged = last; + child_md->dir1 = 0; + child_md->dir2 = e2->start; + } + } + if ((e2++)->flags & RAZOR_ENTRY_LAST) + e2 = NULL; + } else { + map1[e1 - root1] = map2[e2- root2] = last = + add_file(merger, &pool1[e1->name]); + if (e1->start || e2->start) { + child_md = array_add(&merge_stack, sizeof (struct merge_directory)); + child_md->merged = last; + child_md->dir1 = e1->start; + child_md->dir2 = e2->start; + } + if ((e1++)->flags & RAZOR_ENTRY_LAST) + e1 = NULL; + if ((e2++)->flags & RAZOR_ENTRY_LAST) + e2 = NULL; + } + } + + mroot = (struct razor_entry *)merger->set->files.data; + if (last) { + mroot[last].flags = RAZOR_ENTRY_LAST; + mroot[md->merged].start = start; + } else + mroot[md->merged].start = 0; + + end_md = merge_stack.data + merge_stack.size; + for (child_md = merge_stack.data; child_md < end_md; child_md++) + merge_one_directory(merger, child_md); + array_release(&merge_stack); +} + +static void +merge_files(struct razor_merger *merger) +{ + struct razor_entry *root; + struct merge_directory md; + uint32_t *map1, *map2; + + map1 = merger->source1.file_map; + map2 = merger->source2.file_map; + + md.merged = 0; + + if (merger->source1.set->files.size) { + root = (struct razor_entry *) merger->source1.set->files.data; + if (root->start) + fix_file_map(map1, root, root); + md.dir1 = root->start; + } else + md.dir1 = 0; + + if (merger->source2.set->files.size) { + root = (struct razor_entry *) merger->source2.set->files.data; + if (root->start) + fix_file_map(map2, root, root); + md.dir2 = root->start; + } else + md.dir2 = 0; + + merge_one_directory(merger, &md); +} + +static void +emit_files(struct list_head *files, struct array *source_pool, + uint32_t *map, struct array *pool) +{ + uint32_t r; + struct list *p, *q; + + r = pool->size / sizeof *q; + p = list_first(files, source_pool); + while (p) { + q = array_add(pool, sizeof *q); + q->data = map[p->data]; + q->flags = p->flags; + p = list_next(p); + } + + list_set_ptr(files, r); +} + +/* Rebuild property->packages maps. We can't just remap these, as a + * property may have lost or gained a number of packages. Allocate an + * array per property and loop through the packages and add them to + * the arrays for their properties. */ +static void +rebuild_property_package_lists(struct razor_set *set) +{ + struct array *pkgs, *a; + struct razor_package *pkg, *pkg_end; + struct razor_property *prop, *prop_end; + struct list *r; + uint32_t *q; + int count; + + count = set->properties.size / sizeof (struct razor_property); + pkgs = zalloc(count * sizeof *pkgs); + pkg_end = set->packages.data + set->packages.size; + + for (pkg = set->packages.data; pkg < pkg_end; pkg++) { + r = list_first(&pkg->properties, &set->property_pool); + while (r) { + q = array_add(&pkgs[r->data], sizeof *q); + *q = pkg - (struct razor_package *) set->packages.data; + r = list_next(r); + } + } + + prop_end = set->properties.data + set->properties.size; + a = pkgs; + for (prop = set->properties.data; prop < prop_end; prop++, a++) { + list_set_array(&prop->packages, &set->package_pool, a, 0); + array_release(a); + } + free(pkgs); +} + +static void +rebuild_file_package_lists(struct razor_set *set) +{ + struct array *pkgs, *a; + struct razor_package *pkg, *pkg_end; + struct razor_entry *entry, *entry_end; + struct list *r; + uint32_t *q; + int count; + + count = set->files.size / sizeof (struct razor_entry); + pkgs = zalloc(count * sizeof *pkgs); + pkg_end = set->packages.data + set->packages.size; + + for (pkg = set->packages.data; pkg < pkg_end; pkg++) { + r = list_first(&pkg->files, &set->file_pool); + while (r) { + q = array_add(&pkgs[r->data], sizeof *q); + *q = pkg - (struct razor_package *) set->packages.data; + r = list_next(r); + } + } + + entry_end = set->files.data + set->files.size; + a = pkgs; + for (entry = set->files.data; entry < entry_end; entry++, a++) { + list_set_array(&entry->packages, &set->package_pool, a, 0); + array_release(a); + } + free(pkgs); +} + +struct razor_set * +razor_merger_finish(struct razor_merger *merger) +{ + struct razor_set *result; + struct razor_package *p, *pend; + + /* As we built the package list, we filled out a bitvector of + * the properties that are referenced by the packages in the + * new set. Now we do a parallel loop through the properties + * and emit those marked in the bit vector to the new set. In + * the process, we update the bit vector to actually map from + * indices in the old property list to indices in the new + * property list for both sets. */ + + merge_properties(merger); + merge_files(merger); + + /* Now we loop through the packages again and emit the + * property lists, remapped to point to the new properties. */ + + pend = merger->set->packages.data + merger->set->packages.size; + for (p = merger->set->packages.data; p < pend; p++) { + struct source *src; + + if (p->flags & UPSTREAM_SOURCE) + src = &merger->source2; + else + src = &merger->source1; + + emit_properties(&p->properties, + &src->set->property_pool, + src->property_map, + &merger->set->property_pool); + emit_files(&p->files, + &src->set->file_pool, + src->file_map, + &merger->set->file_pool); + p->flags &= ~UPSTREAM_SOURCE; + } + + rebuild_property_package_lists(merger->set); + rebuild_file_package_lists(merger->set); + + result = merger->set; + hashtable_release(&merger->table); + free(merger); + + return result; +} diff --git a/librazor/razor-internal.h b/librazor/razor-internal.h new file mode 100644 index 0000000..94eac41 --- /dev/null +++ b/librazor/razor-internal.h @@ -0,0 +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_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_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 { + 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_ */ diff --git a/librazor/razor.c b/librazor/razor.c new file mode 100644 index 0000000..cddcc1b --- /dev/null +++ b/librazor/razor.c @@ -0,0 +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_PACKAGE_POOL, offsetof(struct razor_set, package_pool) }, + { RAZOR_PROPERTY_POOL, offsetof(struct razor_set, property_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); +} + +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 = magic; + header->version = RAZOR_VERSION; + offset = sizeof data; + + for (i = 0; i < array_size; i++) { + if (sections[i].type != i) + continue; + 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; i++) { + if (sections[i].type != i) + continue; + 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_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, 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); +} diff --git a/librazor/razor.h b/librazor/razor.h new file mode 100644 index 0000000..8429912 --- /dev/null +++ b/librazor/razor.h @@ -0,0 +1,218 @@ +/* + * 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. + */ + +#ifndef _RAZOR_H_ +#define _RAZOR_H_ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +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_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 +}; + +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); +void razor_set_destroy(struct razor_set *set); +int razor_set_write_to_fd(struct razor_set *set, int fd, + enum razor_repo_file_type type); +int razor_set_write(struct razor_set *set, const char *filename, + enum razor_repo_file_type type); + +void razor_set_open_details(struct razor_set *set, const char *filename); +void razor_set_open_files(struct razor_set *set, const char *filename); + +struct razor_package * +razor_set_get_package(struct razor_set *set, const char *package); + +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); + +struct razor_package_iterator; +struct razor_package_iterator * +razor_package_iterator_create(struct razor_set *set); +struct razor_package_iterator * +razor_package_iterator_create_for_property(struct razor_set *set, + struct razor_property *property); +struct razor_package_iterator * +razor_package_iterator_create_for_file(struct razor_set *set, + const char *filename); + +int razor_package_iterator_next(struct razor_package_iterator *pi, + struct razor_package **package, + const char **name, + const char **version, + const char **arch); +void razor_package_iterator_destroy(struct razor_package_iterator *pi); + +struct razor_package_query * +razor_package_query_create(struct razor_set *set); +void +razor_package_query_add_package(struct razor_package_query *pq, + struct razor_package *p); +void +razor_package_query_add_iterator(struct razor_package_query *pq, + struct razor_package_iterator *pi); +struct razor_package_iterator * +razor_package_query_finish(struct razor_package_query *pq); + +struct razor_property_iterator; +struct razor_property_iterator * +razor_property_iterator_create(struct razor_set *set, + struct razor_package *package); +int razor_property_iterator_next(struct razor_property_iterator *pi, + struct razor_property **property, + const char **name, + uint32_t *flags, + const char **version); +void +razor_property_iterator_destroy(struct razor_property_iterator *pi); + +void razor_set_list_files(struct razor_set *set, const char *prefix); +void razor_set_list_package_files(struct razor_set *set, const char *name); + +void razor_set_list_unsatisfied(struct razor_set *set); + +typedef void (*razor_package_callback_t)(const char *name, + const char *old_version, + const char *new_version, + const char *arch, + void *data); +void +razor_set_diff(struct razor_set *set, struct razor_set *upstream, + razor_package_callback_t callback, void *data); + +/* Package transactions */ + +struct razor_transaction * +razor_transaction_create(struct razor_set *system, struct razor_set *upstream); +void razor_transaction_install_package(struct razor_transaction *transaction, + struct razor_package *package); +void razor_transaction_remove_package(struct razor_transaction *transaction, + struct razor_package *package); +void razor_transaction_update_package(struct razor_transaction *trans, + struct razor_package *package); +void razor_transaction_update_all(struct razor_transaction *transaction); +int razor_transaction_resolve(struct razor_transaction *trans); +int razor_transaction_describe(struct razor_transaction *trans); +struct razor_set *razor_transaction_finish(struct razor_transaction *trans); +void razor_transaction_destroy(struct razor_transaction *trans); + +/* Temporary helper for test suite. */ +int razor_transaction_unsatisfied_property(struct razor_transaction *trans, + const char *name, + uint32_t flags, + const char *version); + +/* Importer interface; for building a razor set from external sources, + * like yum, rpmdb or razor package files. */ + +struct razor_importer; +struct razor_rpm; + +struct razor_importer *razor_importer_new(void); +void razor_importer_destroy(struct razor_importer *importer); +void razor_importer_begin_package(struct razor_importer *importer, + 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, + 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); + +int razor_importer_add_rpm(struct razor_importer *importer, + struct razor_rpm *rpm); + +struct razor_set *razor_importer_finish(struct razor_importer *importer); + +void razor_build_evr(char *evr_buf, int size, const char *epoch, + const char *version, const char *release); +int razor_versioncmp(const char *s1, const char *s2); + +struct razor_set *razor_set_create_from_yum(void); +struct razor_set *razor_set_create_from_rpmdb(void); + +/* RPM functions */ + +struct razor_rpm *razor_rpm_open(const char *filename); +int razor_rpm_install(struct razor_rpm *rpm, const char *root); +int razor_rpm_close(struct razor_rpm *rpm); + + +/* Razor root functions. The root data struct encapsulates filesystem + * conventions and the locking protocol. */ + +struct razor_root; +#define RAZOR_ROOT_OPEN_WRITE 0x01 + +int razor_root_create(const char *root); +struct razor_root *razor_root_open(const char *root, int flags); +struct razor_set *razor_root_open_read_only(const char *root); +struct razor_transaction * +razor_root_create_transaction(struct razor_root *image, + struct razor_set *upstream); +int razor_root_close(struct razor_root *image); +void razor_root_update(struct razor_root *image, struct razor_set *next); +int razor_root_commit(struct razor_root *image); +void razor_root_diff(struct razor_root *root, + razor_package_callback_t callback, void *data); + +#endif /* _RAZOR_H_ */ diff --git a/librazor/root.c b/librazor/root.c new file mode 100644 index 0000000..dc3b230 --- /dev/null +++ b/librazor/root.c @@ -0,0 +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, 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_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 --git a/librazor/rpm.c b/librazor/rpm.c new file mode 100644 index 0000000..3f572c4 --- /dev/null +++ b/librazor/rpm.c @@ -0,0 +1,828 @@ +/* + * 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 <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <zlib.h> + +#include "razor.h" +#include "razor-internal.h" + +#define RPM_LEAD_SIZE 96 + +enum { + PIPE = 1, /*!< pipe/fifo */ + CDEV = 2, /*!< character device */ + XDIR = 4, /*!< directory */ + BDEV = 6, /*!< block device */ + REG = 8, /*!< regular file */ + LINK = 10, /*!< hard link */ + SOCK = 12 /*!< socket */ +}; + +enum { + RPMSENSE_LESS = 1 << 1, + RPMSENSE_GREATER = 1 << 2, + RPMSENSE_EQUAL = 1 << 3, + RPMSENSE_PREREQ = 1 << 6, + RPMSENSE_SCRIPT_PRE = 1 << 9, + RPMSENSE_SCRIPT_POST = 1 << 10, + RPMSENSE_SCRIPT_PREUN = 1 << 11, + RPMSENSE_SCRIPT_POSTUN = 1 << 12, +}; + +enum { + RPMTAG_NAME = 1000, /* s */ + RPMTAG_VERSION = 1001, /* s */ + RPMTAG_RELEASE = 1002, /* s */ + RPMTAG_EPOCH = 1003, /* i */ + RPMTAG_SUMMARY = 1004, /* s{} */ + RPMTAG_DESCRIPTION = 1005, /* s{} */ + RPMTAG_BUILDTIME = 1006, /* i */ + RPMTAG_BUILDHOST = 1007, /* s */ + RPMTAG_INSTALLTIME = 1008, /* i */ + RPMTAG_SIZE = 1009, /* i */ + RPMTAG_DISTRIBUTION = 1010, /* s */ + RPMTAG_VENDOR = 1011, /* s */ + RPMTAG_GIF = 1012, /* x */ + RPMTAG_XPM = 1013, /* x */ + RPMTAG_LICENSE = 1014, /* s */ + RPMTAG_PACKAGER = 1015, /* s */ + RPMTAG_GROUP = 1016, /* s{} */ + RPMTAG_CHANGELOG = 1017, /*!< s[] internal */ + RPMTAG_SOURCE = 1018, /* s[] */ + RPMTAG_PATCH = 1019, /* s[] */ + RPMTAG_URL = 1020, /* s */ + RPMTAG_OS = 1021, /* s legacy used int */ + RPMTAG_ARCH = 1022, /* s legacy used int */ + RPMTAG_PREIN = 1023, /* s */ + RPMTAG_POSTIN = 1024, /* s */ + RPMTAG_PREUN = 1025, /* s */ + RPMTAG_POSTUN = 1026, /* s */ + RPMTAG_OLDFILENAMES = 1027, /* s[] obsolete */ + RPMTAG_FILESIZES = 1028, /* i */ + RPMTAG_FILESTATES = 1029, /* c */ + RPMTAG_FILEMODES = 1030, /* h */ + RPMTAG_FILEUIDS = 1031, /*!< internal */ + RPMTAG_FILEGIDS = 1032, /*!< internal */ + RPMTAG_FILERDEVS = 1033, /* h */ + RPMTAG_FILEMTIMES = 1034, /* i */ + RPMTAG_FILEMD5S = 1035, /* s[] */ + RPMTAG_FILELINKTOS = 1036, /* s[] */ + RPMTAG_FILEFLAGS = 1037, /* i */ + RPMTAG_ROOT = 1038, /*!< internal - obsolete */ + RPMTAG_FILEUSERNAME = 1039, /* s[] */ + RPMTAG_FILEGROUPNAME = 1040, /* s[] */ + RPMTAG_EXCLUDE = 1041, /*!< internal - obsolete */ + RPMTAG_EXCLUSIVE = 1042, /*!< internal - obsolete */ + RPMTAG_ICON = 1043, + RPMTAG_SOURCERPM = 1044, /* s */ + RPMTAG_FILEVERIFYFLAGS = 1045, /* i */ + RPMTAG_ARCHIVESIZE = 1046, /* i */ + RPMTAG_PROVIDENAME = 1047, /* s[] */ + RPMTAG_REQUIREFLAGS = 1048, /* i */ + RPMTAG_REQUIRENAME = 1049, /* s[] */ + RPMTAG_REQUIREVERSION = 1050, /* s[] */ + RPMTAG_NOSOURCE = 1051, /*!< internal */ + RPMTAG_NOPATCH = 1052, /*!< internal */ + RPMTAG_CONFLICTFLAGS = 1053, /* i */ + RPMTAG_CONFLICTNAME = 1054, /* s[] */ + RPMTAG_CONFLICTVERSION = 1055, /* s[] */ + RPMTAG_DEFAULTPREFIX = 1056, /*!< internal - deprecated */ + RPMTAG_BUILDROOT = 1057, /*!< internal */ + RPMTAG_INSTALLPREFIX = 1058, /*!< internal - deprecated */ + RPMTAG_EXCLUDEARCH = 1059, + RPMTAG_EXCLUDEOS = 1060, + RPMTAG_EXCLUSIVEARCH = 1061, + RPMTAG_EXCLUSIVEOS = 1062, + RPMTAG_AUTOREQPROV = 1063, /*!< internal */ + RPMTAG_RPMVERSION = 1064, /* s */ + RPMTAG_TRIGGERSCRIPTS = 1065, /* s[] */ + RPMTAG_TRIGGERNAME = 1066, /* s[] */ + RPMTAG_TRIGGERVERSION = 1067, /* s[] */ + RPMTAG_TRIGGERFLAGS = 1068, /* i */ + RPMTAG_TRIGGERINDEX = 1069, /* i */ + RPMTAG_VERIFYSCRIPT = 1079, /* s */ + RPMTAG_CHANGELOGTIME = 1080, /* i */ + RPMTAG_CHANGELOGNAME = 1081, /* s[] */ + RPMTAG_CHANGELOGTEXT = 1082, /* s[] */ + RPMTAG_BROKENMD5 = 1083, /*!< internal - obsolete */ + RPMTAG_PREREQ = 1084, /*!< internal */ + RPMTAG_PREINPROG = 1085, /* s */ + RPMTAG_POSTINPROG = 1086, /* s */ + RPMTAG_PREUNPROG = 1087, /* s */ + RPMTAG_POSTUNPROG = 1088, /* s */ + RPMTAG_BUILDARCHS = 1089, + RPMTAG_OBSOLETENAME = 1090, /* s[] */ + RPMTAG_VERIFYSCRIPTPROG = 1091, /* s */ + RPMTAG_TRIGGERSCRIPTPROG = 1092, /* s */ + RPMTAG_DOCDIR = 1093, /*!< internal */ + RPMTAG_COOKIE = 1094, /* s */ + RPMTAG_FILEDEVICES = 1095, /* i */ + RPMTAG_FILEINODES = 1096, /* i */ + RPMTAG_FILELANGS = 1097, /* s[] */ + RPMTAG_PREFIXES = 1098, /* s[] */ + RPMTAG_INSTPREFIXES = 1099, /* s[] */ + RPMTAG_TRIGGERIN = 1100, /*!< internal */ + RPMTAG_TRIGGERUN = 1101, /*!< internal */ + RPMTAG_TRIGGERPOSTUN = 1102, /*!< internal */ + RPMTAG_AUTOREQ = 1103, /*!< internal */ + RPMTAG_AUTOPROV = 1104, /*!< internal */ + RPMTAG_CAPABILITY = 1105, /*!< internal - obsolete */ + RPMTAG_SOURCEPACKAGE = 1106, /*!< i src.rpm header marker */ + RPMTAG_OLDORIGFILENAMES = 1107, /*!< internal - obsolete */ + RPMTAG_BUILDPREREQ = 1108, /*!< internal */ + RPMTAG_BUILDREQUIRES = 1109, /*!< internal */ + RPMTAG_BUILDCONFLICTS = 1110, /*!< internal */ + RPMTAG_BUILDMACROS = 1111, /*!< internal - unused */ + RPMTAG_PROVIDEFLAGS = 1112, /* i */ + RPMTAG_PROVIDEVERSION = 1113, /* s[] */ + RPMTAG_OBSOLETEFLAGS = 1114, /* i */ + RPMTAG_OBSOLETEVERSION = 1115, /* s[] */ + RPMTAG_DIRINDEXES = 1116, /* i */ + RPMTAG_BASENAMES = 1117, /* s[] */ + RPMTAG_DIRNAMES = 1118, /* s[] */ + RPMTAG_ORIGDIRINDEXES = 1119, /*!< internal */ + RPMTAG_ORIGBASENAMES = 1120, /*!< internal */ + RPMTAG_ORIGDIRNAMES = 1121, /*!< internal */ + RPMTAG_OPTFLAGS = 1122, /* s */ + RPMTAG_DISTURL = 1123, /* s */ + RPMTAG_PAYLOADFORMAT = 1124, /* s */ + RPMTAG_PAYLOADCOMPRESSOR = 1125, /* s */ + RPMTAG_PAYLOADFLAGS = 1126, /* s */ + RPMTAG_INSTALLCOLOR = 1127, /*!< i transaction color when installed */ + RPMTAG_INSTALLTID = 1128, /* i */ + RPMTAG_REMOVETID = 1129, /* i */ + RPMTAG_SHA1RHN = 1130, /*!< internal - obsolete */ + RPMTAG_RHNPLATFORM = 1131, /* s */ + RPMTAG_PLATFORM = 1132, /* s */ + RPMTAG_PATCHESNAME = 1133, /*!< placeholder (SuSE) */ + RPMTAG_PATCHESFLAGS = 1134, /*!< placeholder (SuSE) */ + RPMTAG_PATCHESVERSION = 1135, /*!< placeholder (SuSE) */ + RPMTAG_CACHECTIME = 1136, /* i */ + RPMTAG_CACHEPKGPATH = 1137, /* s */ + RPMTAG_CACHEPKGSIZE = 1138, /* i */ + RPMTAG_CACHEPKGMTIME = 1139, /* i */ + RPMTAG_FILECOLORS = 1140, /* i */ + RPMTAG_FILECLASS = 1141, /* i */ + RPMTAG_CLASSDICT = 1142, /* s[] */ + RPMTAG_FILEDEPENDSX = 1143, /* i */ + RPMTAG_FILEDEPENDSN = 1144, /* i */ + RPMTAG_DEPENDSDICT = 1145, /* i */ + RPMTAG_SOURCEPKGID = 1146, /* x */ + RPMTAG_FILECONTEXTS = 1147, /* s[] */ + RPMTAG_FSCONTEXTS = 1148, /*!< s[] extension */ + RPMTAG_RECONTEXTS = 1149, /*!< s[] extension */ + RPMTAG_POLICIES = 1150, /*!< s[] selinux *.te policy file. */ + RPMTAG_PRETRANS = 1151, /* s */ + RPMTAG_POSTTRANS = 1152, /* s */ + RPMTAG_PRETRANSPROG = 1153, /* s */ + RPMTAG_POSTTRANSPROG = 1154, /* s */ + RPMTAG_DISTTAG = 1155, /* s */ + RPMTAG_SUGGESTSNAME = 1156, /* s[] extension placeholder */ + RPMTAG_SUGGESTSVERSION = 1157, /* s[] extension placeholder */ + RPMTAG_SUGGESTSFLAGS = 1158, /* i extension placeholder */ + RPMTAG_ENHANCESNAME = 1159, /* s[] extension placeholder */ + RPMTAG_ENHANCESVERSION = 1160, /* s[] extension placeholder */ + RPMTAG_ENHANCESFLAGS = 1161, /* i extension placeholder */ + RPMTAG_PRIORITY = 1162, /* i extension placeholder */ + RPMTAG_CVSID = 1163, /* s */ + RPMTAG_TRIGGERPREIN = 1171, /*!< internal */ +}; + +struct rpm_header { + unsigned char magic[4]; + unsigned char reserved[4]; + int nindex; + int hsize; +}; + +struct rpm_header_index { + int tag; + int type; + int offset; + int count; +}; + +struct razor_rpm { + struct rpm_header *signature; + struct rpm_header *header; + const char **dirs; + const char *pool; + void *map; + size_t size; + void *payload; +}; + +static struct rpm_header_index * +razor_rpm_get_header(struct razor_rpm *rpm, unsigned int tag) +{ + struct rpm_header_index *index, *end; + + index = (struct rpm_header_index *) (rpm->header + 1); + end = index + ntohl(rpm->header->nindex); + while (index < end) { + if (ntohl(index->tag) == tag) + return index; + index++; + } + + return NULL; +} + +static const void * +razor_rpm_get_indirect(struct razor_rpm *rpm, + unsigned int tag, unsigned int *count) +{ + struct rpm_header_index *index; + + index = razor_rpm_get_header(rpm, tag); + if (index != NULL) { + if (count) + *count = ntohl(index->count); + + return rpm->pool + ntohl(index->offset); + } + + return NULL; +} + +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 +import_properties(struct razor_importer *importer, uint32_t type, + struct razor_rpm *rpm, + int name_tag, int version_tag, int flags_tag) +{ + const char *name, *version; + const uint32_t *flags; + uint32_t f; + unsigned int i, count; + + name = razor_rpm_get_indirect(rpm, name_tag, &count); + if (name == NULL) + return; + + flags = razor_rpm_get_indirect(rpm, flags_tag, &count); + + version = razor_rpm_get_indirect(rpm, version_tag, &count); + for (i = 0; i < count; i++) { + f = rpm_to_razor_flags(ntohl(flags[i])); + razor_importer_add_property(importer, name, f | type, version); + name += strlen(name) + 1; + version += strlen(version) + 1; + } +} + +static void +import_files(struct razor_importer *importer, struct razor_rpm *rpm) +{ + const char *name; + const uint32_t *index; + unsigned int i, count; + char buffer[256]; + + if (rpm->dirs == NULL) + return; + + /* assert: count is the same for all arrays */ + index = razor_rpm_get_indirect(rpm, RPMTAG_DIRINDEXES, &count); + name = razor_rpm_get_indirect(rpm, RPMTAG_BASENAMES, &count); + for (i = 0; i < count; i++) { + snprintf(buffer, sizeof buffer, + "%s%s", rpm->dirs[ntohl(*index)], name); + razor_importer_add_file(importer, buffer); + name += strlen(name) + 1; + index++; + } +} + +struct razor_rpm * +razor_rpm_open(const char *filename) +{ + struct razor_rpm *rpm; + struct rpm_header_index *base, *index; + struct stat buf; + unsigned int count, i, nindex, hsize; + const char *name; + int fd; + + rpm = malloc(sizeof *rpm); + if (rpm == NULL) + return NULL; + memset(rpm, 0, sizeof *rpm); + + fd = open(filename, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "couldn't open %s\n", filename); + return NULL; + } + + if (fstat(fd, &buf) < 0) { + fprintf(stderr, "failed to stat %s (%m)\n", filename); + return NULL; + } + + rpm->size = buf.st_size; + rpm->map = mmap(NULL, rpm->size, PROT_READ, MAP_PRIVATE, fd, 0); + if (rpm->map == MAP_FAILED) { + fprintf(stderr, "couldn't mmap %s\n", filename); + return NULL; + } + close(fd); + + rpm->signature = rpm->map + RPM_LEAD_SIZE; + nindex = ntohl(rpm->signature->nindex); + hsize = ntohl(rpm->signature->hsize); + rpm->header = (void *) (rpm->signature + 1) + + ALIGN(nindex * sizeof *index + hsize, 8); + nindex = ntohl(rpm->header->nindex); + hsize = ntohl(rpm->header->hsize); + rpm->payload = (void *) (rpm->header + 1) + + nindex * sizeof *index + hsize; + + base = (struct rpm_header_index *) (rpm->header + 1); + rpm->pool = (void *) base + nindex * sizeof *index; + + /* Look up dir names now so we can index them directly. */ + name = razor_rpm_get_indirect(rpm, RPMTAG_DIRNAMES, &count); + if (name) { + rpm->dirs = calloc(count, sizeof *rpm->dirs); + for (i = 0; i < count; i++) { + rpm->dirs[i] = name; + name += strlen(name) + 1; + } + } else { + name = razor_rpm_get_indirect(rpm, RPMTAG_OLDFILENAMES, + &count); + if (name) { + fprintf(stderr, "old filenames not supported\n"); + return NULL; + } + } + + return rpm; +} + +struct cpio_file_header { + char magic[6]; + char inode[8]; + char mode[8]; + char uid[8]; + char gid[8]; + char nlink[8]; + char mtime[8]; + char filesize[8]; + char devmajor[8]; + char devminor[8]; + char rdevmajor[8]; + char rdevminor[8]; + char namesize[8]; + char checksum[8]; + char filename[0]; +}; + +/* gzip flags */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +struct installer { + const char *root; + struct razor_rpm *rpm; + z_stream stream; + unsigned char buffer[32768]; + size_t rest, length; +}; + +static int +installer_inflate(struct installer *installer) +{ + size_t length; + int err; + + if (installer->rest > sizeof installer->buffer) + length = sizeof installer->buffer; + else + length = installer->rest; + + installer->stream.next_out = installer->buffer; + installer->stream.avail_out = length; + err = inflate(&installer->stream, Z_SYNC_FLUSH); + if (err != Z_OK && err != Z_STREAM_END) { + fprintf(stderr, "inflate error: %d (%m)\n", err); + return -1; + } + + installer->rest -= length; + installer->length = length; + + return 0; +} + +static int +installer_align(struct installer *installer, size_t size) +{ + unsigned char buffer[4]; + int err; + + installer->stream.next_out = buffer; + installer->stream.avail_out = + (size - installer->stream.total_out) & (size - 1); + + if (installer->stream.avail_out == 0) + return 0; + + err = inflate(&installer->stream, Z_SYNC_FLUSH); + if (err != Z_OK && err != Z_STREAM_END) { + fprintf(stderr, "inflate error: %d (%m)\n", err); + return -1; + } + + return 0; +} + +static int +create_path(struct installer *installer, const char *path, unsigned int mode) +{ + char buffer[PATH_MAX]; + struct stat buf; + int fd, ret; + + if (razor_create_dir(installer->root, path) < 0) + return -1; + + snprintf(buffer, sizeof buffer, "%s%s", installer->root, path); + + switch (mode >> 12) { + case REG: + /* FIXME: handle the case where a file is already there. */ + fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, mode & 0x1ff); + if (fd < 0){ + fprintf(stderr, "failed to create file %s\n", buffer); + return -1; + } + while (installer->rest > 0) { + if (installer_inflate(installer)) { + fprintf(stderr, "failed to inflate\n"); + return -1; + } + if (razor_write(fd, installer->buffer, + installer->length)) { + fprintf(stderr, "failed to write payload\n"); + return -1; + } + } + if (close(fd) < 0) { + fprintf(stderr, "failed to close %s: %m\n", buffer); + return -1; + } + return 0; + case XDIR: + ret = mkdir(buffer, mode & 0x1ff); + if (ret == 0 || errno != EEXIST) + return ret; + if (stat(buffer, &buf) || !S_ISDIR(buf.st_mode)) { + /* FIXME: also check that mode match. */ + fprintf(stderr, + "%s exists but is not a directory\n", buffer); + return -1; + } + return 0; + case PIPE: + case CDEV: + case BDEV: + case SOCK: + printf("%s: unhandled file type %d\n", buffer, mode >> 12); + return 0; + case LINK: + if (installer_inflate(installer)) { + fprintf(stderr, "failed to inflate\n"); + return -1; + } + if (installer->length >= sizeof installer->buffer) { + fprintf(stderr, "link name too long\n"); + return -1; + } + installer->buffer[installer->length] = '\0'; + if (symlink((const char *) installer->buffer, buffer)) { + fprintf(stderr, "failed to create symlink, %m\n"); + return -1; + } + return 0; + default: + printf("%s: unknown file type %d\n", buffer, mode >> 12); + return 0; + } +} + +static int +run_script(struct installer *installer, + unsigned int program_tag, unsigned int script_tag) +{ + int pid, status, fd[2]; + const char *script = NULL, *program = NULL; + + program = razor_rpm_get_indirect(installer->rpm, program_tag, NULL); + script = razor_rpm_get_indirect(installer->rpm, script_tag, NULL); + if (program == NULL && script == NULL) { + return 0; + } else if (program == NULL) { + program = "/bin/sh"; + } + + if (pipe(fd) < 0) { + fprintf(stderr, "failed to create pipe\n"); + return -1; + } + pid = fork(); + if (pid < 0) { + fprintf(stderr, "failed to fork, %m\n"); + } else if (pid == 0) { + if (dup2(fd[0], STDIN_FILENO) < 0) { + fprintf(stderr, "failed redirect stdin, %m\n"); + return -1; + } + if (close(fd[0]) < 0 || close(fd[1]) < 0) { + fprintf(stderr, "failed to close pipe, %m\n"); + return -1; + } + if (chroot(installer->root) < 0) { + fprintf(stderr, "failed to chroot to %s, %m\n", + installer->root); + return -1; + } + printf("executing program %s in chroot %s\n", + program, installer->root); + if (execl(program, program, NULL)) { + fprintf(stderr, "failed to exec %s, %m\n", program); + exit(-1); + } + } else { + if (script && razor_write(fd[1], script, strlen(script)) < 0) { + fprintf(stderr, "failed to pipe script, %m\n"); + return -1; + } + if (close(fd[0]) || close(fd[1])) { + fprintf(stderr, "failed to close pipe, %m\n"); + return -1; + } + if (wait(&status) < 0) { + fprintf(stderr, "wait for child failed, %m"); + return -1; + } + if (status) + printf("script exited with status %d\n", status); + } + + return 0; +} + +static int +installer_init(struct installer *installer) +{ + unsigned char *gz_header; + int method, flags, err; + + gz_header = installer->rpm->payload; + if (gz_header[0] != 0x1f || gz_header[1] != 0x8b) { + fprintf(stderr, "payload section doesn't have gz header\n"); + return -1; + } + + method = gz_header[2]; + flags = gz_header[3]; + + if (method != Z_DEFLATED || flags != 0) { + fprintf(stderr, + "unknown payload compression method or flags set\n"); + return -1; + } + + installer->stream.zalloc = NULL; + installer->stream.zfree = NULL; + installer->stream.opaque = NULL; + + installer->stream.next_in = gz_header + 10; + installer->stream.avail_in = + (installer->rpm->map + installer->rpm->size) - + (void *) installer->stream.next_in; + installer->stream.next_out = NULL; + installer->stream.avail_out = 0; + + err = inflateInit2(&installer->stream, -MAX_WBITS); + if (err != Z_OK) { + fprintf(stderr, "inflateInit error: %d\n", err); + return -1; + } + + return 0; +} + +static int +installer_finish(struct installer *installer) +{ + int err; + + err = inflateEnd(&installer->stream); + + if (err != Z_OK) { + fprintf(stderr, "inflateEnd error: %d\n", err); + return -1; + } + + return 0; +} + +static unsigned long +fixed_hex_to_ulong(const char *hex, int length) +{ + long l; + int i; + + for (i = 0, l = 0; i < length; i++) { + if (hex[i] < 'a') + l = l * 16 + hex[i] - '0'; + else + l = l * 16 + hex[i] - 'a' + 10; + } + + return l; +} + +int +razor_rpm_install(struct razor_rpm *rpm, const char *root) +{ + struct installer installer; + struct cpio_file_header *header; + struct stat buf; + unsigned int mode; + char *path; + size_t filesize; + + installer.rpm = rpm; + installer.root = root; + + /* FIXME: Only do this before a transaction, not per rpm. */ + if (stat(root, &buf) < 0 || !S_ISDIR(buf.st_mode)) { + fprintf(stderr, + "root installation directory \"%s\" does not exist\n", + root); + return -1; + } + + if (installer_init(&installer)) + return -1; + + run_script(&installer, RPMTAG_PREINPROG, RPMTAG_PREIN); + + while (installer.stream.avail_in > 0) { + installer.rest = sizeof *header; + if (installer_inflate(&installer)) + return -1; + + header = (struct cpio_file_header *) installer.buffer; + mode = fixed_hex_to_ulong(header->mode, sizeof header->mode); + filesize = fixed_hex_to_ulong(header->filesize, + sizeof header->filesize); + + installer.rest = fixed_hex_to_ulong(header->namesize, + sizeof header->namesize); + + if (installer_inflate(&installer) || + installer_align(&installer, 4)) + return -1; + + path = (char *) installer.buffer; + /* This convention is so lame... */ + if (strcmp(path, "TRAILER!!!") == 0) + break; + + installer.rest = filesize; + if (create_path(&installer, path + 1, mode) < 0) + return -1; + if (installer_align(&installer, 4)) + return -1; + } + + if (installer_finish(&installer)) + return -1; + + run_script(&installer, RPMTAG_POSTINPROG, RPMTAG_POSTIN); + + return 0; +} + +int +razor_rpm_close(struct razor_rpm *rpm) +{ + int err; + + free(rpm->dirs); + err = munmap(rpm->map, rpm->size); + free(rpm); + + return err; +} + +int +razor_importer_add_rpm(struct razor_importer *importer, struct razor_rpm *rpm) +{ + const char *name, *version, *release, *arch, *summary; + const uint32_t *epoch; + char evr[128], buf[16]; + + name = razor_rpm_get_indirect(rpm, RPMTAG_NAME, NULL); + epoch = razor_rpm_get_indirect(rpm, RPMTAG_EPOCH, NULL); + version = razor_rpm_get_indirect(rpm, RPMTAG_VERSION, NULL); + release = razor_rpm_get_indirect(rpm, RPMTAG_RELEASE, NULL); + arch = razor_rpm_get_indirect(rpm, RPMTAG_ARCH, NULL); + summary = razor_rpm_get_indirect(rpm, RPMTAG_SUMMARY, NULL); + + if (epoch) { + snprintf(buf, sizeof buf, "%u", ntohl(*epoch)); + razor_build_evr(evr, sizeof evr, buf, version, release); + } else { + razor_build_evr(evr, sizeof evr, NULL, version, release); + } + razor_importer_begin_package(importer, name, evr, arch); + + import_properties(importer, RAZOR_PROPERTY_REQUIRES, rpm, + RPMTAG_REQUIRENAME, + RPMTAG_REQUIREVERSION, + RPMTAG_REQUIREFLAGS); + + import_properties(importer, RAZOR_PROPERTY_PROVIDES, rpm, + RPMTAG_PROVIDENAME, + RPMTAG_PROVIDEVERSION, + RPMTAG_PROVIDEFLAGS); + + import_properties(importer, RAZOR_PROPERTY_OBSOLETES, rpm, + RPMTAG_OBSOLETENAME, + RPMTAG_OBSOLETEVERSION, + RPMTAG_OBSOLETEFLAGS); + + import_properties(importer, RAZOR_PROPERTY_CONFLICTS, rpm, + RPMTAG_CONFLICTNAME, + RPMTAG_CONFLICTVERSION, + RPMTAG_CONFLICTFLAGS); + + import_files(importer, rpm); + + razor_importer_finish_package(importer); + + return 0; +} diff --git a/librazor/transaction.c b/librazor/transaction.c new file mode 100644 index 0000000..93b1bf2 --- /dev/null +++ b/librazor/transaction.c @@ -0,0 +1,912 @@ +/* + * 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-internal.h" +#include "razor.h" + +static int +provider_satisfies_requirement(struct razor_property *provider, + const char *provider_strings, + uint32_t flags, + const char *required) +{ + int cmp, len; + const char *provided = &provider_strings[provider->version]; + + if (!*required) + return 1; + if (!*provided) { + if (flags & RAZOR_PROPERTY_LESS) + return 0; + else + return 1; + } + + cmp = razor_versioncmp(provided, required); + + switch (flags & RAZOR_PROPERTY_RELATION_MASK) { + case RAZOR_PROPERTY_LESS: + return cmp < 0; + + case RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL: + if (cmp <= 0) + return 1; + /* fall through: FIXME, make sure this is correct */ + + case RAZOR_PROPERTY_EQUAL: + if (cmp == 0) + return 1; + + /* "foo == 1.1" is satisfied by "foo 1.1-2" */ + len = strlen(required); + if (!strncmp(required, provided, len) && provided[len] == '-') + return 1; + return 0; + + case RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL: + return cmp >= 0; + + case RAZOR_PROPERTY_GREATER: + return cmp > 0; + } + + /* shouldn't happen */ + return 0; +} + +#define TRANS_PACKAGE_PRESENT 1 +#define TRANS_PACKAGE_UPDATE 2 +#define TRANS_PROPERTY_SATISFIED 0x80000000 + +struct transaction_set { + struct razor_set *set; + uint32_t *packages; + uint32_t *properties; +}; + +struct razor_transaction { + int package_count, errors; + struct transaction_set system, upstream; + int changes; +}; + +static void +transaction_set_init(struct transaction_set *ts, struct razor_set *set) +{ + int count; + + ts->set = set; + count = set->packages.size / sizeof (struct razor_package); + ts->packages = zalloc(count * sizeof *ts->packages); + count = set->properties.size / sizeof (struct razor_property); + ts->properties = zalloc(count * sizeof *ts->properties); +} + +static void +transaction_set_release(struct transaction_set *ts) +{ + free(ts->packages); + free(ts->properties); +} + +static void +transaction_set_install_package(struct transaction_set *ts, + struct razor_package *package) +{ + struct razor_package *pkgs; + struct list *prop; + int i; + + pkgs = ts->set->packages.data; + i = package - pkgs; + if (ts->packages[i] == TRANS_PACKAGE_PRESENT) + return; + + ts->packages[i] = TRANS_PACKAGE_PRESENT; + + prop = list_first(&package->properties, &ts->set->property_pool); + while (prop) { + ts->properties[prop->data]++; + prop = list_next(prop); + } +} + +static void +transaction_set_remove_package(struct transaction_set *ts, + struct razor_package *package) +{ + struct razor_package *pkgs; + struct list *prop; + int i; + + pkgs = ts->set->packages.data; + i = package - pkgs; + if (ts->packages[i] == 0) + return; + + ts->packages[i] = 0; + + prop = list_first(&package->properties, &ts->set->property_pool); + while (prop) { + ts->properties[prop->data]--; + prop = list_next(prop); + } +} + +struct razor_transaction * +razor_transaction_create(struct razor_set *system, struct razor_set *upstream) +{ + struct razor_transaction *trans; + struct razor_package *p, *spkgs, *pend; + + trans = zalloc(sizeof *trans); + transaction_set_init(&trans->system, system); + transaction_set_init(&trans->upstream, upstream); + + spkgs = trans->system.set->packages.data; + pend = trans->system.set->packages.data + + trans->system.set->packages.size; + for (p = spkgs; p < pend; p++) + transaction_set_install_package(&trans->system, p); + + return trans; +} + +void +razor_transaction_install_package(struct razor_transaction *trans, + struct razor_package *package) +{ + transaction_set_install_package(&trans->upstream, package); + trans->changes++; +} + +void +razor_transaction_remove_package(struct razor_transaction *trans, + struct razor_package *package) +{ + transaction_set_remove_package(&trans->system, package); + trans->changes++; +} + +void +razor_transaction_update_package(struct razor_transaction *trans, + struct razor_package *package) +{ + struct razor_package *spkgs, *upkgs, *end; + + spkgs = trans->system.set->packages.data; + upkgs = trans->upstream.set->packages.data; + end = trans->system.set->packages.data + + trans->system.set->packages.size; + if (spkgs <= package && package < end) + trans->system.packages[package - spkgs] |= TRANS_PACKAGE_UPDATE; + else + trans->upstream.packages[package - upkgs] |= TRANS_PACKAGE_UPDATE; +} + +struct prop_iter { + struct razor_property *p, *start, *end; + const char *pool; + uint32_t *present; +}; + +static void +prop_iter_init(struct prop_iter *pi, struct transaction_set *ts) +{ + pi->p = ts->set->properties.data; + pi->start = ts->set->properties.data; + pi->end = ts->set->properties.data + ts->set->properties.size; + pi->pool = ts->set->string_pool.data; + pi->present = ts->properties; +} + +static int +prop_iter_next(struct prop_iter *pi, uint32_t flags, struct razor_property **p) +{ + while (pi->p < pi->end) { + if ((pi->present[pi->p - pi->start] & ~TRANS_PROPERTY_SATISFIED) && + (pi->p->flags & RAZOR_PROPERTY_TYPE_MASK) == flags) { + *p = pi->p++; + return 1; + } + pi->p++; + } + + return 0; +} + +static struct razor_property * +prop_iter_seek_to(struct prop_iter *pi, + uint32_t flags, const char *match) +{ + uint32_t name; + + while (pi->p < pi->end && strcmp(&pi->pool[pi->p->name], match) < 0) + pi->p++; + + if (pi->p == pi->end || strcmp(&pi->pool[pi->p->name], match) > 0) + return NULL; + + name = pi->p->name; + while (pi->p < pi->end && + pi->p->name == name && + (pi->p->flags & RAZOR_PROPERTY_TYPE_MASK) != flags) + pi->p++; + + if (pi->p == pi->end || pi->p->name != name) + return NULL; + + return pi->p; +} + +/* Remove packages from set that provide any of the matching (same + * name and type) providers from ppi onwards that match the + * requirement that rpi points to. */ +static void +remove_matching_providers(struct razor_transaction *trans, + struct prop_iter *ppi, + uint32_t flags, + const char *version) +{ + struct razor_property *p; + struct razor_package *pkg, *pkgs; + struct razor_package_iterator pkg_iter; + struct razor_set *set; + const char *n, *v, *a; + uint32_t type; + + if (ppi->present == trans->system.properties) + set = trans->system.set; + else + set = trans->upstream.set; + + pkgs = (struct razor_package *) set->packages.data; + type = ppi->p->flags & RAZOR_PROPERTY_TYPE_MASK; + for (p = ppi->p; + p < ppi->end && + p->name == ppi->p->name && + (p->flags & RAZOR_PROPERTY_TYPE_MASK) == type; + p++) { + if (!ppi->present[p - ppi->start]) + continue; + if (!provider_satisfies_requirement(p, ppi->pool, + flags, version)) + continue; + + razor_package_iterator_init_for_property(&pkg_iter, set, p); + while (razor_package_iterator_next(&pkg_iter, + &pkg, &n, &v, &a)) { + fprintf(stderr, "removing %s-%s\n", n, v); + razor_transaction_remove_package(trans, pkg); + } + } +} + +static void +flag_matching_providers(struct razor_transaction *trans, + struct prop_iter *ppi, + struct razor_property *r, + struct prop_iter *rpi, + unsigned int flag) +{ + struct razor_property *p; + struct razor_package *pkg, *pkgs; + struct razor_package_iterator pkg_iter; + struct razor_set *set; + const char *name, *version, *arch; + uint32_t *flags, type; + + if (ppi->present == trans->system.properties) { + set = trans->system.set; + flags = trans->system.packages; + } else { + set = trans->upstream.set; + flags = trans->upstream.packages; + } + + pkgs = (struct razor_package *) set->packages.data; + type = ppi->p->flags & RAZOR_PROPERTY_TYPE_MASK; + for (p = ppi->p; + p < ppi->end && + p->name == ppi->p->name && + (p->flags & RAZOR_PROPERTY_TYPE_MASK) == type; + p++) { + if (!ppi->present[p - ppi->start]) + continue; + if (!provider_satisfies_requirement(p, ppi->pool, + r->flags, + &rpi->pool[r->version])) + continue; + + razor_package_iterator_init_for_property(&pkg_iter, set, p); + while (razor_package_iterator_next(&pkg_iter, &pkg, + &name, &version, &arch)) { + + fprintf(stderr, "flagging %s-%s for providing %s matching %s %s\n", + name, version, + ppi->pool + p->name, + rpi->pool + r->name, + rpi->pool + r->version); + flags[pkg - pkgs] |= flag; + } + } +} + +static struct razor_package * +pick_matching_provider(struct razor_set *set, + struct prop_iter *ppi, + uint32_t flags, + const char *version) +{ + struct razor_property *p; + struct razor_package *pkgs; + struct list *i; + uint32_t type; + + /* This is where we decide which pkgs to pull in to satisfy a + * requirement. There may be several different providers + * (different versions) and each version of a provider may + * come from a number of packages. We pick the first package + * from the first provider that matches. */ + + pkgs = set->packages.data; + type = ppi->p->flags & RAZOR_PROPERTY_TYPE_MASK; + for (p = ppi->p; + p < ppi->end && + p->name == ppi->p->name && + (p->flags & RAZOR_PROPERTY_TYPE_MASK) == type && + ppi->present[p - ppi->start] == 0; + p++) { + if (!provider_satisfies_requirement(p, ppi->pool, + flags, version)) + continue; + + i = list_first(&p->packages, &set->package_pool); + + return &pkgs[i->data]; + } + + return NULL; +} + +static void +remove_obsoleted_packages(struct razor_transaction *trans) +{ + struct razor_property *up; + struct razor_package *spkgs; + struct prop_iter spi, upi; + + spkgs = trans->system.set->packages.data; + prop_iter_init(&spi, &trans->system); + prop_iter_init(&upi, &trans->upstream); + + while (prop_iter_next(&upi, RAZOR_PROPERTY_OBSOLETES, &up)) { + if (!prop_iter_seek_to(&spi, RAZOR_PROPERTY_PROVIDES, + &upi.pool[up->name])) + continue; + remove_matching_providers(trans, &spi, up->flags, + &upi.pool[up->version]); + } +} + +static int +any_provider_satisfies_requirement(struct prop_iter *ppi, + uint32_t flags, + const char *version) +{ + struct razor_property *p; + uint32_t type; + + type = ppi->p->flags & RAZOR_PROPERTY_TYPE_MASK; + for (p = ppi->p; + p < ppi->end && + p->name == ppi->p->name && + (p->flags & RAZOR_PROPERTY_TYPE_MASK) == type; + p++) { + if (ppi->present[p - ppi->start] > 0 && + provider_satisfies_requirement(p, ppi->pool, + flags, version)) + return 1; + } + + return 0; +} + +static void +clear_requires_flags(struct transaction_set *ts) +{ + struct razor_property *p; + const char *pool; + int i, count; + + count = ts->set->properties.size / sizeof *p; + p = ts->set->properties.data; + pool = ts->set->string_pool.data; + for (i = 0; i < count; i++) { + ts->properties[i] &= ~TRANS_PROPERTY_SATISFIED; + if (strncmp(&pool[p[i].name], "rpmlib(", 7) == 0) + ts->properties[i] |= TRANS_PROPERTY_SATISFIED; + } +} + +const char * +razor_property_relation_to_string(struct razor_property *p) +{ + switch (p->flags & RAZOR_PROPERTY_RELATION_MASK) { + case RAZOR_PROPERTY_LESS: + return "<"; + + case RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL: + return "<="; + + case RAZOR_PROPERTY_EQUAL: + return "="; + + case RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL: + return ">="; + + case RAZOR_PROPERTY_GREATER: + return ">"; + + default: + return "?"; + } +} + +const char * +razor_property_type_to_string(struct razor_property *p) +{ + switch (p->flags & RAZOR_PROPERTY_TYPE_MASK) { + case RAZOR_PROPERTY_REQUIRES: + return "requires"; + case RAZOR_PROPERTY_PROVIDES: + return "provides"; + case RAZOR_PROPERTY_CONFLICTS: + return "conflicts"; + case RAZOR_PROPERTY_OBSOLETES: + return "obsoletes"; + default: + return NULL; + } +} + +static void +mark_satisfied_requires(struct razor_transaction *trans, + struct transaction_set *rts, + struct transaction_set *pts) +{ + struct prop_iter rpi, ppi; + struct razor_property *rp; + + prop_iter_init(&rpi, rts); + prop_iter_init(&ppi, pts); + + while (prop_iter_next(&rpi, RAZOR_PROPERTY_REQUIRES, &rp)) { + if (!prop_iter_seek_to(&ppi, RAZOR_PROPERTY_PROVIDES, + &rpi.pool[rp->name])) + continue; + + if (any_provider_satisfies_requirement(&ppi, rp->flags, + &rpi.pool[rp->version])) + rpi.present[rp - rpi.start] |= TRANS_PROPERTY_SATISFIED; + } +} + +static void +mark_all_satisfied_requires(struct razor_transaction *trans) +{ + clear_requires_flags(&trans->system); + clear_requires_flags(&trans->upstream); + mark_satisfied_requires(trans, &trans->system, &trans->system); + mark_satisfied_requires(trans, &trans->system, &trans->upstream); + mark_satisfied_requires(trans, &trans->upstream, &trans->system); + mark_satisfied_requires(trans, &trans->upstream, &trans->upstream); +} + +static void +update_unsatisfied_packages(struct razor_transaction *trans) +{ + struct razor_package *spkgs, *pkg; + struct razor_property *sp; + struct prop_iter spi; + struct razor_package_iterator pkg_iter; + const char *name, *version, *arch; + + spkgs = trans->system.set->packages.data; + prop_iter_init(&spi, &trans->system); + + while (prop_iter_next(&spi, RAZOR_PROPERTY_REQUIRES, &sp)) { + if (spi.present[sp - spi.start] & TRANS_PROPERTY_SATISFIED) + continue; + + razor_package_iterator_init_for_property(&pkg_iter, + trans->system.set, + sp); + while (razor_package_iterator_next(&pkg_iter, &pkg, + &name, &version, &arch)) { + fprintf(stderr, "updating %s because %s %s %s " + "isn't satisfied\n", + name, spi.pool + sp->name, + razor_property_relation_to_string(sp), + spi.pool + sp->version); + trans->system.packages[pkg - spkgs] |= + TRANS_PACKAGE_UPDATE; + } + } +} + +void +razor_transaction_update_all(struct razor_transaction *trans) +{ + struct razor_package *p; + int i, count; + + count = trans->system.set->packages.size / sizeof *p; + for (i = 0; i < count; i++) + trans->system.packages[i] |= TRANS_PACKAGE_UPDATE; +} + +static void +update_conflicted_packages(struct razor_transaction *trans) +{ + struct razor_package *pkg, *spkgs; + struct razor_property *up, *sp; + struct prop_iter spi, upi; + struct razor_package_iterator pkg_iter; + const char *name, *version, *arch; + + spkgs = trans->system.set->packages.data; + prop_iter_init(&spi, &trans->system); + prop_iter_init(&upi, &trans->upstream); + + while (prop_iter_next(&spi, RAZOR_PROPERTY_CONFLICTS, &sp)) { + if (!prop_iter_seek_to(&upi, RAZOR_PROPERTY_PROVIDES, + &spi.pool[sp->name])) + continue; + + if (!any_provider_satisfies_requirement(&upi, sp->flags, + &spi.pool[sp->version])) + continue; + + razor_package_iterator_init_for_property(&pkg_iter, + trans->system.set, + sp); + while (razor_package_iterator_next(&pkg_iter, &pkg, + &name, &version, &arch)) { + fprintf(stderr, "updating %s %s because it conflicts with %s", + name, version, spi.pool + sp->name); + trans->system.packages[pkg - spkgs] |= + TRANS_PACKAGE_UPDATE; + } + } + + prop_iter_init(&spi, &trans->system); + prop_iter_init(&upi, &trans->upstream); + + while (prop_iter_next(&upi, RAZOR_PROPERTY_CONFLICTS, &up)) { + sp = prop_iter_seek_to(&spi, RAZOR_PROPERTY_PROVIDES, + &upi.pool[upi.p->name]); + + if (sp) + flag_matching_providers(trans, &spi, up, &upi, + TRANS_PACKAGE_UPDATE); + } +} + +static void +pull_in_requirements(struct razor_transaction *trans, + struct prop_iter *rpi, struct prop_iter *ppi) +{ + struct razor_property *rp, *pp; + struct razor_package *pkg, *upkgs; + + upkgs = trans->upstream.set->packages.data; + while (prop_iter_next(rpi, RAZOR_PROPERTY_REQUIRES, &rp)) { + if (rpi->present[rp - rpi->start] & TRANS_PROPERTY_SATISFIED) + continue; + + pp = prop_iter_seek_to(ppi, RAZOR_PROPERTY_PROVIDES, + &rpi->pool[rp->name]); + if (pp == NULL) + continue; + pkg = pick_matching_provider(trans->upstream.set, + ppi, rp->flags, + &rpi->pool[rp->version]); + if (pkg == NULL) + continue; + + rpi->present[rp - rpi->start] |= TRANS_PROPERTY_SATISFIED; + + fprintf(stderr, "pulling in %s which provides %s %s %s " + "to satisfy %s %s %s\n", + ppi->pool + pkg->name, + ppi->pool + pp->name, + razor_property_relation_to_string(pp), + ppi->pool + pp->version, + &rpi->pool[rp->name], + razor_property_relation_to_string(rp), + &rpi->pool[rp->version]); + + trans->upstream.packages[pkg - upkgs] |= TRANS_PACKAGE_UPDATE; + } +} + +static void +pull_in_all_requirements(struct razor_transaction *trans) +{ + struct prop_iter rpi, ppi; + + prop_iter_init(&rpi, &trans->system); + prop_iter_init(&ppi, &trans->upstream); + pull_in_requirements(trans, &rpi, &ppi); + + prop_iter_init(&rpi, &trans->upstream); + prop_iter_init(&ppi, &trans->upstream); + pull_in_requirements(trans, &rpi, &ppi); +} + +static void +flush_scheduled_system_updates(struct razor_transaction *trans) +{ + struct razor_package_iterator *pi; + struct razor_package *p, *pkg, *spkgs; + struct prop_iter ppi; + const char *name, *version, *arch; + + spkgs = trans->system.set->packages.data; + pi = razor_package_iterator_create(trans->system.set); + prop_iter_init(&ppi, &trans->upstream); + + while (razor_package_iterator_next(pi, &p, &name, &version, &arch)) { + if (!(trans->system.packages[p - spkgs] & TRANS_PACKAGE_UPDATE)) + continue; + + if (!prop_iter_seek_to(&ppi, RAZOR_PROPERTY_PROVIDES, name)) + continue; + + pkg = pick_matching_provider(trans->upstream.set, &ppi, + RAZOR_PROPERTY_GREATER, version); + if (pkg == NULL) + continue; + + fprintf(stderr, "updating %s-%s to %s-%s\n", + name, version, + &ppi.pool[pkg->name], &ppi.pool[pkg->version]); + + razor_transaction_remove_package(trans, p); + razor_transaction_install_package(trans, pkg); + } + + razor_package_iterator_destroy(pi); +} + +static void +flush_scheduled_upstream_updates(struct razor_transaction *trans) +{ + struct razor_package_iterator *pi; + struct razor_package *p, *upkgs; + struct prop_iter spi; + const char *name, *version, *arch; + + upkgs = trans->upstream.set->packages.data; + pi = razor_package_iterator_create(trans->upstream.set); + prop_iter_init(&spi, &trans->system); + + while (razor_package_iterator_next(pi, &p, &name, &version, &arch)) { + if (!(trans->upstream.packages[p - upkgs] & TRANS_PACKAGE_UPDATE)) + continue; + + if (prop_iter_seek_to(&spi, RAZOR_PROPERTY_PROVIDES, name)) + remove_matching_providers(trans, + &spi, + RAZOR_PROPERTY_LESS, + version); + razor_transaction_install_package(trans, p); + fprintf(stderr, "installing %s-%s\n", name, version); + } +} + +int +razor_transaction_resolve(struct razor_transaction *trans) +{ + int last = 0; + + flush_scheduled_system_updates(trans); + flush_scheduled_upstream_updates(trans); + + while (last < trans->changes) { + last = trans->changes; + remove_obsoleted_packages(trans); + mark_all_satisfied_requires(trans); + update_unsatisfied_packages(trans); + update_conflicted_packages(trans); + pull_in_all_requirements(trans); + flush_scheduled_system_updates(trans); + flush_scheduled_upstream_updates(trans); + } + + return trans->changes; +} + +static void +describe_unsatisfied(struct razor_set *set, struct razor_property *rp) +{ + struct razor_package_iterator pi; + struct razor_package *pkg; + const char *name, *version, *arch, *pool; + + pool = set->string_pool.data; + if (pool[rp->version] == '\0') { + razor_package_iterator_init_for_property(&pi, set, rp); + while (razor_package_iterator_next(&pi, &pkg, + &name, &version, &arch)) + fprintf(stderr, "%s is needed by %s-%s.%s\n", + &pool[rp->name], + name, version, arch); + } else { + razor_package_iterator_init_for_property(&pi, set, rp); + while (razor_package_iterator_next(&pi, &pkg, + &name, &version, &arch)) + fprintf(stderr, "%s %s %s is needed by %s-%s.%s\n", + &pool[rp->name], + razor_property_relation_to_string(rp), + &pool[rp->version], + name, version, arch); + } +} + +int +razor_transaction_describe(struct razor_transaction *trans) +{ + struct prop_iter rpi; + struct razor_property *rp; + int unsatisfied; + + flush_scheduled_system_updates(trans); + flush_scheduled_upstream_updates(trans); + mark_all_satisfied_requires(trans); + + unsatisfied = 0; + prop_iter_init(&rpi, &trans->system); + while (prop_iter_next(&rpi, RAZOR_PROPERTY_REQUIRES, &rp)) { + if (!(rpi.present[rp - rpi.start] & TRANS_PROPERTY_SATISFIED)) { + describe_unsatisfied(trans->system.set, rp); + unsatisfied++; + } + } + + prop_iter_init(&rpi, &trans->upstream); + while (prop_iter_next(&rpi, RAZOR_PROPERTY_REQUIRES, &rp)) { + if (!(rpi.present[rp - rpi.start] & TRANS_PROPERTY_SATISFIED)) { + describe_unsatisfied(trans->upstream.set, rp); + unsatisfied++; + } + } + + return unsatisfied; +} + +int +razor_transaction_unsatisfied_property(struct razor_transaction *trans, + const char *name, + uint32_t flags, + const char *version) +{ + struct prop_iter pi; + struct razor_property *p; + + prop_iter_init(&pi, &trans->system); + while (prop_iter_next(&pi, flags, &p)) { + if (!(trans->system.properties[p - pi.start] & TRANS_PROPERTY_SATISFIED) && + p->flags == flags && + strcmp(&pi.pool[p->name], name) == 0 && + strcmp(&pi.pool[p->version], version) == 0) + + return 1; + } + + prop_iter_init(&pi, &trans->upstream); + while (prop_iter_next(&pi, flags, &p)) { + if (!(trans->upstream.properties[p - pi.start] & TRANS_PROPERTY_SATISFIED) && + p->flags == flags && + strcmp(&pi.pool[p->name], name) == 0 && + strcmp(&pi.pool[p->version], version) == 0) + + return 1; + } + + return 0; +} + +struct razor_set * +razor_transaction_finish(struct razor_transaction *trans) +{ + struct razor_merger *merger; + struct razor_package *u, *uend, *upkgs, *s, *send, *spkgs; + char *upool, *spool; + int cmp; + + s = trans->system.set->packages.data; + spkgs = trans->system.set->packages.data; + send = trans->system.set->packages.data + + trans->system.set->packages.size; + spool = trans->system.set->string_pool.data; + + u = trans->upstream.set->packages.data; + upkgs = trans->upstream.set->packages.data; + uend = trans->upstream.set->packages.data + + trans->upstream.set->packages.size; + upool = trans->upstream.set->string_pool.data; + + merger = razor_merger_create(trans->system.set, trans->upstream.set); + while (s < send || u < uend) { + if (s < send && u < uend) + cmp = strcmp(&spool[s->name], &upool[u->name]); + else if (s < send) + cmp = -1; + else + cmp = 1; + + if (cmp < 0) { + if (trans->system.packages[s - spkgs] & TRANS_PACKAGE_PRESENT) + razor_merger_add_package(merger, s); + s++; + } else if (cmp == 0) { + if (trans->system.packages[s - spkgs] & TRANS_PACKAGE_PRESENT) + razor_merger_add_package(merger, s); + if (trans->upstream.packages[u - upkgs] & TRANS_PACKAGE_PRESENT) + razor_merger_add_package(merger, u); + + s++; + u++; + } else { + if (trans->upstream.packages[u - upkgs] & TRANS_PACKAGE_PRESENT) + razor_merger_add_package(merger, u); + u++; + } + } + + razor_transaction_destroy(trans); + + return razor_merger_finish(merger); +} + +void +razor_transaction_destroy(struct razor_transaction *trans) +{ + transaction_set_release(&trans->system); + transaction_set_release(&trans->upstream); + free(trans); +} diff --git a/librazor/types.c b/librazor/types.c new file mode 100644 index 0000000..1dc9aef --- /dev/null +++ b/librazor/types.c @@ -0,0 +1,246 @@ +#include <stdlib.h> +#include <string.h> + +#include "razor-internal.h" + +void +array_init(struct array *array) +{ + memset(array, 0, sizeof *array); +} + +void +array_release(struct array *array) +{ + free(array->data); +} + +void * +array_add(struct array *array, int size) +{ + int alloc; + void *data, *p; + + if (array->alloc > 0) + alloc = array->alloc; + else + alloc = 16; + + while (alloc < array->size + size) + alloc *= 2; + + if (array->alloc < alloc) { + data = realloc(array->data, alloc); + if (data == NULL) + return 0; + array->data = data; + array->alloc = alloc; + } + + p = array->data + array->size; + array->size += size; + + return p; +} + +/* RAZOR_IMMEDIATE and RAZOR_ENTRY_LAST must have the same value */ +#define RAZOR_ENTRY_LAST 0x80 +#define RAZOR_IMMEDIATE 0x80 +#define RAZOR_EMPTY_LIST 0xff + +void +list_set_empty(struct list_head *head) +{ + head->list_ptr = ~0; + head->flags = RAZOR_EMPTY_LIST; +} + +void +list_set_ptr(struct list_head *head, uint32_t ptr) +{ + head->list_ptr = ptr; + head->flags = 0; +} + +void +list_set_array(struct list_head *head, struct array *pool, + struct array *items, int force_indirect) +{ + struct list *p; + + if (!force_indirect) { + if (items->size == 0) { + list_set_empty(head); + return; + } else if (items->size == sizeof (uint32_t)) { + head->list_ptr = *(uint32_t *) items->data; + head->flags = RAZOR_IMMEDIATE; + return; + } + } + + p = array_add(pool, items->size); + memcpy(p, items->data, items->size); + p[items->size / sizeof *p - 1].flags = RAZOR_ENTRY_LAST; + list_set_ptr(head, p - (struct list *) pool->data); +} + +struct list * +list_first(struct list_head *head, struct array *pool) +{ + if (head->flags == RAZOR_EMPTY_LIST) + return NULL; + else if (head->flags == RAZOR_IMMEDIATE) + return (struct list *) head; + else + return (struct list *) pool->data + head->list_ptr; +} + +struct list * +list_next(struct list *list) +{ + if (list->flags) + return NULL; + return ++list; +} + +void +list_remap_pool(struct array *pool, uint32_t *map) +{ + struct list *p, *end; + + end = pool->data + pool->size; + for (p = pool->data; p < end; p++) + p->data = map[p->data]; +} + +void +list_remap_head(struct list_head *head, uint32_t *map) +{ + if (head->flags == RAZOR_IMMEDIATE) + head->list_ptr = map[head->list_ptr]; +} + + +void +hashtable_init(struct hashtable *table, struct array *pool) +{ + array_init(&table->buckets); + table->pool = pool; +} + +void +hashtable_release(struct hashtable *table) +{ + array_release(&table->buckets); +} + +static unsigned int +hash_string(const char *key) +{ + const char *p; + unsigned int hash = 0; + + for (p = key; *p; p++) + hash = (hash * 617) ^ *p; + + return hash; +} + +uint32_t +hashtable_lookup(struct hashtable *table, const char *key) +{ + unsigned int mask, start, i; + uint32_t *b; + char *pool; + + pool = table->pool->data; + mask = table->buckets.alloc - 1; + start = hash_string(key) * sizeof(uint32_t); + + for (i = 0; i < table->buckets.alloc; i += sizeof *b) { + b = table->buckets.data + ((start + i) & mask); + + if (*b == 0) + return 0; + + if (strcmp(key, &pool[*b]) == 0) + return *b; + } + + return 0; +} + +static void +do_insert(struct hashtable *table, uint32_t value) +{ + unsigned int mask, start, i; + uint32_t *b; + const char *key; + + key = (char *) table->pool->data + value; + mask = table->buckets.alloc - 1; + start = hash_string(key) * sizeof(uint32_t); + + for (i = 0; i < table->buckets.alloc; i += sizeof *b) { + b = table->buckets.data + ((start + i) & mask); + if (*b == 0) { + *b = value; + break; + } + } +} + +static uint32_t +add_to_string_pool(struct hashtable *table, const char *key) +{ + int len; + char *p; + + len = strlen(key) + 1; + p = array_add(table->pool, len); + memcpy(p, key, len); + + return p - (char *) table->pool->data; +} + +uint32_t +hashtable_insert(struct hashtable *table, const char *key) +{ + uint32_t value, *buckets, *b, *end; + int alloc; + + alloc = table->buckets.alloc; + array_add(&table->buckets, 4 * sizeof *buckets); + if (alloc != table->buckets.alloc) { + end = table->buckets.data + alloc; + memset(end, 0, table->buckets.alloc - alloc); + for (b = table->buckets.data; b < end; b++) { + value = *b; + if (value != 0) { + *b = 0; + do_insert(table, value); + } + } + } + + value = add_to_string_pool(table, key); + do_insert (table, value); + + return value; +} + +uint32_t +hashtable_tokenize(struct hashtable *table, const char *string) +{ + uint32_t token; + + if (string == NULL) + string = ""; + + token = hashtable_lookup(table, string); + if (token != 0) + return token; + + return hashtable_insert(table, string); +} diff --git a/librazor/util.c b/librazor/util.c new file mode 100644 index 0000000..29a7716 --- /dev/null +++ b/librazor/util.c @@ -0,0 +1,167 @@ +#include <limits.h> +#include <string.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <unistd.h> + +#include "razor-internal.h" + +int +razor_create_dir(const char *root, const char *path) +{ + char buffer[PATH_MAX], *p; + const char *slash, *next; + struct stat buf; + + /* Create all sub-directories in dir. We know root exists and + * is a dir, root does not end in a '/', and path has a + * leading '/'. */ + + strcpy(buffer, root); + p = buffer + strlen(buffer); + slash = path; + for (slash = path; *slash != '\0'; slash = next) { + next = strchr(slash + 1, '/'); + if (next == NULL) + break; + + memcpy(p, slash, next - slash); + p += next - slash; + *p = '\0'; + + if (stat(buffer, &buf) == 0) { + if (!S_ISDIR(buf.st_mode)) { + fprintf(stderr, + "%s exists but is not a directory\n", + buffer); + return -1; + } + } else if (mkdir(buffer, 0777) < 0) { + fprintf(stderr, "failed to make directory %s: %m\n", + buffer); + return -1; + } + + /* FIXME: What to do about permissions for dirs we + * have to create but are not in the cpio archive? */ + } + + return 0; +} + +int +razor_write(int fd, const void *data, size_t size) +{ + size_t rest; + ssize_t written; + const unsigned char *p; + + rest = size; + p = data; + while (rest > 0) { + written = write(fd, p, rest); + if (written < 0) { + fprintf(stderr, "write error: %m\n"); + return -1; + } + rest -= written; + p += written; + } + + return 0; +} + +struct qsort_context { + size_t size; + razor_compare_with_data_func_t compare; + void *data; +}; + +static void +qsort_swap(void *p1, void *p2, size_t size) +{ + char buffer[size]; + + memcpy(buffer, p1, size); + memcpy(p1, p2, size); + memcpy(p2, buffer, size); +} + +static void +__qsort_with_data(void *base, size_t nelem, uint32_t *map, + struct qsort_context *ctx) +{ + void *p, *start, *end, *pivot; + uint32_t *mp, *mstart, *mend, tmp; + int left, right, result; + size_t size = ctx->size; + + p = base; + start = base; + end = base + nelem * size; + mp = map; + mstart = map; + mend = map + nelem; + pivot = base + (random() % nelem) * size; + + while (p < end) { + result = ctx->compare(p, pivot, ctx->data); + if (result < 0) { + qsort_swap(p, start, size); + tmp = *mp; + *mp = *mstart; + *mstart = tmp; + if (start == pivot) + pivot = p; + start += size; + mstart++; + p += size; + mp++; + } else if (result == 0) { + p += size; + mp++; + } else { + end -= size; + mend--; + qsort_swap(p, end, size); + tmp = *mp; + *mp = *mend; + *mend = tmp; + if (end == pivot) + pivot = p; + } + } + + left = (start - base) / size; + right = (base + nelem * size - end) / size; + if (left > 1) + __qsort_with_data(base, left, map, ctx); + if (right > 1) + __qsort_with_data(end, right, mend, ctx); +} + +uint32_t * +razor_qsort_with_data(void *base, size_t nelem, size_t size, + razor_compare_with_data_func_t compare, void *data) +{ + struct qsort_context ctx; + uint32_t *map; + int i; + + if (nelem == 0) + return NULL; + + ctx.size = size; + ctx.compare = compare; + ctx.data = data; + + map = malloc(nelem * sizeof (uint32_t)); + for (i = 0; i < nelem; i++) + map[i] = i; + + __qsort_with_data(base, nelem, map, &ctx); + + return map; +} |