/* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc * Copyright (C) 2009-2012, 2016, 2018 J. Ali Harlow * * 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MSWIN_API #include #endif #include "razor.h" #include "types/types.h" #include "razor-internal.h" #ifndef O_BINARY #define O_BINARY 0 #endif struct razor_set_section_index { const char *name; uint32_t offset; uint32_t flags; }; #define MAIN(type, field) \ { type, offsetof(struct razor_set, field), RAZOR_SECTION_MAIN } #define FILES(type, field) \ { type, offsetof(struct razor_set, field), RAZOR_SECTION_FILES } #define DETAILS(type, field) \ { type, offsetof(struct razor_set, field), RAZOR_SECTION_DETAILS } struct razor_set_section_index razor_sections[] = { MAIN(RAZOR_STRING_POOL, string_pool), MAIN(RAZOR_PACKAGES, packages), MAIN(RAZOR_PROPERTIES, properties), MAIN(RAZOR_PACKAGE_POOL, package_pool), MAIN(RAZOR_PROPERTY_POOL, property_pool), MAIN(RAZOR_PREFIX_POOL, prefix_pool), FILES(RAZOR_FILES, files), FILES(RAZOR_FILE_POOL, file_pool), FILES(RAZOR_FILE_STRING_POOL, file_string_pool), DETAILS(RAZOR_DETAILS_STRING_POOL, details_string_pool) }; RAZOR_EXPORT struct razor_set * razor_set_create_without_root(void) { struct razor_set *set; char *empty; set = zalloc(sizeof *set); if (set) { empty = array_add(&set->string_pool, 1); *empty = '\0'; set->lock = -1; set->ref_count = 1; set->header_version = RAZOR_HEADER_VERSION; set->flags = RAZOR_SET_PRIVATE; } return set; } RAZOR_EXPORT struct razor_set * razor_set_create(void) { struct razor_set *set; struct razor_entry *e; set = razor_set_create_without_root(); e = array_add(&set->files, sizeof *e); e->name = 0; e->flags = RAZOR_ENTRY_LAST; e->start = 0; list_set_empty(&e->packages); return set; } RAZOR_EXPORT uint32_t razor_set_get_header_version(struct razor_set *set) { return set->header_version; } RAZOR_EXPORT int razor_set_set_header_version(struct razor_set *set, uint32_t header_version) { if (header_versionRAZOR_HEADER_VERSION) return -1; else { set->header_version = header_version; return 0; } } struct razor_mapped_file { struct razor_set_header *header; size_t size; struct razor_mapped_file *next; }; RAZOR_EXPORT int razor_set_bind_sections(struct razor_set *set, const char *uri, enum razor_set_flags flags, struct razor_error **error) { struct razor_set_section *s, *sections; struct razor_mapped_file *file; const char *pool, *reason; struct array *array; int i, j, code; file = zalloc(sizeof *file); if (file == NULL) { razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL, "Not enough memory"); return -1; } file->header = razor_uri_get_contents(uri, &file->size, flags & RAZOR_SET_PRIVATE, error); if (!file->header) { free(file); return -1; } if (file->size < sizeof *file->header) { code = RAZOR_GENERAL_ERROR_DATABASE_CORRUPTED; reason = "Premature EOF"; } else if (file->header->magic != RAZOR_MAGIC) { code = RAZOR_GENERAL_ERROR_DATABASE_CORRUPTED; reason = "Bad magic number"; } else if (file->header->version < RAZOR_HEADER_VERSION_MIN || file->header->version > RAZOR_HEADER_VERSION) { code = RAZOR_GENERAL_ERROR_DATABASE_INCOMPATIBLE; reason = "Incompatible file version"; } else reason = NULL; if (reason) { razor_set_error(error, RAZOR_GENERAL_ERROR, code, uri, reason); razor_uri_free_contents(file->header, file->size); free(file); return -1; } set->flags = flags & RAZOR_SET_PRIVATE; set->header_version = file->header->version; if (set->mapped_files == NULL) { for (i = 0; i < ARRAY_SIZE(razor_sections); i++) { array = (void *) set + razor_sections[i].offset; array_release(array); } } file->next = set->mapped_files; set->mapped_files = file; sections = (void *) file->header + sizeof *file->header; pool = (void *) sections + file->header->num_sections * sizeof *sections; for (i = 0; i < file->header->num_sections; i++) { s = sections + i; for (j = 0; j < ARRAY_SIZE(razor_sections); j++) if (!strcmp(razor_sections[j].name, &pool[s->name])) break; if (j == ARRAY_SIZE(razor_sections)) continue; array = (void *) set + razor_sections[j].offset; array->data = (void *) file->header + s->offset; array->size = s->size; array->alloc = s->size; } return 0; } RAZOR_EXPORT struct razor_set * razor_set_open(const char *uri, enum razor_set_flags flags, struct razor_error **error) { struct razor_set *set; set = zalloc(sizeof *set); if (!set) { razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL, "Not enough memory"); return NULL; } set->lock = -1; set->ref_count = 1; if (razor_set_bind_sections(set, uri, flags, error)) { free(set); return NULL; } return set; } struct razor_uri_lock { char *uri; int fd; int exclusive; }; static struct array razor_uri_locks = { 0, }; /* * The underlying locks on MS-Windows and POSIX are different. Posix locks * created with fcntl(F_SETLK) only affect other processes whereas MS-Windows * locks affect the locking process as well. Partly because the latter is * preferable and partly because it makes testing easier if platforms behave * in the same way, we implement platform independent locks which follow the * MS-Windows pattern. * * Note that razor_set_aquire_lock() currently assumes that this is a private * helper function. Should we ever want to make this a library-wide API then * we should probably add a 'void *owner' parameter and add a new error code * (RAZOR_GENERAL_ERROR_LOCALLY_LOCKED?) if a lock is present with a different * owner so that razor_set_aquire_lock() can generate a different error message. */ static int razor_uri_lock(const char *uri, int flags, mode_t mode, int exclusive, struct razor_error **error) { int fd, i; struct razor_uri_lock *ulock; #ifdef MSWIN_API DWORD lflags = LOCKFILE_FAIL_IMMEDIATELY, err; OVERLAPPED lock = {0}; #else struct flock lock = {0}; #endif fd = razor_uri_open(uri, flags, mode, error); if (fd < 0) return -1; for (i = ptr_array_len(&razor_uri_locks) - 1; i >= 0; i--) { ulock = ptr_array_index(&razor_uri_locks, i); if (ulock && !strcmp(ulock->uri, uri)) { if (!ulock->exclusive && !exclusive) break; razor_set_error(error, RAZOR_GENERAL_ERROR, RAZOR_GENERAL_ERROR_ALREADY_LOCKED, uri, "An existing lock is already held on this file"); close(fd); return -1; } } #ifdef MSWIN_API if (exclusive) lflags |= LOCKFILE_EXCLUSIVE_LOCK; if (!LockFileEx((HANDLE)_get_osfhandle(fd), lflags, 0, 1, 0, &lock)) { err = GetLastError(); if (err == ERROR_IO_PENDING) razor_set_error(error, RAZOR_GENERAL_ERROR, RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED, uri, "An existing lock is already held on this file"); else razor_set_error_mswin(error, uri, err); close(fd); return -1; } #else lock.l_type = exclusive ? F_WRLCK : F_RDLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(fd, F_SETLK, &lock) < 0) { if (errno == EAGAIN || errno == EACCES) razor_set_error(error, RAZOR_GENERAL_ERROR, RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED, uri, "An existing lock is already held on this file"); else razor_set_error_posix(error, uri); close(fd); return -1; } #endif ulock = zalloc(sizeof *ulock); ulock->uri = strdup(uri); ulock->fd = fd; ulock->exclusive = exclusive; return ptr_array_add(&razor_uri_locks, ulock); } static void razor_uri_unlock(int id) { struct razor_uri_lock *ulock; #ifndef MSWIN_API struct flock lock = {0}; #endif if (id < 0 || id >= ptr_array_len(&razor_uri_locks)) return; ulock = ptr_array_index(&razor_uri_locks, id); if (!ulock) return; #ifdef MSWIN_API (void)UnlockFile((HANDLE)_get_osfhandle(ulock->fd), 0, 0, 1, 0); #else lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; (void)fcntl(ulock->fd, F_SETLK, &lock); #endif ptr_array_remove_index(&razor_uri_locks, id); free(ulock->uri); close(ulock->fd); free(ulock); } int razor_set_acquire_lock(struct razor_set *set, const char *uri, int exclusive, struct razor_error **error) { int lock; const char *errstr = NULL; assert(set != NULL); if (!uri) { razor_uri_unlock(set->lock); set->lock = -1; return 0; } lock = razor_uri_lock(uri, O_CREAT | O_RDWR | O_TRUNC | O_BINARY, 0666, exclusive, error); if (lock >= 0) { set->lock = lock; return 0; } if (!error) return -1; if (razor_error_matches(*error, RAZOR_GENERAL_ERROR, RAZOR_GENERAL_ERROR_ALREADY_LOCKED)) { /* * This represents a design flaw in the caller. */ if (exclusive) errstr = "Database is in use and cannot be locked"; else errstr = "Database is being updated and cannot be locked"; } else if (razor_error_matches(*error, RAZOR_GENERAL_ERROR, RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED)) { /* * This is probably caused by another application holding * the lock, but under MS-Windows the file could in theory * be locked by this application outside of razor_set. */ if (exclusive) errstr = "Database is in use. Please try again later"; else errstr = "Database is being updated. Please try again later"; } if (errstr) { razor_error_free(*error); razor_set_error(error, RAZOR_GENERAL_ERROR, RAZOR_GENERAL_ERROR_DATABASE_LOCKED, uri, errstr); } return -1; } static void razor_set_destroy(struct razor_set *set) { struct razor_mapped_file *file, *next; struct array *array; int i; assert (set != NULL); if (set->mapped_files == NULL) { for (i = 0; i < ARRAY_SIZE(razor_sections); i++) { array = (void *) set + razor_sections[i].offset; array_release(array); } } else { for (file = set->mapped_files; file != NULL; file = next) { next = file->next; razor_uri_free_contents(file->header, file->size); free(file); } } razor_set_acquire_lock(set, NULL, 0, NULL); free(set); } RAZOR_EXPORT void razor_set_unref(struct razor_set *set) { if (set && !--set->ref_count) razor_set_destroy(set); } RAZOR_EXPORT struct razor_set * razor_set_ref(struct razor_set *set) { if (set) set->ref_count++; return set; } RAZOR_EXPORT void razor_set_write_to_handle(struct razor_set *set, struct razor_atomic *atomic, int handle, uint32_t section_mask) { struct razor_set_header header; struct razor_set_section sections[ARRAY_SIZE(razor_sections)]; struct hashtable table; struct array pool, *arrays[ARRAY_SIZE(razor_sections)]; uint32_t offset; int count, i, j; static const char padding[4]; array_init(&pool); hashtable_init(&table, &pool); j = 0; for (i = 0; i < ARRAY_SIZE(razor_sections); i++) { if ((razor_sections[i].flags & section_mask) == 0) continue; arrays[j] = (void *) set + razor_sections[i].offset; sections[j].name = hashtable_tokenize(&table, razor_sections[i].name); j++; } count = j; header.magic = RAZOR_MAGIC; header.version = set->header_version; header.num_sections = count; offset = sizeof header + count * sizeof *sections + ALIGN(pool.size, 4); for (i = 0; i < count; i++) { sections[i].offset = offset; sections[i].size = arrays[i]->size; offset += ALIGN(arrays[i]->size, 4); } razor_atomic_write(atomic, handle, &header, sizeof header); razor_atomic_write(atomic, handle, sections, count * sizeof *sections); razor_atomic_write(atomic, handle, pool.data, pool.size); razor_atomic_write(atomic, handle, padding, PADDING(pool.size, 4)); for (i = 0; i < count; i++) { razor_atomic_write(atomic, handle, arrays[i]->data, arrays[i]->size); razor_atomic_write(atomic, handle, padding, PADDING(arrays[i]->size, 4)); } array_release(&pool); hashtable_release(&table); } RAZOR_EXPORT int razor_set_write(struct razor_set *set, struct razor_atomic *atomic, const char *uri, uint32_t sections) { int h; h = razor_atomic_create_file(atomic, uri, S_IRWXU | S_IRWXG | S_IRWXO); if (h < 0) return -1; razor_set_write_to_handle(set, atomic, h, sections); return razor_atomic_close(atomic, h); } 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_string(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: if (!set->details_string_pool.size) return ""; pool = set->details_string_pool.data; return &pool[package->summary]; case RAZOR_DETAIL_DESCRIPTION: if (!set->details_string_pool.size) return ""; pool = set->details_string_pool.data; return &pool[package->description]; case RAZOR_DETAIL_URL: if (!set->details_string_pool.size) return ""; pool = set->details_string_pool.data; return &pool[package->url]; case RAZOR_DETAIL_LICENSE: if (!set->details_string_pool.size) return ""; pool = set->details_string_pool.data; return &pool[package->license]; case RAZOR_DETAIL_PREUNPROG: pool = set->string_pool.data; return &pool[package->preun.program]; case RAZOR_DETAIL_PREUN: pool = set->string_pool.data; return &pool[package->preun.body]; case RAZOR_DETAIL_POSTUNPROG: pool = set->string_pool.data; return &pool[package->postun.program]; case RAZOR_DETAIL_POSTUN: pool = set->string_pool.data; return &pool[package->postun.body]; default: fprintf(stderr, "type %u not found\n", type); return NULL; } } static const char *const * razor_package_get_details_array(struct razor_set *set, struct razor_package *package, enum razor_detail_type type) { switch (type) { case RAZOR_DETAIL_PREFIXES: /* We don't track prefixes in packages. Install * prefixes are tracked, and made available via * razor_install_prefix_iterator_create(). */ return NULL; 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 **string; const char *const **array; for (i = 0;; i += 2) { type = va_arg(args, enum razor_detail_type); if (type == RAZOR_DETAIL_LAST) break; if (type == RAZOR_DETAIL_PREFIXES) { array = va_arg(args, const char *const **); *array = razor_package_get_details_array(set, package, type); } else { string = va_arg(args, const char **); *string = razor_package_get_details_string(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_package_remove: * @prev: The %razor_set before the current transaction * @next: The %razor_set after the current transaction is applied * @package: a %razor_package * @root: the root into which the package is currently installed * @install_count: the value to pass to uninstall scripts * @stage: Limit the removal to just the scripts or the files * * Removes an installed package. **/ RAZOR_EXPORT int razor_package_remove(struct razor_set *prev, struct razor_set *next, struct razor_atomic *atomic, struct razor_package *package, const char *root_uri, int install_count, enum razor_stage_type stage) { struct razor_file_iterator *fi; struct razor_package_iterator *pi; struct razor_package *p; char *uri, *relative, *buffer, buf[32]; const char *name, *program, *script; int i, count, retval = 0; struct environment env; struct list *link; const char *prefix; struct razor_error *tmp_error = NULL; if (stage & RAZOR_STAGE_SCRIPTS) { environment_init(&env); link = list_first(&package->install_prefixes, &prev->prefix_pool); for (i = 0; link; i++) { prefix = (const char *)prev->string_pool.data + link->data; sprintf(buf, "RPM_INSTALL_PREFIX%d", i); environment_add_variable(&env, buf, prefix); link = list_next(link); } environment_set(&env); } if (stage & RAZOR_STAGE_SCRIPTS_PRE) { razor_package_get_details(prev, package, RAZOR_DETAIL_PREUNPROG, &program, RAZOR_DETAIL_PREUN, &script, RAZOR_DETAIL_LAST); retval = razor_run_script(root_uri, RAZOR_PROPERTY_PREUN, program, script, install_count, &tmp_error); if (retval < 0) { razor_atomic_propagate_error(atomic, tmp_error, NULL); tmp_error = NULL; } } if (!retval && (stage & RAZOR_STAGE_FILES)) { fi = razor_file_iterator_create(prev, package, 1); while (razor_file_iterator_next(fi, &name)) { pi = razor_package_iterator_create_for_file(next, name); count = 0; while (razor_package_iterator_next(pi, &p, RAZOR_DETAIL_LAST)) count++; razor_package_iterator_destroy(pi); if (count <= 0) { uri = razor_path_to_uri(name); if (str_has_prefix(uri, "file:///")) relative = uri + 8; else if (str_has_prefix(uri, "file:/")) relative = uri + 6; else if (str_has_prefix(uri, "file:")) relative = uri + 5; else if (str_has_prefix(uri, "/")) relative = uri + 1; else relative = uri; buffer = razor_resolve_uri_root(root_uri, relative, 1, &tmp_error); free(uri); if (buffer) { razor_atomic_remove(atomic, buffer); free(buffer); } else { razor_atomic_propagate_error(atomic, tmp_error, NULL); tmp_error = NULL; break; } } } razor_file_iterator_destroy(fi); retval = razor_atomic_in_error_state(atomic); } if (!retval && (stage & RAZOR_STAGE_SCRIPTS_POST)) { razor_package_get_details(prev, package, RAZOR_DETAIL_POSTUNPROG, &program, RAZOR_DETAIL_POSTUN, &script, RAZOR_DETAIL_LAST); retval |= razor_run_script(root_uri, RAZOR_PROPERTY_POSTUN, program, script, install_count, &tmp_error); if (retval < 0) { razor_atomic_propagate_error(atomic, tmp_error, NULL); tmp_error = NULL; } } if (stage & RAZOR_STAGE_SCRIPTS) { environment_unset(&env); environment_release(&env); } return retval; } 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, *subdir; const char *n, *pool = set->file_string_pool.data; int len; assert (set != NULL); assert (pattern != NULL); if (dir == NULL) return NULL; e = dir; do { n = pool + e->name; if (strcmp(pattern, n) == 0) return e; len = strlen(n); if (e->start != 0 && strncmp(pattern, n, len) == 0 && pattern[len] == '/') { subdir = (struct razor_entry *) set->files.data + e->start; return razor_set_find_entry(set, subdir, 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, *subdir; const char *n, *pool = set->file_string_pool.data; e = dir; 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); subdir = (struct razor_entry *) set->files.data + e->start; list_dir(set, subdir, 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 *root, *e; char buffer[512], *p, *base; assert (set != NULL); root = (struct razor_entry *) set->files.data; if (pattern == NULL) { p = set->file_string_pool.data; e = root; do { if (e->start) { strcpy(buffer, p + e->name); list_dir(set, root + e->start, buffer, NULL); } } while (!((e++)->flags & RAZOR_ENTRY_LAST)); return; } strcpy(buffer, pattern); e = razor_set_find_entry(set, root, buffer); if (e && e->start) { base = NULL; } else { p = strrchr(buffer, '/'); if (p) { *p = '\0'; base = p + 1; } else { base = NULL; } } e = razor_set_find_entry(set, root, buffer); if (e && e->start) list_dir(set, root + e->start, buffer, base); } RAZOR_EXPORT void razor_set_list_package_files(struct razor_set *set, struct razor_package *package) { struct razor_file_iterator *fi; const char *name; assert (set != NULL); assert (package != NULL); fi = razor_file_iterator_create(set, package, 0); while (razor_file_iterator_next(fi, &name)) printf("%s\n", name); razor_file_iterator_destroy(fi); } /* * Package data can potentially come from two places. The so-called * metadata (eg., from comps.xml) and from an RPM file. We consider * a package which has additional data from an RPM file as "fixed". * If a package needs fixing, then razor_transaction_fixup_package() * will do so. When considering what packages to add and to remove * we need to take this into account since we always want to add * unfixed packages (otherwise we have a potential conflict between * the existing package data and that present in the RPM). */ static int razor_package_is_fixed(struct razor_set *set, struct razor_package *p) { const char *preunprog, *preun, *postunprog, *postun; if (!p) return 0; razor_package_get_details(set, p, RAZOR_DETAIL_PREUNPROG, &preunprog, RAZOR_DETAIL_PREUN, &preun, RAZOR_DETAIL_POSTUNPROG, &postunprog, RAZOR_DETAIL_POSTUN, &postun, RAZOR_DETAIL_LAST); return *preunprog || *preun || *postunprog || *postun; } 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, is_fixed1, is_fixed2; 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); is_fixed1 = razor_package_is_fixed(set, p1); razor_package_iterator_next(pi2, &p2, RAZOR_DETAIL_NAME, &name2, RAZOR_DETAIL_VERSION, &version2, RAZOR_DETAIL_ARCH, &arch2, RAZOR_DETAIL_LAST); is_fixed2 = razor_package_is_fixed(upstream, p2); while (p1 || p2) { if (p1 && p2) { res = strcmp(name1, name2); if (res == 0) res = razor_versioncmp(version1, version2); if (res == 0) res = is_fixed1 - is_fixed2; } 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); is_fixed1 = razor_package_is_fixed(set, p1); } 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); is_fixed2 = razor_package_is_fixed(upstream, p2); } } 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 deque *order, *left; }; 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; } } /* * Does have a requirement for