/* * Copyright (C) 2008 Kristian Høgsberg * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "razor-internal.h" #include "razor.h" void * zalloc(size_t size) { void *p; p = malloc(size); memset(p, 0, size); return p; } struct razor_set_section_index { const char *name; uint32_t offset; }; struct razor_set_section_index 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_index 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_index razor_details_sections[] = { { RAZOR_DETAILS_STRING_POOL, offsetof(struct razor_set, details_string_pool) }, }; RAZOR_EXPORT 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; } static int razor_set_bind_sections(struct razor_set *set, struct razor_set_header **header, size_t *header_size, struct razor_set_section_index section_index[], int section_index_size, const char *filename) { struct razor_set_section *s, *sections; struct array *array; const char *pool; int i; *header = razor_file_get_contents(filename, header_size); if (!*header) return -1; sections = (void *) *header + sizeof **header; pool = (void *) sections + (*header)->num_sections * sizeof *sections; for (i = 0; i < (*header)->num_sections; i++) { int j; s = sections + i; for (j = 0; j < section_index_size; j++) if (!strcmp(section_index[j].name, &pool[s->name])) break; if (j == section_index_size) continue; array = (void *) set + section_index[j].offset; array->data = (void *) *header + s->offset; array->size = s->size; array->alloc = s->size; } return 0; } RAZOR_EXPORT struct razor_set * razor_set_open(const char *filename) { struct razor_set *set; set = zalloc(sizeof *set); if (razor_set_bind_sections(set, &set->header, &set->header_size, razor_sections, ARRAY_SIZE(razor_sections), filename)){ free(set); return NULL; } return set; } RAZOR_EXPORT int razor_set_open_details(struct razor_set *set, const char *filename) { return razor_set_bind_sections(set, &set->details_header, &set->details_header_size, razor_details_sections, ARRAY_SIZE(razor_details_sections), filename); } RAZOR_EXPORT int razor_set_open_files(struct razor_set *set, const char *filename) { return razor_set_bind_sections(set, &set->files_header, &set->files_header_size, razor_files_sections, ARRAY_SIZE(razor_files_sections), filename); } RAZOR_EXPORT void razor_set_destroy(struct razor_set *set) { struct array *a; int i; assert (set != NULL); if (set->header) { razor_file_free_contents(set->header, 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) { razor_file_free_contents(set->details_header, 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) { razor_file_free_contents(set->files_header, 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, struct razor_set_section_index *sections, size_t array_size) { struct razor_set_header header; struct razor_set_section *out_sections = malloc(array_size * sizeof *out_sections); struct hashtable table; struct array *a, pool; uint32_t offset; int i; header.magic = RAZOR_MAGIC; header.version = RAZOR_VERSION; header.num_sections = array_size; offset = sizeof header + array_size * sizeof *out_sections; array_init(&pool); hashtable_init(&table, &pool); for (i = 0; i < array_size; i++) out_sections[i].name = hashtable_tokenize(&table, sections[i].name); offset += pool.size; for (i = 0; i < array_size; i++) { a = (void *) set + sections[i].offset; out_sections[i].offset = offset; out_sections[i].size = a->size; offset += a->size; } razor_write(fd, &header, sizeof header); razor_write(fd, out_sections, array_size * sizeof *out_sections); razor_write(fd, pool.data, pool.size); for (i = 0; i < array_size; i++) { a = (void *) set + sections[i].offset; razor_write(fd, a->data, a->size); } free(out_sections); return 0; } RAZOR_EXPORT 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_sections, ARRAY_SIZE(razor_sections)); case RAZOR_REPO_FILE_DETAILS: return razor_set_write_sections_to_fd(set, fd, razor_details_sections, ARRAY_SIZE(razor_details_sections)); case RAZOR_REPO_FILE_FILES: return razor_set_write_sections_to_fd(set, fd, razor_files_sections, ARRAY_SIZE(razor_files_sections)); default: return -1; } } RAZOR_EXPORT 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); } RAZOR_EXPORT 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); } RAZOR_EXPORT int razor_versioncmp(const char *s1, const char *s2) { const char *p1, *p2; long n1, n2; int res; assert (s1 != NULL); assert (s2 != NULL); 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; } static const char * razor_package_get_details_type(struct razor_set *set, struct razor_package *package, enum razor_detail_type type) { const char *pool; switch (type) { case RAZOR_DETAIL_NAME: pool = set->string_pool.data; return &pool[package->name]; case RAZOR_DETAIL_VERSION: pool = set->string_pool.data; return &pool[package->version]; case RAZOR_DETAIL_ARCH: pool = set->string_pool.data; return &pool[package->arch]; case RAZOR_DETAIL_SUMMARY: pool = set->details_string_pool.data; return &pool[package->summary]; case RAZOR_DETAIL_DESCRIPTION: pool = set->details_string_pool.data; return &pool[package->description]; case RAZOR_DETAIL_URL: pool = set->details_string_pool.data; return &pool[package->url]; case RAZOR_DETAIL_LICENSE: pool = set->details_string_pool.data; return &pool[package->license]; default: fprintf(stderr, "type %u not found\n", type); return NULL; } } /** * razor_package_get_details_varg: * @set: a %razor_set * @package: a %razor_package * @args: a va_list of arguments to set **/ void razor_package_get_details_varg(struct razor_set *set, struct razor_package *package, va_list args) { int i; enum razor_detail_type type; const char **data; for (i = 0;; i += 2) { type = va_arg(args, enum razor_detail_type); if (type == RAZOR_DETAIL_LAST) break; data = va_arg(args, const char **); *data = razor_package_get_details_type(set, package, type); } } /** * razor_package_get_details: * @set: a %razor_set * @package: a %razor_package * * Gets details about a package using a varg interface * The vararg must be terminated with %RAZOR_DETAIL_LAST. * * Example: razor_package_get_details (set, package, * RAZOR_DETAIL_URL, &url, * RAZOR_DETAIL_LAST); **/ RAZOR_EXPORT void razor_package_get_details(struct razor_set *set, struct razor_package *package, ...) { va_list args; assert (set != NULL); assert (package != NULL); va_start(args, NULL); razor_package_get_details_varg (set, package, args); va_end (args); } RAZOR_EXPORT const char * razor_property_relation_to_string(struct razor_property *p) { assert (p != NULL); 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 "?"; } } RAZOR_EXPORT const char * razor_property_type_to_string(struct razor_property *p) { assert (p != NULL); 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; } } RAZOR_EXPORT 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->file_string_pool.data; int len; assert (set != NULL); assert (dir != NULL); assert (pattern != NULL); 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->file_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)); } RAZOR_EXPORT void razor_set_list_files(struct razor_set *set, const char *pattern) { struct razor_entry *e; char buffer[512], *p, *base; assert (set != NULL); 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 && 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->file_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; } RAZOR_EXPORT void razor_set_list_package_files(struct razor_set *set, struct razor_package *package) { struct list *r; uint32_t end; char buffer[512]; assert (set != NULL); assert (package != NULL); 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. **/ RAZOR_EXPORT void razor_set_diff(struct razor_set *set, struct razor_set *upstream, razor_diff_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; assert (set != NULL); assert (upstream != NULL); pi1 = razor_package_iterator_create(set); pi2 = razor_package_iterator_create(upstream); razor_package_iterator_next(pi1, &p1, RAZOR_DETAIL_NAME, &name1, RAZOR_DETAIL_VERSION, &version1, RAZOR_DETAIL_ARCH, &arch1, RAZOR_DETAIL_LAST); razor_package_iterator_next(pi2, &p2, RAZOR_DETAIL_NAME, &name2, RAZOR_DETAIL_VERSION, &version2, RAZOR_DETAIL_ARCH, &arch2, RAZOR_DETAIL_LAST); 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(RAZOR_DIFF_ACTION_REMOVE, p1, name1, version1, arch1, data); else if (p1 == NULL || res > 0) callback(RAZOR_DIFF_ACTION_ADD, p2, name2, version2, arch2, data); if (p1 != NULL && res <= 0) razor_package_iterator_next(pi1, &p1, RAZOR_DETAIL_NAME, &name1, RAZOR_DETAIL_VERSION, &version1, RAZOR_DETAIL_ARCH, &arch1, RAZOR_DETAIL_LAST); if (p2 != NULL && res >= 0) razor_package_iterator_next(pi2, &p2, RAZOR_DETAIL_NAME, &name2, RAZOR_DETAIL_VERSION, &version2, RAZOR_DETAIL_ARCH, &arch2, RAZOR_DETAIL_LAST); } razor_package_iterator_destroy(pi1); razor_package_iterator_destroy(pi2); } struct install_action { enum razor_install_action action; struct razor_package *package; }; struct razor_install_iterator { struct razor_set *set; struct razor_set *next; struct array actions; struct install_action *a, *end; }; static void add_action(enum razor_diff_action action, struct razor_package *package, const char *name, const char *version, const char *arch, void *data) { struct razor_install_iterator *ii = data; struct install_action *a; a = array_add(&ii->actions, sizeof *a); a->package = package; switch (action) { case RAZOR_DIFF_ACTION_ADD: a->action = RAZOR_INSTALL_ACTION_ADD; break; case RAZOR_DIFF_ACTION_REMOVE: a->action = RAZOR_INSTALL_ACTION_REMOVE; break; } } RAZOR_EXPORT struct razor_install_iterator * razor_set_create_install_iterator(struct razor_set *set, struct razor_set *next) { struct razor_install_iterator *ii; assert (set != NULL); assert (next != NULL); ii = zalloc(sizeof *ii); ii->set = set; ii->next = next; razor_set_diff(set, next, add_action, ii); ii->a = ii->actions.data; ii->end = ii->actions.data + ii->actions.size; /* FIXME: We need to figure out the right install order here, * so the post and pre scripts can run. */ return ii; } RAZOR_EXPORT int razor_install_iterator_next(struct razor_install_iterator *ii, struct razor_set **set, struct razor_package **package, enum razor_install_action *action, int *count) { if (ii->a == ii->end) return 0; switch (ii->a->action) { case RAZOR_INSTALL_ACTION_ADD: *set = ii->next; break; case RAZOR_INSTALL_ACTION_REMOVE: *set = ii->set; break; } *package = ii->a->package; *action = ii->a->action; *count = 0; ii->a++; return 1; } RAZOR_EXPORT void razor_install_iterator_destroy(struct razor_install_iterator *ii) { array_release(&ii->actions); free(ii); }