# HG changeset patch # User Richard Hughes # Date 1213645230 14400 # Node ID c3eb520e2219c2cf7ded6e8c48984ee48086da67 # Parent edd5fe0a00bab1e7a76fe6367917a06d90f357f6 Autoconfify razor. committer: Kristian H?gsberg diff -r edd5fe0a00ba -r c3eb520e2219 .gitignore --- a/.gitignore Sun Jun 15 23:15:59 2008 -0400 +++ b/.gitignore Mon Jun 16 15:40:30 2008 -0400 @@ -1,10 +1,32 @@ -.gitignore +aclocal.m4 +autom4te.cache +compile +config.guess +config.h +config.h.in +config.log +config.status +config.sub +configure +depcomp +INSTALL +install-sh +intltool-extract +intltool-extract.in +intltool-merge +intltool-merge.in +intltool-update +intltool-update.in +libtool +ltmain.sh +Makefile +Makefile.in +missing +mkinstalldirs +py-compile +stamp-h1 *.o +ChangeLog +*.tar.gz *~ -*.repo -razor -test-driver -pkgs -set -primary.xml.gz -filelists.xml.gz + diff -r edd5fe0a00ba -r c3eb520e2219 AUTHORS --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AUTHORS Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,2 @@ +Kristian Høgsberg + diff -r edd5fe0a00ba -r c3eb520e2219 DEPSOLVE.txt --- a/DEPSOLVE.txt Sun Jun 15 23:15:59 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,364 +0,0 @@ -YUM vs RAZOR ------------- - -At a very high level, yum's depsolver does something roughly -equivalent to: - - - For each package being installed or removed - - - For each relevant property (provides, requires, conflicts, - obsoletes): - - - Figure out what additional packages need to be added to - or removed from the system to satisfy this property - -which ends up being roughly O(N^2 * M) where N is the total number of -properties and M is the number of packages being acted on. - -(I just figured that out off the top of my head, and I'm not totally -familiar with the yum code, so it may be wrong.) - -Razor's depsolver is something like: - - - do { - - - For each property to be added to or removed from the system: - - - Figure out what packages need to be added to or removed - from the system to satisfy this property - - - } until we stop adding/remove more packages - -with the key being that it's very easy to find the PROVIDES -corresponding to a REQUIRES and vice versa, because the property -arrays are sorted, and so all properties with the same "name" will be -adjacent to one another in the array, allowing many dependencies to be -satisified in essentially constant time. (Actually... we've been -calling it constant, but it's really O(log N) for heavily-depended-on -packages, because the more packages you have, the more variations on -"requires foo", "requires foo = 1.1", "requires foo > 1.0", etc you're -going to have to scan through.) - -Ideally though, each iteration of the inner loop body happens in -constant time, and thus the inner loop as a whole is O(N), and thus -the depsolver as a whole is O(N * M) (or at least, less than -O(N * M * log N). - - -FILE DEPENDENCIES ------------------ - -Whenever we add a package with a file REQUIRES to a razor_set, we also -add a PROVIDES for that file to the package or packages which provide -that file. This means that if we later add another package that -requires the same file (eg, /bin/sh or /usr/bin/perl), we can resolve -its file requirement exactly like we would resolve a property -requirement, in nearly constant time. - -When adding a *new* file requirement (ie, a requirement on a file that -no existing package depends on), we still have to scan through the -file tree, which is O(log N) in the number of files. - -(AFAICT, there's no reason yum couldn't do the same optimization. -Also, AFAICT, yum currently sticks property dependencies and file -dependencies into the same hash table, so that if any package in the -transaction has a file dependency, it causes *property* dependencies -to become slower to resolve as well...) - - -THE RULES ---------- - -This is what we have figured out for transaction-solving rules; -neither yum nor rpm's algorithm seems to be explained in full -anywhere... - - 1. Every requested install in the initial package set must be - satisfied as either a new install or an update: - - - if the requested package name is the name of an upstream - package: - - - if there is not a corresponding already-installed - package, then install the upstream package - - - else if the upstream package is newer than the - already-installed package, then update the package - - - else it's an error (UP_TO_DATE) - - - else if the requested package name is the name of an - already-installed package: - - - if there is an upstream package that obsoletes the - already-installed package, then behave as though the - user had requested that that package be installed - instead. - - - else it's an error (UP_TO_DATE or INSTALL_UNAVAILABLE?) - - - else it's an error (INSTALL_UNAVAILABLE) - - 2. Every requested removal in the initial package set must be - satisfied as a removal. If any requested package name is not - the name of an installed package, it's an error - (REMOVE_NOT_INSTALLED) - - REQUIRES processing: - - 3. If a package being installed or updated-to REQUIRES a property - that is not provided by any installed or to-be-installed - package, we need to find an installable package that provides - that property. If we find one, install/update it. If not, it's - an error (UNSATISFIABLE). (If we find an upstream package - providing the property that corresponds to a system package - that's being removed, then it's a CONTRADICTION.) - - 4. If an already-installed package REQUIRES a property which is - only provided by a package that is being removed, then that - package needs to be removed as well. - - 5. If an already-installed package REQUIRES a property which is - only provided by a package that is being upgraded or obsoleted - (to a new package which does not provide that property), then: - - - if there is an update for the installed package, then update - the installed package - - - else if there is another installable package that provides - the required property, then install that. - - - else it's an error (UNSATISFIABLE?) - - CONFLICTS processing - - 6. If a package being installed or updated-to CONFLICTS with a - property provided by an installed package: - - - if there is an update for the installed package, which the - new package does not conflict with, then update the - installed package. - - - else it's an error (NEW_CONFLICT) - - 7. If an already-installed package CONFLICTS with a property - provided by a to-be-installed package: - - - if there is an update for the installed package, which does - not conflict with the new package, then update the installed - package. - - - else it's an error (NEW_CONFLICT) - - 8. If a package being installed or updated-to CONFLICTS with a - property provided by a to-be-installed package, then it's an - error (CONTRADICTION). - - OBSOLETES processing. NOTE: OBSOLETES are only matched against - package names, not against arbitrary provided properties - - 9. If a package being installed or updated-to OBSOLETES an - installed package, then obsolete that package. (ie, remove it, - but treat it as updated for purposes of dangling REQUIRES). - - 10. If an already-installed package OBSOLETES a to-be-installed - package, then it's an error. (ALREADY_OBSOLETE) - - 11. If a package being installed or updated-to OBSOLETES another - package being installed or updated-to, then it's an error - (CONTRADICTION). - - - -THE DEPSOLVER -------------- - -We start with two razor_sets, system and upstream, and a list of -requested installations and removals. - - FIXME: what about multiple upstream repos? Having to deal with - arbitrary numbers of razor_sets is possible, but will probably be - messy... It might be easier to either store all upstream repo data - in a single .repo file, or else merge all upstream .repo files - together into a single razor_set at startup. (Or some combination - of those.) - -We create a bit array of the packages in each set, indicating which -ones are installed; the system bitarray starts out all 1s, and the -upstream bitarray all 0s. Each bit is only allowed to change state -once during the transaction; an installed package can be removed, or -an uninstalled package installed, but trying to reinstall a removed -package, or uninstall a newly-installed package is an error. This -means the packages break down into four categories: - - - installed (1 bit in the system bit array) - - to-be-removed (0 bit in the system bit array) - - to-be-installed (1 bit in the upstream bit array) - - installable (0 bit in the upstream bit array) - - -Depsolver algorithm: - - - Create new razor_transaction_packages ("rtp"s) for each - requested install or remove. These will be "unresolved", because - we haven't yet found the razor_packages that correspond to them. - - - while there are new rtps: - - - sort the new rtps - - - Walk the system property list, upstream property list, and - new rtp list in parallel, and: - - - For each uninstalled PROVIDES: - - - If the property is a valid package name (that is, - either it's a package providing its own name, or it - has a matching OBSOLETES), and it matches the name - of a new rtp of type INSTALL or FORCED_UPDATE with - an unresolved new_package: - - - If the upstream package has the same version as - the system package, we have an UP_TO_DATE error - (FIXME: not quite right. This doesn't deal with - the case where we try to update an application - because of a library update, and it turns out - there's no new version of the application, but - there IS a compat package containing the old - version of the library.) - - - Otherwise, set the rtp's new_package to point to - the package providing this property and set the - appropriate bit in the upstream bit array. - - - For each to-be-installed non-file REQUIRES: - - - See if there's an installed or to-be-installed - package that PROVIDES that property. - - - If not, see if there's an installable package that - PROVIDES that property, and create a new INSTALL rtp - for it if so. - - - If not, see if there's a to-be-removed package that - PROVIDES that property. (If we find such a package, - we have a CONTRADICTION error.) - - - If none of the above, then we have an UNSATISFIABLE - error - - - For each to-be-installed file REQUIRES: - - - (We create fake file PROVIDES to match file REQUIRES - when importing/merging razor sets, so if there is - already another installed package that REQUIRES this - file, there will be a PROVIDES listed for it as well.) - - - See if there's an installed package that PROVIDES - that file. - - - If not, do a binary search of the system file tree - looking to see if some installed package provides - that file but does not have a PROVIDES for it. - - - If not, see if there's an installable package that - PROVIDES that property, and create a new INSTALL rtp - for it if so. - - - (If we actually work with multiple upstream - razor_sets, then we will need to search the upstream - file trees at this point, because it's possible that - a package in one upstream repo would require a file - in another upstream repo. But if we merge the - multiple upstream repos into a single razor_set at - some point, then we would not need to do that, - because it would be guaranteed that we would have - already created a fake PROVIDES if any package - provides the file.) - - - If no installed or installable package provides the - file, see if there's a to-be-removed package that - provides the file. (If we find such a package, we - have a CONTRADICTION error.) - - - If none of the above, then we have an UNSATISFIABLE - error - - - For each to-be-installed PROVIDES: - - - Check if the new PROVIDES conflicts with an - installed CONFLICTS. If so, create a new - FORCED_UPDATE rtp for the installed package, so we - can try to upgrade it to a non-conflicting version. - (If we can't, we'll have an OLD_CONFLICT error.) - - - Check if the new PROVIDES conflicts with an - installed OBSOLETES *and* the PROVIDES property - corresponds to the name of its package. (That is, - OBSOLETES are only matched against package names, - not arbitrary provided properties.) If so, we have - an ALREADY_OBSOLETE error. - - - Check if the new PROVIDES conflicts with a - to-be-installed CONFLICTS. If so, we have a - CONTRADICTION error. - - - For each to-be-installed CONFLICTS: - - - Basically the reverse of the previous case: check if - the new CONFLICTS conflicts with an installed - PROVIDES. If so, create a new FORCED_UPDATE rtp for - the installed package, so we can try to upgrade it - to a non-conflicting version. (If we can't, we'll - have an NEW_CONFLICT error.) - - - Check if the new CONFLICTS conflicts with a - to-be-installed PROVIDES. If so, we have a - CONTRADICTION error. - - - For each to-be-installed OBSOLETES: - - - Check if there's an installed package that PROVIDES - that property. If so, create an OBSOLETED rtp for - the installed package. - - - If not, check if there's a to-be-installed package - that PROVIDES that property. If so, we have a - CONTRADICTION error. - - - - For each installed PROVIDES: - - - If the property is a valid package name (that is, - it's a package providing its own name), and it - matches the name of a new rtp with an unresolved - old_package, then set the rtp's old_package to point - to the package providing this property and clear the - appropriate bit in the system bit array. - - - For each to-be-removed PROVIDES: - - - If there's also an identical to-be-installed - PROVIDES, we're ok and can skip this - - - Otherwise, for each installed REQUIRES of this - property: - - - Look for some other installed or to-be-installed - property that satisfies the REQUIRES. - - - If there isn't one, then for each installed - package in this REQUIRES's package list: - - - If the PROVIDES was lost because the old - package was REMOVEd (not FORCED_UPDATE or - OBSOLETED), then create a new REMOVE rtp for - this package. - - - Otherwise, create a new FORCED_UPDATE rtp - for this package. - - - (We don't need to look at to-be-installed REQUIRES - of this property, because if there are any, they - will cause a CONTRADICTION error when we try to - re-satisfy them the next time through.) diff -r edd5fe0a00ba -r c3eb520e2219 Makefile --- a/Makefile Sun Jun 15 23:15:59 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -CFLAGS = -Wall -Wstrict-prototypes -Wmissing-prototypes -g -LDLIBS = librazor.a -lexpat -lz -g -lrpm -lcurl - -all : razor test-driver rpm-razor - -librazor_objs = razor.o yum.o rpm.o types.o util.o razor-root.o -librazor.a : $(librazor_objs) - ar cr $@ $(librazor_objs) - -razor : main.o librazor.a - -*.o : razor.h razor-internal.h -razor.o : types.h - -test-driver : librazor.a test-driver.o - -rpm-razor : librazor.a rpm-razor.o - -test : test-driver - ./test-driver test.xml - -reset : ./razor - sudo rm -rf install - ./razor init - -clean : - rm -f *.o razor librazor.a diff -r edd5fe0a00ba -r c3eb520e2219 Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile.am Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,38 @@ +## Process this file with automake to produce Makefile.in + +SUBDIRS = data docs librazor src po + +# Creating ChangeLog from git log (taken from cairo/Makefile.am): +ChangeLog: $(srcdir)/ChangeLog + +$(srcdir)/ChangeLog: + @if test -d "$(srcdir)/.git"; then \ + (cd "$(srcdir)" && \ + ./missing --run git-log --stat) | fmt --split-only > $@.tmp \ + && mv -f $@.tmp $@ \ + || ($(RM) $@.tmp; \ + echo Failed to generate ChangeLog, your ChangeLog may be outdated >&2; \ + (test -f $@ || echo git-log is required to generate this file >> $@)); \ + else \ + test -f $@ || \ + (echo A git checkout and git-log is required to generate ChangeLog >&2 && \ + echo A git checkout and git-log is required to generate this file >> $@); \ + fi + +.PHONY: ChangeLog $(srcdir)/ChangeLog + +EXTRA_DIST = \ + TODO \ + ChangeLog \ + intltool-extract.in \ + intltool-merge.in \ + intltool-update.in + +DISTCLEANFILES = \ + intltool-extract \ + intltool-merge \ + intltool-update + +clean-local : + rm -f *~ + diff -r edd5fe0a00ba -r c3eb520e2219 REPO.txt --- a/REPO.txt Sun Jun 15 23:15:59 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ -The repo file format / razor_set data structure ------------------------------------------------ - -The repo starts with a header, containing some number of sections, -terminated by a section with type 0: - - struct razor_set_header { - uint32_t magic; - uint32_t version; - struct razor_set_section sections[0]; - }; - - struct razor_set_section { - uint32_t type; - uint32_t offset; - uint32_t size; - }; - -razor_set_open() mmaps the repo file, and creates a struct razor_set: - - 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 razor_set_header *header; - }; - -by finding the sections with those IDs and creating "struct array"s -pointing to the right places in the mmapped data. (This is the only -processing needed when reading in the file; everything else is used -exactly as-is.) - - -The sections ------------- - -RAZOR_STRING_POOL - - Stores one copy of each string that appears in the repo. (At - the moment, this is: package names, package versions, property - names, property versions, and (basenames of) filenames.) The - strings are arbitrarily-sized, 0-terminated, and not in any - particular order (although the empty string always ends up - being at offset 0). - -RAZOR_PACKAGES - - Array of struct razor_package; one for each package in the - set, sorted by name. - -RAZOR_PROPERTIES - - Array of struct razor_property; one for each unique property - in the set, sorted by type, then name, then relation type (eg, - "<" or ">="), then version. (Properties with no version have - relation type RAZOR_VERSION_EQUAL, and version "".) - -RAZOR_FILES - - Array of struct razor_entry; one for each file owned by any - package in the set. The current sort order (which is subject - to change) is breadth-first, sorted by basename. So eg: /, /bin, - /dev, /etc, /bin/false, /bin/true, /dev/null, /etc/passwd. - -RAZOR_PACKAGE_POOL - - Array of struct list, with each list item containing the index - of a struct razor_package in the packages section. See the - discussion of lists below. - -RAZOR_PROPERTY_POOL - - Array of struct list, with each list item containing the index - of a struct razor_property in the properties section. See the - discussion of lists below. - -RAZOR_FILE_POOL - - Array of struct list, with each list item containing the index - of a struct razor_entry in the files section. See the - discussion of lists below. - - -Data types ----------- -Note that the exact layout of bits involves some historical accidents. -(Particularly the fact that the "name" field in most structs loses its -high bits to a flags field.) - -struct list_head - uint list_ptr : 24; - uint flags : 8; - -struct list - uint data : 24; - uint flags : 8; - - Used to store lists of package, property, or file IDs. "struct - list_head" stores the head of the list, which points to one or - more "struct list"s in the appropriate "pool" section. - ("struct list" should probably be called "struct list_item".) - - "list_first(&head, &pool)" returns a "struct list *" pointing - to the first element of the list (or NULL for an empty list), - and "list_next(list)" will return successive elements, until - NULL is returned. Each "list->data" contains the index of a - package, property, or file in the corresponding section of the - set. - - Peeking underneath the abstraction, a list_head's "flags" is - 0xff if the list is empty, 0x80 if it contains a single - element, or 0x00 if it contains more than one element. In the - single-element case, that element is actually stored in the - list_head directly rather than being stored in a pool (and so - list_first() just casts the list_head* to a list* and returns - it). For multi-element lists, list_ptr is the index in the - pool of the first element of this list; the list continues - through successive elements of the pool until one with - non-zero flags is reached, indicating the end of the list. - -struct razor_package - uint name : 24; - uint flags : 8; - uint version : 32; - struct list_head properties; - struct list_head files; - - name and version are indexes into string_pool. properties is a - list of all of the package's properties, and files is a list - of its files. flags is currently only used during razor_set - merging, to keep track of which set a package came from. - -struct razor_property - uint name : 24; - uint flags : 6; - uint type : 2; - uint relation : 32; - uint version : 32; - struct list_head packages; - - name and version are indexes into string_pool. type is an enum - razor_property_type (eg, RAZOR_PROPERTY_REQUIRES), and - relation is an enum razor_version_relation (eg, - RAZOR_VERSION_GREATER_OR_EQUAL). packages is a list of the - packages that have this property. flags is currently unused. - -struct razor_entry - uint name : 24; - uint flags : 8; - uint start : 32; - struct list_head packages; - - name is an index into string_pool, giving the basename of the - file. start is either 0, or an index pointing to another - razor_entry that is the first child of this entry (for a - non-empty directory). (Entry 0 is always the root of the tree, - so no entry could have entry 0 as a child.) flags is 0x80 - (RAZOR_ENTRY_LAST) if an entry is the last entry in its - directory. Otherwise it is 0. - - Note that given a pointer to a struct_razor_entry (eg, from a - package's "files" list), there is no way to reconstruct its - full name without walking the entire files array up to that - point. Because of this and other problems (fix_file_map()), it - seems like razor_entry should be modified to include a pointer - to its parent. (Storing full paths instead of just basenames - would also fix this problem, but that would use much more - memory.) diff -r edd5fe0a00ba -r c3eb520e2219 autogen.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/autogen.sh Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,95 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +DIE=0 + +(test -f $srcdir/configure.ac) || { + echo -n "**Error**: Directory $srcdir does not look like the" + echo " top-level package directory" + exit 1 +} + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have autoconf installed." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +(grep "^AM_PROG_LIBTOOL" $srcdir/configure.ac >/dev/null) && { + (libtool --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have libtool installed." + echo "You can get it from: ftp://ftp.gnu.org/pub/gnu/" + DIE=1 + } +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have automake installed." + echo "You can get it from: ftp://ftp.gnu.org/pub/gnu/" + DIE=1 + NO_AUTOMAKE=yes +} + + +# if no automake, don't bother testing for aclocal +test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing aclocal. The version of automake" + echo "installed doesn't appear recent enough." + echo "You can get automake from ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if test -z "$*"; then + echo "**Warning**: I am going to run configure with no arguments." + echo "If you wish to pass any to it, please specify them on the" + echo $0 " command line." + echo +fi + +case $CC in +xlc ) + am_opt=--include-deps;; +esac + + aclocalinclude="$ACLOCAL_FLAGS" + + if grep "^AM_PROG_LIBTOOL" configure.ac >/dev/null; then + if test -z "$NO_LIBTOOLIZE" ; then + echo "Running libtoolize..." + libtoolize --force --copy + fi + fi + echo "Running aclocal $aclocalinclude ..." + aclocal $aclocalinclude + if grep "^AM_CONFIG_HEADER" configure.ac >/dev/null; then + echo "Running autoheader..." + autoheader + fi + echo "Running automake --gnu -Wno-portability $am_opt ..." + automake --add-missing --gnu -Wno-portability $am_opt + echo "Running autoconf ..." + autoconf + +intltoolize --copy --force --automake || exit 1 + +conf_flags="--enable-maintainer-mode" + +if test x$NOCONFIGURE = x; then + echo "Running $srcdir/configure $conf_flags $@ ..." + $srcdir/configure $conf_flags "$@" \ + && echo "Now type make to compile." || exit 1 +else + echo "Skipping configure process." +fi diff -r edd5fe0a00ba -r c3eb520e2219 bash-completion.sh --- a/bash-completion.sh Sun Jun 15 23:15:59 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -__razor_commands () { - local IFS=$'\n' - COMPREPLY=($(IFS=: compgen -S' ' -W "list-requires:list-provides:list-files:list-file-packages:list-package-files:what-requires:what-provides:import-yum:import-rpmdb:validate:update:diff:install:init:download" -- $1)) -} - -__razor_packages () { - local IFS=$'\n' - - COMPREPLY=($(./razor list --only-names "$1*" | while read p; do echo "$p "; done)) -} - -__razor_upstream_packages () { - local IFS=$'\n' - - COMPREPLY=($(RAZOR_REPO=rawhide.repo ./razor list --only-names "$1*" | while read p; do echo "$p "; done)) -} - -__razor_files() { - COMPREPLY=($(./razor list-files "$1*")) -} - -__razor_requires() { - COMPREPLY=($(compgen -W "$(./razor list-requires)" -- $1)) -} - -__razor_provides() { - COMPREPLY=($(compgen -W "$(./razor list-provides)" -- $1)) -} - -__razor() { - local cur="${COMP_WORDS[COMP_CWORD]}" - - if [ $COMP_CWORD = 1 ]; then - __razor_commands $cur - else - case "${COMP_WORDS[1]}" in - list-requires|list-provides|list-package-files) - __razor_packages $cur ;; - list-files|list-file-packages) __razor_files $cur ;; - what-requires) __razor_requires $cur ;; - what-provides) __razor_provides $cur ;; - install|download) __razor_upstream_packages $cur ;; - esac - fi -} - -complete -o nospace -F __razor razor diff -r edd5fe0a00ba -r c3eb520e2219 configure.ac --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/configure.ac Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,201 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.59c) +AC_INIT(razor, 0.8, krh@redhat.com) +AM_INIT_AUTOMAKE(razor, 0.1) +AM_CONFIG_HEADER(config.h) +AM_MAINTAINER_MODE + +# libtool versioning - this applies to all libraries in this package +# +# See http://sources.redhat.com/autobook/autobook/autobook_91.html#SEC91 for details +# +LT_CURRENT=1 +LT_REVISION=0 +LT_AGE=0 +AC_SUBST(LT_CURRENT) +AC_SUBST(LT_REVISION) +AC_SUBST(LT_AGE) + +AC_ISC_POSIX +AC_PROG_CC +AM_PROG_CC_STDC +AC_HEADER_STDC +AM_PROG_LIBTOOL +AC_PROG_MAKE_SET +AC_PROG_LN_S +AC_SYS_LARGEFILE +AM_PROG_CC_C_O + +# Taken from dbus +AC_ARG_ENABLE(ansi, [ --enable-ansi enable -ansi -pedantic gcc flags],enable_ansi=$enableval,enable_ansi=no) +AC_ARG_ENABLE(verbose-mode, [ --enable-verbose-mode support verbose debug mode],enable_verbose_mode=$enableval,enable_verbose_mode=$USE_MAINTAINER_MODE) + +if test "${enable_verbose_mode}" != no; then + # To get -rdynamic you pass -export-dynamic to libtool. + AC_DEFINE(BUILT_R_DYNAMIC,1,[whether -export-dynamic was passed to libtool]) + R_DYNAMIC_LDFLAG=-export-dynamic +else + R_DYNAMIC_LDFLAG= +fi +AC_SUBST(R_DYNAMIC_LDFLAG) + +#### gcc warning flags + +if test "x$GCC" = "xyes"; then + changequote(,)dnl + case " $CFLAGS " in + *[\ \ ]-Wall[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wall" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-Wchar-subscripts[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wchar-subscripts" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-Wmissing-declarations[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wmissing-declarations" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-Wnested-externs[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wnested-externs" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-Wcast-align[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wcast-align" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-Wformat[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wformat" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-Wformat-security[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wformat-security" ;; + esac + + if test "x$enable_ansi" = "xyes"; then + case " $CFLAGS " in + *[\ \ ]-ansi[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -ansi" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-D_POSIX_C_SOURCE*) ;; + *) CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=199309L" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-D_BSD_SOURCE[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -D_BSD_SOURCE" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-pedantic[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -pedantic" ;; + esac + fi + changequote([,])dnl +fi + +PKG_CHECK_MODULES(CURL, [libcurl]) +AC_SUBST(CURL_CFLAGS) +AC_SUBST(CURL_LIBS) + +ZLIB_LIBS="" +AC_ARG_WITH(zlib, [ --with-zlib= Use zlib from here], + [ + zlib=$withval + CPPFLAGS="$CPPFLAGS -I$withval/include" + LDFLAGS="$LDFLAGS -L$withval/lib" + ] + ) +AC_CHECK_HEADERS(zlib.h, [AC_DEFINE(HAVE_ZLIB_H)], + [AC_MSG_ERROR([Can't find zlib.h. Please install zlib.])]) +AC_CHECK_LIB(z, inflate, [ZLIB_LIBS="-lz"], + [AC_MSG_ERROR([Can't find zlib library. Please install zlib.])]) +AC_SUBST(ZLIB_LIBS) + +EXPAT_LIB="" +AC_ARG_WITH(expat, [ --with-expat= Use expat from here], + [ + expat=$withval + CPPFLAGS="$CPPFLAGS -I$withval/include" + LDFLAGS="$LDFLAGS -L$withval/lib" + ] + ) +AC_CHECK_HEADERS(expat.h, [AC_DEFINE(HAVE_EXPAT_H)], + [AC_MSG_ERROR([Can't find expat.h. Please install expat.])]) +AC_CHECK_LIB(expat, XML_ParserCreate, [EXPAT_LIBS="-lexpat"], + [AC_MSG_ERROR([Can't find expat library. Please install expat.])]) +AC_SUBST(EXPAT_LIBS) + +RPM_LIB="" +AC_ARG_WITH(rpm, [ --with-rpm= Use rpm from here], + [ + rpm=$withval + CPPFLAGS="$CPPFLAGS -I$withval/include" + LDFLAGS="$LDFLAGS -L$withval/lib" + ] + ) +AC_CHECK_HEADERS(rpm/rpmlib.h, [], + [AC_MSG_ERROR([Can't find rpm/rpmlib.h. Please install rpm-devel.])]) +AC_CHECK_LIB(rpm,rpmdbOpen,[RPM_LIBS="-lrpm"], + [AC_MSG_ERROR([Can't find rpm library. Please install rpm-devel.])]) +AC_SUBST(RPM_LIBS) + +if test "x$GCC" = "xyes"; then + LDFLAGS="-Wl,--as-needed $LDFLAGS" +fi + +# ***************************** +# Make available to Makefile.am +# ***************************** +AC_SUBST(SYSCONFDIR, $sysconfdir) + +# ******************** +# Internationalisation +# ******************** + +IT_PROG_INTLTOOL([0.36.0]) +GETTEXT_PACKAGE=razor +AC_SUBST([GETTEXT_PACKAGE]) +AM_GLIB_GNU_GETTEXT +AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE],["$GETTEXT_PACKAGE"],[gettext domain]) + +AC_OUTPUT([ +Makefile +data/razor.pc +data/Makefile +librazor/Makefile +src/Makefile +docs/Makefile +po/Makefile.in +]) + +dnl ========================================================================== +echo " + razor $VERSION + ================= + + prefix: ${prefix} + libdir: ${libdir} + libexecdir: ${libexecdir} + bindir: ${bindir} + sbindir: ${sbindir} + datadir: ${datadir} + sysconfdir: ${sysconfdir} + localstatedir: ${localstatedir} + docdir: ${docdir} + + compiler: ${CC} + cflags: ${CFLAGS} + Maintainer mode: ${USE_MAINTAINER_MODE} + Building verbose mode: ${enable_verbose_mode} +" + diff -r edd5fe0a00ba -r c3eb520e2219 data/.gitignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/.gitignore Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,2 @@ +razor.pc + diff -r edd5fe0a00ba -r c3eb520e2219 data/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/Makefile.am Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,11 @@ +## Process this file with automake to produce Makefile.in + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = razor.pc + +bashcompletiondir = ${SYSCONFDIR}/bash_completion.d +dist_bashcompletion_DATA = bash-completion.sh + +clean-local : + rm -f *~ + diff -r edd5fe0a00ba -r c3eb520e2219 data/bash-completion.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/bash-completion.sh Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,47 @@ +__razor_commands () { + local IFS=$'\n' + COMPREPLY=($(IFS=: compgen -S' ' -W "list-requires:list-provides:list-files:list-file-packages:list-package-files:what-requires:what-provides:import-yum:import-rpmdb:validate:update:diff:install:init:download" -- $1)) +} + +__razor_packages () { + local IFS=$'\n' + + COMPREPLY=($(./razor list --only-names "$1*" | while read p; do echo "$p "; done)) +} + +__razor_upstream_packages () { + local IFS=$'\n' + + COMPREPLY=($(RAZOR_REPO=rawhide.repo ./razor list --only-names "$1*" | while read p; do echo "$p "; done)) +} + +__razor_files() { + COMPREPLY=($(./razor list-files "$1*")) +} + +__razor_requires() { + COMPREPLY=($(compgen -W "$(./razor list-requires)" -- $1)) +} + +__razor_provides() { + COMPREPLY=($(compgen -W "$(./razor list-provides)" -- $1)) +} + +__razor() { + local cur="${COMP_WORDS[COMP_CWORD]}" + + if [ $COMP_CWORD = 1 ]; then + __razor_commands $cur + else + case "${COMP_WORDS[1]}" in + list-requires|list-provides|list-package-files) + __razor_packages $cur ;; + list-files|list-file-packages) __razor_files $cur ;; + what-requires) __razor_requires $cur ;; + what-provides) __razor_provides $cur ;; + install|download) __razor_upstream_packages $cur ;; + esac + fi +} + +complete -o nospace -F __razor razor diff -r edd5fe0a00ba -r c3eb520e2219 data/razor.pc.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/razor.pc.in Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: razor +Description: library for depsolving, installing and removing packages +Version: @VERSION@ +Requires: expat curl +Libs: -L${libdir} -lexpat -lz -lcurl +Cflags: -I${includedir}/razor + diff -r edd5fe0a00ba -r c3eb520e2219 docs/DEPSOLVE.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/DEPSOLVE.txt Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,364 @@ +YUM vs RAZOR +------------ + +At a very high level, yum's depsolver does something roughly +equivalent to: + + - For each package being installed or removed + + - For each relevant property (provides, requires, conflicts, + obsoletes): + + - Figure out what additional packages need to be added to + or removed from the system to satisfy this property + +which ends up being roughly O(N^2 * M) where N is the total number of +properties and M is the number of packages being acted on. + +(I just figured that out off the top of my head, and I'm not totally +familiar with the yum code, so it may be wrong.) + +Razor's depsolver is something like: + + - do { + + - For each property to be added to or removed from the system: + + - Figure out what packages need to be added to or removed + from the system to satisfy this property + + - } until we stop adding/remove more packages + +with the key being that it's very easy to find the PROVIDES +corresponding to a REQUIRES and vice versa, because the property +arrays are sorted, and so all properties with the same "name" will be +adjacent to one another in the array, allowing many dependencies to be +satisified in essentially constant time. (Actually... we've been +calling it constant, but it's really O(log N) for heavily-depended-on +packages, because the more packages you have, the more variations on +"requires foo", "requires foo = 1.1", "requires foo > 1.0", etc you're +going to have to scan through.) + +Ideally though, each iteration of the inner loop body happens in +constant time, and thus the inner loop as a whole is O(N), and thus +the depsolver as a whole is O(N * M) (or at least, less than +O(N * M * log N). + + +FILE DEPENDENCIES +----------------- + +Whenever we add a package with a file REQUIRES to a razor_set, we also +add a PROVIDES for that file to the package or packages which provide +that file. This means that if we later add another package that +requires the same file (eg, /bin/sh or /usr/bin/perl), we can resolve +its file requirement exactly like we would resolve a property +requirement, in nearly constant time. + +When adding a *new* file requirement (ie, a requirement on a file that +no existing package depends on), we still have to scan through the +file tree, which is O(log N) in the number of files. + +(AFAICT, there's no reason yum couldn't do the same optimization. +Also, AFAICT, yum currently sticks property dependencies and file +dependencies into the same hash table, so that if any package in the +transaction has a file dependency, it causes *property* dependencies +to become slower to resolve as well...) + + +THE RULES +--------- + +This is what we have figured out for transaction-solving rules; +neither yum nor rpm's algorithm seems to be explained in full +anywhere... + + 1. Every requested install in the initial package set must be + satisfied as either a new install or an update: + + - if the requested package name is the name of an upstream + package: + + - if there is not a corresponding already-installed + package, then install the upstream package + + - else if the upstream package is newer than the + already-installed package, then update the package + + - else it's an error (UP_TO_DATE) + + - else if the requested package name is the name of an + already-installed package: + + - if there is an upstream package that obsoletes the + already-installed package, then behave as though the + user had requested that that package be installed + instead. + + - else it's an error (UP_TO_DATE or INSTALL_UNAVAILABLE?) + + - else it's an error (INSTALL_UNAVAILABLE) + + 2. Every requested removal in the initial package set must be + satisfied as a removal. If any requested package name is not + the name of an installed package, it's an error + (REMOVE_NOT_INSTALLED) + + REQUIRES processing: + + 3. If a package being installed or updated-to REQUIRES a property + that is not provided by any installed or to-be-installed + package, we need to find an installable package that provides + that property. If we find one, install/update it. If not, it's + an error (UNSATISFIABLE). (If we find an upstream package + providing the property that corresponds to a system package + that's being removed, then it's a CONTRADICTION.) + + 4. If an already-installed package REQUIRES a property which is + only provided by a package that is being removed, then that + package needs to be removed as well. + + 5. If an already-installed package REQUIRES a property which is + only provided by a package that is being upgraded or obsoleted + (to a new package which does not provide that property), then: + + - if there is an update for the installed package, then update + the installed package + + - else if there is another installable package that provides + the required property, then install that. + + - else it's an error (UNSATISFIABLE?) + + CONFLICTS processing + + 6. If a package being installed or updated-to CONFLICTS with a + property provided by an installed package: + + - if there is an update for the installed package, which the + new package does not conflict with, then update the + installed package. + + - else it's an error (NEW_CONFLICT) + + 7. If an already-installed package CONFLICTS with a property + provided by a to-be-installed package: + + - if there is an update for the installed package, which does + not conflict with the new package, then update the installed + package. + + - else it's an error (NEW_CONFLICT) + + 8. If a package being installed or updated-to CONFLICTS with a + property provided by a to-be-installed package, then it's an + error (CONTRADICTION). + + OBSOLETES processing. NOTE: OBSOLETES are only matched against + package names, not against arbitrary provided properties + + 9. If a package being installed or updated-to OBSOLETES an + installed package, then obsolete that package. (ie, remove it, + but treat it as updated for purposes of dangling REQUIRES). + + 10. If an already-installed package OBSOLETES a to-be-installed + package, then it's an error. (ALREADY_OBSOLETE) + + 11. If a package being installed or updated-to OBSOLETES another + package being installed or updated-to, then it's an error + (CONTRADICTION). + + + +THE DEPSOLVER +------------- + +We start with two razor_sets, system and upstream, and a list of +requested installations and removals. + + FIXME: what about multiple upstream repos? Having to deal with + arbitrary numbers of razor_sets is possible, but will probably be + messy... It might be easier to either store all upstream repo data + in a single .repo file, or else merge all upstream .repo files + together into a single razor_set at startup. (Or some combination + of those.) + +We create a bit array of the packages in each set, indicating which +ones are installed; the system bitarray starts out all 1s, and the +upstream bitarray all 0s. Each bit is only allowed to change state +once during the transaction; an installed package can be removed, or +an uninstalled package installed, but trying to reinstall a removed +package, or uninstall a newly-installed package is an error. This +means the packages break down into four categories: + + - installed (1 bit in the system bit array) + - to-be-removed (0 bit in the system bit array) + - to-be-installed (1 bit in the upstream bit array) + - installable (0 bit in the upstream bit array) + + +Depsolver algorithm: + + - Create new razor_transaction_packages ("rtp"s) for each + requested install or remove. These will be "unresolved", because + we haven't yet found the razor_packages that correspond to them. + + - while there are new rtps: + + - sort the new rtps + + - Walk the system property list, upstream property list, and + new rtp list in parallel, and: + + - For each uninstalled PROVIDES: + + - If the property is a valid package name (that is, + either it's a package providing its own name, or it + has a matching OBSOLETES), and it matches the name + of a new rtp of type INSTALL or FORCED_UPDATE with + an unresolved new_package: + + - If the upstream package has the same version as + the system package, we have an UP_TO_DATE error + (FIXME: not quite right. This doesn't deal with + the case where we try to update an application + because of a library update, and it turns out + there's no new version of the application, but + there IS a compat package containing the old + version of the library.) + + - Otherwise, set the rtp's new_package to point to + the package providing this property and set the + appropriate bit in the upstream bit array. + + - For each to-be-installed non-file REQUIRES: + + - See if there's an installed or to-be-installed + package that PROVIDES that property. + + - If not, see if there's an installable package that + PROVIDES that property, and create a new INSTALL rtp + for it if so. + + - If not, see if there's a to-be-removed package that + PROVIDES that property. (If we find such a package, + we have a CONTRADICTION error.) + + - If none of the above, then we have an UNSATISFIABLE + error + + - For each to-be-installed file REQUIRES: + + - (We create fake file PROVIDES to match file REQUIRES + when importing/merging razor sets, so if there is + already another installed package that REQUIRES this + file, there will be a PROVIDES listed for it as well.) + + - See if there's an installed package that PROVIDES + that file. + + - If not, do a binary search of the system file tree + looking to see if some installed package provides + that file but does not have a PROVIDES for it. + + - If not, see if there's an installable package that + PROVIDES that property, and create a new INSTALL rtp + for it if so. + + - (If we actually work with multiple upstream + razor_sets, then we will need to search the upstream + file trees at this point, because it's possible that + a package in one upstream repo would require a file + in another upstream repo. But if we merge the + multiple upstream repos into a single razor_set at + some point, then we would not need to do that, + because it would be guaranteed that we would have + already created a fake PROVIDES if any package + provides the file.) + + - If no installed or installable package provides the + file, see if there's a to-be-removed package that + provides the file. (If we find such a package, we + have a CONTRADICTION error.) + + - If none of the above, then we have an UNSATISFIABLE + error + + - For each to-be-installed PROVIDES: + + - Check if the new PROVIDES conflicts with an + installed CONFLICTS. If so, create a new + FORCED_UPDATE rtp for the installed package, so we + can try to upgrade it to a non-conflicting version. + (If we can't, we'll have an OLD_CONFLICT error.) + + - Check if the new PROVIDES conflicts with an + installed OBSOLETES *and* the PROVIDES property + corresponds to the name of its package. (That is, + OBSOLETES are only matched against package names, + not arbitrary provided properties.) If so, we have + an ALREADY_OBSOLETE error. + + - Check if the new PROVIDES conflicts with a + to-be-installed CONFLICTS. If so, we have a + CONTRADICTION error. + + - For each to-be-installed CONFLICTS: + + - Basically the reverse of the previous case: check if + the new CONFLICTS conflicts with an installed + PROVIDES. If so, create a new FORCED_UPDATE rtp for + the installed package, so we can try to upgrade it + to a non-conflicting version. (If we can't, we'll + have an NEW_CONFLICT error.) + + - Check if the new CONFLICTS conflicts with a + to-be-installed PROVIDES. If so, we have a + CONTRADICTION error. + + - For each to-be-installed OBSOLETES: + + - Check if there's an installed package that PROVIDES + that property. If so, create an OBSOLETED rtp for + the installed package. + + - If not, check if there's a to-be-installed package + that PROVIDES that property. If so, we have a + CONTRADICTION error. + + + - For each installed PROVIDES: + + - If the property is a valid package name (that is, + it's a package providing its own name), and it + matches the name of a new rtp with an unresolved + old_package, then set the rtp's old_package to point + to the package providing this property and clear the + appropriate bit in the system bit array. + + - For each to-be-removed PROVIDES: + + - If there's also an identical to-be-installed + PROVIDES, we're ok and can skip this + + - Otherwise, for each installed REQUIRES of this + property: + + - Look for some other installed or to-be-installed + property that satisfies the REQUIRES. + + - If there isn't one, then for each installed + package in this REQUIRES's package list: + + - If the PROVIDES was lost because the old + package was REMOVEd (not FORCED_UPDATE or + OBSOLETED), then create a new REMOVE rtp for + this package. + + - Otherwise, create a new FORCED_UPDATE rtp + for this package. + + - (We don't need to look at to-be-installed REQUIRES + of this property, because if there are any, they + will cause a CONTRADICTION error when we try to + re-satisfy them the next time through.) diff -r edd5fe0a00ba -r c3eb520e2219 docs/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/Makefile.am Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,5 @@ + +EXTRA_DIST = \ + DEPSOLVE.txt \ + REPO.txt + diff -r edd5fe0a00ba -r c3eb520e2219 docs/REPO.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/REPO.txt Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,172 @@ +The repo file format / razor_set data structure +----------------------------------------------- + +The repo starts with a header, containing some number of sections, +terminated by a section with type 0: + + struct razor_set_header { + uint32_t magic; + uint32_t version; + struct razor_set_section sections[0]; + }; + + struct razor_set_section { + uint32_t type; + uint32_t offset; + uint32_t size; + }; + +razor_set_open() mmaps the repo file, and creates a struct razor_set: + + 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 razor_set_header *header; + }; + +by finding the sections with those IDs and creating "struct array"s +pointing to the right places in the mmapped data. (This is the only +processing needed when reading in the file; everything else is used +exactly as-is.) + + +The sections +------------ + +RAZOR_STRING_POOL + + Stores one copy of each string that appears in the repo. (At + the moment, this is: package names, package versions, property + names, property versions, and (basenames of) filenames.) The + strings are arbitrarily-sized, 0-terminated, and not in any + particular order (although the empty string always ends up + being at offset 0). + +RAZOR_PACKAGES + + Array of struct razor_package; one for each package in the + set, sorted by name. + +RAZOR_PROPERTIES + + Array of struct razor_property; one for each unique property + in the set, sorted by type, then name, then relation type (eg, + "<" or ">="), then version. (Properties with no version have + relation type RAZOR_VERSION_EQUAL, and version "".) + +RAZOR_FILES + + Array of struct razor_entry; one for each file owned by any + package in the set. The current sort order (which is subject + to change) is breadth-first, sorted by basename. So eg: /, /bin, + /dev, /etc, /bin/false, /bin/true, /dev/null, /etc/passwd. + +RAZOR_PACKAGE_POOL + + Array of struct list, with each list item containing the index + of a struct razor_package in the packages section. See the + discussion of lists below. + +RAZOR_PROPERTY_POOL + + Array of struct list, with each list item containing the index + of a struct razor_property in the properties section. See the + discussion of lists below. + +RAZOR_FILE_POOL + + Array of struct list, with each list item containing the index + of a struct razor_entry in the files section. See the + discussion of lists below. + + +Data types +---------- +Note that the exact layout of bits involves some historical accidents. +(Particularly the fact that the "name" field in most structs loses its +high bits to a flags field.) + +struct list_head + uint list_ptr : 24; + uint flags : 8; + +struct list + uint data : 24; + uint flags : 8; + + Used to store lists of package, property, or file IDs. "struct + list_head" stores the head of the list, which points to one or + more "struct list"s in the appropriate "pool" section. + ("struct list" should probably be called "struct list_item".) + + "list_first(&head, &pool)" returns a "struct list *" pointing + to the first element of the list (or NULL for an empty list), + and "list_next(list)" will return successive elements, until + NULL is returned. Each "list->data" contains the index of a + package, property, or file in the corresponding section of the + set. + + Peeking underneath the abstraction, a list_head's "flags" is + 0xff if the list is empty, 0x80 if it contains a single + element, or 0x00 if it contains more than one element. In the + single-element case, that element is actually stored in the + list_head directly rather than being stored in a pool (and so + list_first() just casts the list_head* to a list* and returns + it). For multi-element lists, list_ptr is the index in the + pool of the first element of this list; the list continues + through successive elements of the pool until one with + non-zero flags is reached, indicating the end of the list. + +struct razor_package + uint name : 24; + uint flags : 8; + uint version : 32; + struct list_head properties; + struct list_head files; + + name and version are indexes into string_pool. properties is a + list of all of the package's properties, and files is a list + of its files. flags is currently only used during razor_set + merging, to keep track of which set a package came from. + +struct razor_property + uint name : 24; + uint flags : 6; + uint type : 2; + uint relation : 32; + uint version : 32; + struct list_head packages; + + name and version are indexes into string_pool. type is an enum + razor_property_type (eg, RAZOR_PROPERTY_REQUIRES), and + relation is an enum razor_version_relation (eg, + RAZOR_VERSION_GREATER_OR_EQUAL). packages is a list of the + packages that have this property. flags is currently unused. + +struct razor_entry + uint name : 24; + uint flags : 8; + uint start : 32; + struct list_head packages; + + name is an index into string_pool, giving the basename of the + file. start is either 0, or an index pointing to another + razor_entry that is the first child of this entry (for a + non-empty directory). (Entry 0 is always the root of the tree, + so no entry could have entry 0 as a child.) flags is 0x80 + (RAZOR_ENTRY_LAST) if an entry is the last entry in its + directory. Otherwise it is 0. + + Note that given a pointer to a struct_razor_entry (eg, from a + package's "files" list), there is no way to reconstruct its + full name without walking the entire files array up to that + point. Because of this and other problems (fix_file_map()), it + seems like razor_entry should be modified to include a pointer + to its parent. (Storing full paths instead of just basenames + would also fix this problem, but that would use much more + memory.) diff -r edd5fe0a00ba -r c3eb520e2219 librazor/.gitignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librazor/.gitignore Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,5 @@ +.deps +.libs +*.lo +*.la + diff -r edd5fe0a00ba -r c3eb520e2219 librazor/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librazor/Makefile.am Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,34 @@ +## 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 \ + razor-root.c \ + types.h \ + types.c \ + util.c \ + rpm.c + +librazor_la_LIBADD = $(ZLIB_LIBS) + +clean-local : + rm -f *~ + diff -r edd5fe0a00ba -r c3eb520e2219 librazor/razor-internal.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librazor/razor-internal.h Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,19 @@ +#ifndef _RAZOR_INTERNAL_H_ +#define _RAZOR_INTERNAL_H_ + +#define ALIGN(value, base) (((value) + (base - 1)) & ~((base) - 1)) + +/* 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 -r edd5fe0a00ba -r c3eb520e2219 librazor/razor-root.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librazor/razor-root.c Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include +#include +#include +#include "razor.h" +#include "razor-internal.h" + +static const char system_repo_filename[] = "system.repo"; +static const char next_repo_filename[] = "system-next.repo"; +static const char razor_root_path[] = "/var/lib/razor"; + +struct razor_root { + struct razor_set *system; + struct razor_set *next; + int fd; + char path[PATH_MAX]; + char new_path[PATH_MAX]; +}; + +int +razor_root_create(const char *root) +{ + struct stat buf; + struct razor_set *set; + char path[PATH_MAX]; + + if (stat(root, &buf) < 0) { + if (mkdir(root, 0777) < 0) { + fprintf(stderr, + "could not create install root \"%s\"\n", + root); + return -1; + } + fprintf(stderr, "created install root \"%s\"\n", root); + } else if (!S_ISDIR(buf.st_mode)) { + fprintf(stderr, + "install root \"%s\" exists, but is not a directory\n", + root); + return -1; + } + + snprintf(path, sizeof path, "%s/%s", + razor_root_path, system_repo_filename); + if (razor_create_dir(root, path) < 0) { + fprintf(stderr, "could not create %s%s\n", + root, razor_root_path); + return -1; + } + + set = razor_set_create(); + snprintf(path, sizeof path, "%s%s/%s", + root, razor_root_path, system_repo_filename); + if (stat(path, &buf) == 0) { + fprintf(stderr, + "a razor install root is already initialized\n"); + return -1; + } + if (razor_set_write(set, path) < 0) { + 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, root, 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); + 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 -r edd5fe0a00ba -r c3eb520e2219 librazor/razor.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librazor/razor.c Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,2611 @@ +/* + * 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 "razor.h" +#include "razor-internal.h" +#include "types.h" + +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_VERSION 1 + +#define RAZOR_STRING_POOL 0 +#define RAZOR_PACKAGES 1 +#define RAZOR_PROPERTIES 2 +#define RAZOR_FILES 3 +#define RAZOR_PACKAGE_POOL 4 +#define RAZOR_PROPERTY_POOL 5 +#define RAZOR_FILE_POOL 6 + +struct razor_package { + uint name : 24; + uint flags : 8; + uint32_t version; + uint32_t arch; + struct list_head properties; + struct list_head files; +}; + +struct razor_property { + uint name : 24; + uint flags : 6; + enum razor_property_type type : 2; + enum razor_version_relation relation : 32; + uint32_t version; + struct list_head packages; +}; + +struct razor_entry { + uint name : 24; + uint 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 razor_set_header *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 razor_package *package; + struct array properties; + struct array files; + struct array file_requires; +}; + +static void * +zalloc(size_t size) +{ + void *p; + + p = malloc(size); + memset(p, 0, size); + + return p; +} + +struct razor_set_section razor_sections[] = { + { RAZOR_STRING_POOL, offsetof(struct razor_set, string_pool) }, + { RAZOR_PACKAGES, offsetof(struct razor_set, packages) }, + { RAZOR_PROPERTIES, offsetof(struct razor_set, properties) }, + { RAZOR_FILES, offsetof(struct razor_set, files) }, + { RAZOR_PACKAGE_POOL, offsetof(struct razor_set, package_pool) }, + { RAZOR_PROPERTY_POOL, offsetof(struct razor_set, property_pool) }, + { RAZOR_FILE_POOL, offsetof(struct razor_set, file_pool) }, +}; + +struct razor_set * +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_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); + } + } + + free(set); +} + +int +razor_set_write_to_fd(struct razor_set *set, int fd) +{ + char data[4096]; + struct razor_set_header *header = (struct razor_set_header *) data; + struct array *a; + uint32_t offset; + int i; + + memset(data, 0, sizeof data); + header->magic = RAZOR_MAGIC; + header->version = RAZOR_VERSION; + offset = sizeof data; + + for (i = 0; i < ARRAY_SIZE(razor_sections); i++) { + if (razor_sections[i].type != i) + continue; + a = (void *) set + razor_sections[i].offset; + header->sections[i].type = i; + header->sections[i].offset = offset; + header->sections[i].size = a->size; + offset += ALIGN(a->size, 4096); + } + + header->sections[i].type = ~0; + header->sections[i].offset = 0; + header->sections[i].size = 0; + + razor_write(fd, data, sizeof data); + memset(data, 0, sizeof data); + for (i = 0; i < ARRAY_SIZE(razor_sections); i++) { + if (razor_sections[i].type != i) + continue; + a = (void *) set + razor_sections[i].offset; + razor_write(fd, a->data, a->size); + razor_write(fd, data, ALIGN(a->size, 4096) - a->size); + } + + return 0; +} + +int +razor_set_write(struct razor_set *set, const char *filename) +{ + 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); + 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); +} + +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_property(struct razor_importer *importer, + const char *name, + enum razor_version_relation relation, + const char *version, + enum razor_property_type type) +{ + struct razor_property *p; + uint32_t *r; + + p = array_add(&importer->set->properties, sizeof *p); + p->name = hashtable_tokenize(&importer->table, name); + p->flags = 0; + p->type = type; + p->relation = relation; + 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 (type == 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); + + return importer; +} + +/* Destroy an importer without creating the set. */ +void +razor_importer_destroy(struct razor_importer *importer) +{ + /* FIXME: write this */ +} + +static int +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 versioncmp(p1, p2); + } + + return *p1 - *p2; +} + +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 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->type != prop2->type) + return prop1->type - prop2->type; + else if (prop1->relation != prop2->relation) + return prop1->relation - prop2->relation; + else + return 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->type != up->type || + rp->relation != up->relation || rp->version != up->version) { + up++; + up->name = rp->name; + up->flags = 0; + up->type = rp->type; + up->relation = rp->relation; + 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 struct razor_entry * +find_entry(struct razor_set *set, struct razor_entry *dir, const char *pattern); + +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 = 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->type = RAZOR_PROPERTY_PROVIDES; + prop->relation = RAZOR_VERSION_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; +} + +struct razor_package_iterator { + struct razor_set *set; + struct razor_package *package, *end; + struct list *index; + int free_index; +}; + +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; +} + +static 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); +} + +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_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; +} + +struct razor_property_iterator { + struct razor_set *set; + struct razor_property *property, *end; + struct list *index; +}; + +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, + enum razor_version_relation *relation, + const char **version, + enum razor_property_type *type) +{ + 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]; + *relation = p->relation; + *version = &pool[p->version]; + *type = p->type; + } else { + *property = NULL; + } + + return valid; +} + +void +razor_property_iterator_destroy(struct razor_property_iterator *pi) +{ + free(pi); +} + +static struct razor_entry * +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 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 = 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 = find_entry(set, set->files.data, buffer); + if (e->start != 0) + list_dir(set, e, buffer, base); +} + +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 = 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); +} + +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); +} + +#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; +}; + +static 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; +} + +static 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, enum razor_version_relation relation, + const char *version, int type) +{ + struct razor_property *p; + + p = array_add(&merger->set->properties, sizeof *p); + p->name = hashtable_tokenize(&merger->table, name); + p->flags = 0; + p->type = type; + p->relation = relation; + 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->type - p2->type; + if (cmp == 0) + cmp = p1->relation - p2->relation; + if (cmp == 0) + cmp = versioncmp(&pool1[p1->version], + &pool2[p2->version]); + if (cmp < 0) { + map1[i++] = add_property(merger, + &pool1[p1->name], + p1->relation, + &pool1[p1->version], + p1->type); + } else if (cmp > 0) { + map2[j++] = add_property(merger, + &pool2[p2->name], + p2->relation, + &pool2[p2->version], + p2->type); + } else { + map1[i++] = map2[j++] = add_property(merger, + &pool1[p1->name], + p1->relation, + &pool1[p1->version], + p1->type); + } + } +} + +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); +} + +static 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; +} + +/* 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 = 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); +} + +static int +provider_satisfies_requirement(struct razor_property *provider, + const char *provider_strings, + enum razor_version_relation relation, + const char *required) +{ + int cmp, len; + const char *provided = &provider_strings[provider->version]; + + if (!*required) + return 1; + if (!*provided) { + if (relation >= RAZOR_VERSION_EQUAL) + return 1; + else + return 0; + } + + cmp = versioncmp(provided, required); + + switch (relation) { + case RAZOR_VERSION_LESS: + return cmp < 0; + + case RAZOR_VERSION_LESS_OR_EQUAL: + if (cmp <= 0) + return 1; + /* fall through: FIXME, make sure this is correct */ + + case RAZOR_VERSION_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_VERSION_GREATER_OR_EQUAL: + return cmp >= 0; + + case RAZOR_VERSION_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, + enum razor_property_type type, struct razor_property **p) +{ + while (pi->p < pi->end) { + if ((pi->present[pi->p - pi->start] & ~TRANS_PROPERTY_SATISFIED) && + pi->p->type == type) { + *p = pi->p++; + return 1; + } + pi->p++; + } + + return 0; +} + +static struct razor_property * +prop_iter_seek_to(struct prop_iter *pi, + enum razor_property_type type, 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->type != type) + 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, + enum razor_version_relation relation, + 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; + + if (ppi->present == trans->system.properties) + set = trans->system.set; + else + set = trans->upstream.set; + + pkgs = (struct razor_package *) set->packages.data; + for (p = ppi->p; + p < ppi->end && + p->name == ppi->p->name && + p->type == ppi->p->type; + p++) { + if (!ppi->present[p - ppi->start]) + continue; + if (!provider_satisfies_requirement(p, ppi->pool, + relation, 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; + + 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; + for (p = ppi->p; + p < ppi->end && + p->name == ppi->p->name && + p->type == ppi->p->type; + p++) { + if (!ppi->present[p - ppi->start]) + continue; + if (!provider_satisfies_requirement(p, ppi->pool, + r->relation, + &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, + enum razor_version_relation relation, + const char *version) +{ + struct razor_property *p; + struct razor_package *pkgs; + struct list *i; + + /* 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; + for (p = ppi->p; + p < ppi->end && + p->name == ppi->p->name && + p->type == ppi->p->type && + ppi->present[p - ppi->start] == 0; + p++) { + if (!provider_satisfies_requirement(p, ppi->pool, + relation, 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->relation, + &upi.pool[up->version]); + } +} + +static int +any_provider_satisfies_requirement(struct prop_iter *ppi, + enum razor_version_relation relation, + const char *version) +{ + struct razor_property *p; + + for (p = ppi->p; + p < ppi->end && + p->name == ppi->p->name && + p->type == ppi->p->type; + p++) { + if (ppi->present[p - ppi->start] > 0 && + provider_satisfies_requirement(p, ppi->pool, + relation, 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; + } +} + +static const char *relation_string[] = { "<", "<=", "=", ">=", ">" }; + +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->relation, + &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, + relation_string[sp->relation], + 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->relation, + &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->relation, + &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, + relation_string[pp->relation], + ppi->pool + pp->version, + &rpi->pool[rp->name], + relation_string[rp->relation], + &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_VERSION_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)) + continue; + remove_matching_providers(trans, &spi, + RAZOR_VERSION_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); + + 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], + relation_string[rp->relation], + &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, + enum razor_version_relation rel, + const char *version, + enum razor_property_type type) +{ + struct prop_iter pi; + struct razor_property *p; + + prop_iter_init(&pi, &trans->system); + while (prop_iter_next(&pi, type, &p)) { + if (!(trans->system.properties[p - pi.start] & TRANS_PROPERTY_SATISFIED) && + p->relation == rel && + 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, type, &p)) { + if (!(trans->upstream.properties[p - pi.start] & TRANS_PROPERTY_SATISFIED) && + p->relation == rel && + 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); +} + +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 -r edd5fe0a00ba -r c3eb520e2219 librazor/razor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librazor/razor.h Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,189 @@ +/* + * 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. + */ + +#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_property_type { + RAZOR_PROPERTY_REQUIRES, + RAZOR_PROPERTY_PROVIDES, + RAZOR_PROPERTY_CONFLICTS, + RAZOR_PROPERTY_OBSOLETES +}; + +enum razor_version_relation { + RAZOR_VERSION_LESS, + RAZOR_VERSION_LESS_OR_EQUAL, + RAZOR_VERSION_EQUAL, + RAZOR_VERSION_GREATER_OR_EQUAL, + RAZOR_VERSION_GREATER +}; +extern const char * const razor_version_relations[]; + +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); +int razor_set_write(struct razor_set *set, const char *filename); + +struct razor_package * +razor_set_get_package(struct razor_set *set, const char *package); + +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, + enum razor_version_relation *relation, + const char **version, + enum razor_property_type *type); +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, + enum razor_version_relation rel, + const char *version, + enum razor_property_type type); + +/* 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_property(struct razor_importer *importer, + const char *name, + enum razor_version_relation relation, + const char *version, + enum razor_property_type type); +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); + +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 -r edd5fe0a00ba -r c3eb520e2219 librazor/rpm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librazor/rpm.c Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,813 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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), +}; + +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 enum razor_version_relation +rpm_to_razor_flags(uint32_t flags) +{ + switch (flags & (RPMSENSE_LESS | RPMSENSE_EQUAL | RPMSENSE_GREATER)) { + case RPMSENSE_LESS: + return RAZOR_VERSION_LESS; + case RPMSENSE_LESS|RPMSENSE_EQUAL: + return RAZOR_VERSION_LESS_OR_EQUAL; + case RPMSENSE_EQUAL: + return RAZOR_VERSION_EQUAL; + case RPMSENSE_GREATER|RPMSENSE_EQUAL: + return RAZOR_VERSION_GREATER_OR_EQUAL; + case RPMSENSE_GREATER: + return RAZOR_VERSION_GREATER; + } + + /* FIXME? */ + return RAZOR_VERSION_EQUAL; +} + +static void +import_properties(struct razor_importer *importer, unsigned long 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, version, type); + 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]; + + /* 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); + 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; + 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); + + 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 -r edd5fe0a00ba -r c3eb520e2219 librazor/types.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librazor/types.c Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,246 @@ +#include +#include + +#include "types.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 -r edd5fe0a00ba -r c3eb520e2219 librazor/types.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librazor/types.h Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,59 @@ +#ifndef _RAZOR_TYPES_H_ +#define _RAZOR_TYPES_H_ + +#include + +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 { + uint list_ptr : 24; + uint flags : 8; +}; + +struct list { + uint data : 24; + uint 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 bitarray { + uint32_t *bits; +}; + +void bitarray_init(struct bitarray *bitarray, int size, int intial_value); +void bitarray_release(struct bitarray *bitarray); +void bitarray_set(struct bitarray *bitarray, int bit, int value); +int bitarray_get(struct bitarray *bitarray, int bit); + + +#endif /* _RAZOR_TYPES_H_ */ diff -r edd5fe0a00ba -r c3eb520e2219 librazor/util.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librazor/util.c Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff -r edd5fe0a00ba -r c3eb520e2219 main.c --- a/main.c Sun Jun 15 23:15:59 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,804 +0,0 @@ -/* - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "razor.h" - -static const char system_repo_filename[] = "system.repo"; -static const char next_repo_filename[] = "system-next.repo"; -static const char rawhide_repo_filename[] = "rawhide.repo"; -static const char updated_repo_filename[] = "system-updated.repo"; -static const char install_root[] = "install"; -static const char *repo_filename = system_repo_filename; -static const char *yum_url; - -static int -command_list(int argc, const char *argv[]) -{ - struct razor_set *set; - struct razor_package_iterator *pi; - struct razor_package *package; - const char *pattern, *name, *version, *arch; - int only_names = 0, i = 0; - - if (i < argc && strcmp(argv[i], "--only-names") == 0) { - only_names = 1; - i++; - } - - pattern = argv[i]; - set = razor_set_open(repo_filename); - pi = razor_package_iterator_create(set); - while (razor_package_iterator_next(pi, &package, - &name, &version, &arch)) { - if (pattern && fnmatch(pattern, name, 0) != 0) - continue; - - if (only_names) - printf("%s\n", name); - else - printf("%s-%s.%s\n", name, version, arch); - } - razor_package_iterator_destroy(pi); - razor_set_destroy(set); - - return 0; -} - -static int -list_properties(const char *package_name, - enum razor_property_type required_type) -{ - static const char *relation_string[] = { "<", "<=", "=", ">=", ">" }; - struct razor_set *set; - struct razor_property *property; - struct razor_package *package; - struct razor_property_iterator *pi; - const char *name, *version; - enum razor_property_type type; - enum razor_version_relation relation; - - set = razor_set_open(repo_filename); - if (package_name) - package = razor_set_get_package(set, package_name); - else - package = NULL; - - pi = razor_property_iterator_create(set, package); - while (razor_property_iterator_next(pi, &property, - &name, &relation, &version, - &type)) { - if (type != required_type) - continue; - if (version[0] == '\0') - printf("%s\n", name); - else - printf("%s %s %s\n", name, - relation_string[relation], version); - } - razor_property_iterator_destroy(pi); - - razor_set_destroy(set); - - return 0; -} - -static int -command_list_requires(int argc, const char *argv[]) -{ - return list_properties(argv[0], RAZOR_PROPERTY_REQUIRES); -} - -static int -command_list_provides(int argc, const char *argv[]) -{ - return list_properties(argv[0], RAZOR_PROPERTY_PROVIDES); -} - -static int -command_list_obsoletes(int argc, const char *argv[]) -{ - return list_properties(argv[0], RAZOR_PROPERTY_OBSOLETES); -} - -static int -command_list_conflicts(int argc, const char *argv[]) -{ - return list_properties(argv[0], RAZOR_PROPERTY_CONFLICTS); -} - -static int -command_list_files(int argc, const char *argv[]) -{ - struct razor_set *set; - - set = razor_set_open(repo_filename); - if (set == NULL) - return 1; - razor_set_list_files(set, argv[0]); - razor_set_destroy(set); - - return 0; -} - -static int -command_list_file_packages(int argc, const char *argv[]) -{ - struct razor_set *set; - struct razor_package_iterator *pi; - struct razor_package *package; - const char *name, *version, *arch; - - set = razor_set_open(repo_filename); - if (set == NULL) - return 1; - - pi = razor_package_iterator_create_for_file(set, argv[0]); - while (razor_package_iterator_next(pi, &package, - &name, &version, &arch)) - printf("%s-%s\n", name, version); - razor_package_iterator_destroy(pi); - - razor_set_destroy(set); - - return 0; -} - -static int -command_list_package_files(int argc, const char *argv[]) -{ - struct razor_set *set; - - set = razor_set_open(repo_filename); - if (set == NULL) - return 1; - razor_set_list_package_files(set, argv[0]); - razor_set_destroy(set); - - return 0; -} - -static void -list_packages_for_property(struct razor_set *set, - struct razor_property *property) -{ - struct razor_package_iterator *pi; - struct razor_package *package; - const char *name, *version, *arch; - - pi = razor_package_iterator_create_for_property(set, property); - while (razor_package_iterator_next(pi, &package, - &name, &version, &arch)) - printf("%s-%s.%s\n", name, version, arch); - razor_package_iterator_destroy(pi); -} - -static int -list_property_packages(const char *ref_name, - const char *ref_version, - enum razor_property_type ref_type) -{ - struct razor_set *set; - struct razor_property *property; - struct razor_property_iterator *pi; - const char *name, *version; - enum razor_property_type type; - enum razor_version_relation relation; - - if (ref_name == NULL) - return 0; - - set = razor_set_open(repo_filename); - if (set == NULL) - return 1; - - pi = razor_property_iterator_create(set, NULL); - while (razor_property_iterator_next(pi, &property, - &name, &relation, &version, - &type)) { - if (strcmp(ref_name, name) != 0) - continue; - if (ref_version && relation == RAZOR_VERSION_EQUAL && - strcmp(ref_version, version) != 0) - continue; - if (ref_type != type) - continue; - - list_packages_for_property(set, property); - } - razor_property_iterator_destroy(pi); - - return 0; -} - -static int -command_what_requires(int argc, const char *argv[]) -{ - return list_property_packages(argv[0], argv[1], - RAZOR_PROPERTY_REQUIRES); -} - -static int -command_what_provides(int argc, const char *argv[]) -{ - return list_property_packages(argv[0], argv[1], - RAZOR_PROPERTY_PROVIDES); -} - -static int -show_progress(void *clientp, - double dltotal, double dlnow, double ultotal, double ulnow) -{ - const char *file = clientp; - - if (!dlnow < dltotal) - fprintf(stderr, "\rdownloading %s, %dkB/%dkB", - file, (int) dlnow / 1024, (int) dltotal / 1024); - - return 0; -} - -static int -download_if_missing(const char *url, const char *file) -{ - CURL *curl; - struct stat buf; - char error[256]; - FILE *fp; - CURLcode res; - long response; - - curl = curl_easy_init(); - if (curl == NULL) - return 1; - - curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); - curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, show_progress); - curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, file); - - if (stat(file, &buf) < 0) { - fp = fopen(file, "w"); - if (fp == NULL) { - fprintf(stderr, - "failed to open %s for writing\n", file); - return -1; - } - curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); - curl_easy_setopt(curl, CURLOPT_URL, url); - res = curl_easy_perform(curl); - fclose(fp); - if (res != CURLE_OK) { - fprintf(stderr, "curl error: %s\n", error); - unlink(file); - return -1; - } - res = curl_easy_getinfo(curl, - CURLINFO_RESPONSE_CODE, &response); - if (res != CURLE_OK) { - fprintf(stderr, "curl error: %s\n", error); - unlink(file); - return -1; - } - if (response != 200) { - fprintf(stderr, " - failed %ld\n", response); - unlink(file); - return -1; - } - fprintf(stderr, "\n"); - } - - curl_easy_cleanup(curl); - - return 0; -} - -#define YUM_URL "http://download.fedora.redhat.com" \ - "/pub/fedora/linux/development/i386/os" - -static int -command_import_yum(int argc, const char *argv[]) -{ - struct razor_set *set; - char buffer[512]; - - printf("downloading from %s.\n", yum_url); - snprintf(buffer, sizeof buffer, - "%s/repodata/primary.xml.gz", yum_url); - if (download_if_missing(buffer, "primary.xml.gz") < 0) - return -1; - snprintf(buffer, sizeof buffer, - "%s/repodata/filelists.xml.gz", yum_url); - if (download_if_missing(buffer, "filelists.xml.gz") < 0) - return -1; - - set = razor_set_create_from_yum(); - if (set == NULL) - return 1; - razor_set_write(set, rawhide_repo_filename); - razor_set_destroy(set); - printf("wrote %s\n", rawhide_repo_filename); - - return 0; -} - -static int -command_import_rpmdb(int argc, const char *argv[]) -{ - struct razor_set *set; - - set = razor_set_create_from_rpmdb(); - if (set == NULL) - return 1; - razor_set_write(set, repo_filename); - razor_set_destroy(set); - printf("wrote %s\n", repo_filename); - - return 0; -} - -static int -mark_packages_for_update(struct razor_transaction *trans, - struct razor_set *set, const char *pattern) -{ - struct razor_package_iterator *pi; - struct razor_package *package; - const char *name, *version, *arch; - int matches = 0; - - pi = razor_package_iterator_create(set); - while (razor_package_iterator_next(pi, &package, - &name, &version, &arch)) { - if (pattern && fnmatch(pattern, name, 0) == 0) { - razor_transaction_update_package(trans, package); - matches++; - } - } - razor_package_iterator_destroy(pi); - - return matches; -} - -static int -mark_packages_for_removal(struct razor_transaction *trans, - struct razor_set *set, const char *pattern) -{ - struct razor_package_iterator *pi; - struct razor_package *package; - const char *name, *version, *arch; - int matches = 0; - - pi = razor_package_iterator_create(set); - while (razor_package_iterator_next(pi, &package, - &name, &version, &arch)) { - if (pattern && fnmatch(pattern, name, 0) == 0) { - razor_transaction_remove_package(trans, package); - matches++; - } - } - razor_package_iterator_destroy(pi); - - return matches; -} - -static int -command_update(int argc, const char *argv[]) -{ - struct razor_set *set, *upstream; - struct razor_transaction *trans; - int i, errors; - - set = razor_set_open(repo_filename); - upstream = razor_set_open(rawhide_repo_filename); - if (set == NULL || upstream == NULL) - return 1; - - trans = razor_transaction_create(set, upstream); - if (argc == 0) - razor_transaction_update_all(trans); - for (i = 0; i < argc; i++) { - if (mark_packages_for_update(trans, set, argv[i]) == 0) { - fprintf(stderr, "no match for %s\n", argv[i]); - return 1; - } - } - - errors = razor_transaction_resolve(trans); - if (errors) - return 1; - - set = razor_transaction_finish(trans); - razor_set_write(set, updated_repo_filename); - razor_set_destroy(set); - razor_set_destroy(upstream); - printf("wrote system-updated.repo\n"); - - return 0; -} - -static int -command_remove(int argc, const char *argv[]) -{ - struct razor_set *set, *upstream; - struct razor_transaction *trans; - int i, errors; - - set = razor_set_open(repo_filename); - if (set == NULL) - return 1; - - upstream = razor_set_create(); - trans = razor_transaction_create(set, upstream); - for (i = 0; i < argc; i++) { - if (mark_packages_for_removal(trans, set, argv[i]) == 0) { - fprintf(stderr, "no match for %s\n", argv[i]); - return 1; - } - } - - errors = razor_transaction_resolve(trans); - if (errors) - return 1; - - set = razor_transaction_finish(trans); - razor_set_write(set, updated_repo_filename); - razor_set_destroy(set); - razor_set_destroy(upstream); - printf("wrote system-updated.repo\n"); - - return 0; -} - -static void -print_diff(const char *name, - const char *old_version, const char *new_version, const char *arch, - void *data) -{ - if (old_version) - printf("removing %s %s\n", name, old_version); - else - printf("install %s %s\n", name, new_version); -} - -static int -command_diff(int argc, const char *argv[]) -{ - struct razor_set *set, *updated; - - set = razor_set_open(repo_filename); - updated = razor_set_open(updated_repo_filename); - if (set == NULL || updated == NULL) - return 1; - - razor_set_diff(set, updated, print_diff, NULL); - - razor_set_destroy(set); - razor_set_destroy(updated); - - return 0; -} - -static int -command_import_rpms(int argc, const char *argv[]) -{ - DIR *dir; - struct dirent *de; - struct razor_importer *importer; - struct razor_set *set; - struct razor_rpm *rpm; - int len; - char filename[256]; - const char *dirname = argv[0]; - - if (dirname == NULL) { - fprintf(stderr, "usage: razor import-rpms DIR\n"); - return -1; - } - - dir = opendir(dirname); - if (dir == NULL) { - fprintf(stderr, "couldn't read dir %s\n", dirname); - return -1; - } - - importer = razor_importer_new(); - - while (de = readdir(dir), de != NULL) { - len = strlen(de->d_name); - if (len < 5 || strcmp(de->d_name + len - 4, ".rpm") != 0) - continue; - snprintf(filename, sizeof filename, - "%s/%s", dirname, de->d_name); - rpm = razor_rpm_open(filename); - if (rpm == NULL) { - fprintf(stderr, - "failed to open rpm \"%s\"\n", filename); - continue; - } - if (razor_importer_add_rpm(importer, rpm)) { - fprintf(stderr, "couldn't import %s\n", filename); - break; - } - razor_rpm_close(rpm); - } - - if (de != NULL) { - razor_importer_destroy(importer); - return -1; - } - - set = razor_importer_finish(importer); - - razor_set_write(set, repo_filename); - razor_set_destroy(set); - printf("wrote %s\n", repo_filename); - - return 0; -} - -static void -download_package(const char *name, - const char *old_version, - const char *new_version, - const char *arch, - void *data) -{ - char file[PATH_MAX], url[256]; - const char *v; - int *errors = data; - - if (old_version) - return; - - /* Skip epoch */ - v = strchr(new_version, ':'); - if (v != NULL) - v = v + 1; - else - v = new_version; - - snprintf(url, sizeof url, - "%s/Packages/%s-%s.%s.rpm", yum_url, name, v, arch); - snprintf(file, sizeof file, - "rpms/%s-%s.%s.rpm", name, v, arch); - if (download_if_missing(url, file) < 0) - (*errors)++; -} - -static void -install_package(const char *name, - const char *old_version, - const char *new_version, - const char *arch, - void *data) -{ - const char *v, *root = data; - char file[PATH_MAX]; - struct razor_rpm *rpm; - - if (old_version) { - printf("removing %s %s not handled\n", name, old_version); - return; - } - - /* Skip epoch */ - v = strchr(new_version, ':'); - if (v != NULL) - v = v + 1; - else - v = new_version; - - printf("install %s %s\n", name, v); - snprintf(file, sizeof file, "rpms/%s-%s.%s.rpm", name, v, arch); - - rpm = razor_rpm_open(file); - if (rpm == NULL) { - fprintf(stderr, "failed to open rpm %s\n", file); - return; - } - if (razor_rpm_install(rpm, root) < 0) { - fprintf(stderr, - "failed to install rpm %s\n", file); - return; - } - razor_rpm_close(rpm); -} - -static int -command_install(int argc, const char *argv[]) -{ - struct razor_root *root; - struct razor_set *upstream, *next; - struct razor_transaction *trans; - int i = 0, errors, dependencies = 1; - - if (i < argc && strcmp(argv[i], "--no-dependencies") == 0) { - dependencies = 0; - i++; - } - - root = razor_root_open(install_root, RAZOR_ROOT_OPEN_WRITE); - upstream = razor_set_open(rawhide_repo_filename); - trans = razor_root_create_transaction(root, upstream); - - for (; i < argc; i++) { - if (mark_packages_for_update(trans, upstream, argv[i]) == 0) { - fprintf(stderr, "no package matched %s\n", argv[i]); - razor_root_close(root); - return 1; - } - } - - if (dependencies) { - errors = razor_transaction_resolve(trans); - if (errors) { - razor_root_close(root); - return 1; - } - } - - next = razor_transaction_finish(trans); - - razor_root_update(root, next); - - if (mkdir("rpms", 0777) && errno != EEXIST) { - fprintf(stderr, "failed to create rpms directory.\n"); - razor_root_close(root); - return 1; - } - - razor_root_diff(root, download_package, &errors); - if (errors > 0) { - fprintf(stderr, "failed to download %d packages\n", errors); - razor_root_close(root); - return 1; - } - - /* FIXME: We need to figure out the right install order here, - * so the post and pre scripts can run. */ - razor_root_diff(root, install_package, (void *) root); - - razor_set_destroy(next); - razor_set_destroy(upstream); - - return razor_root_commit(root); -} - -static int -command_init(int argc, const char *argv[]) -{ - return razor_root_create(install_root); -} - -static int -command_download(int argc, const char *argv[]) -{ - struct razor_set *set; - struct razor_package_iterator *pi; - struct razor_package *package; - const char *pattern = argv[0], *name, *version, *arch; - char url[256], file[256]; - int matches = 0; - - if (mkdir("rpms", 0777) && errno != EEXIST) { - fprintf(stderr, "failed to create rpms directory.\n"); - return 1; - } - - set = razor_set_open(rawhide_repo_filename); - pi = razor_package_iterator_create(set); - while (razor_package_iterator_next(pi, &package, - &name, &version, &arch)) { - if (pattern && fnmatch(pattern, name, 0) != 0) - continue; - - matches++; - snprintf(url, sizeof url, - "%s/Packages/%s-%s.%s.rpm", - yum_url, name, version, arch); - snprintf(file, sizeof file, - "rpms/%s-%s.%s.rpm", name, version, arch); - download_if_missing(url, file); - } - razor_package_iterator_destroy(pi); - razor_set_destroy(set); - - if (matches == 0) - fprintf(stderr, "no packages matched \"%s\"\n", pattern); - else if (matches == 1) - fprintf(stderr, "downloaded 1 package\n"); - else - fprintf(stderr, "downloaded %d packages\n", matches); - - return 0; -} - -static struct { - const char *name; - const char *description; - int (*func)(int argc, const char *argv[]); -} razor_commands[] = { - { "list", "list all packages", command_list }, - { "list-requires", "list all requires for the given package", command_list_requires }, - { "list-provides", "list all provides for the given package", command_list_provides }, - { "list-obsoletes", "list all obsoletes for the given package", command_list_obsoletes }, - { "list-conflicts", "list all conflicts for the given package", command_list_conflicts }, - { "list-files", "list files for package set", command_list_files }, - { "list-file-packages", "list packages owning file", command_list_file_packages }, - { "list-package-files", "list files in package", command_list_package_files }, - { "what-requires", "list the packages that have the given requires", command_what_requires }, - { "what-provides", "list the packages that have the given provides", command_what_provides }, - { "import-yum", "import yum metadata files", command_import_yum }, - { "import-rpmdb", "import the system rpm database", command_import_rpmdb }, - { "import-rpms", "import rpms from the given directory", command_import_rpms }, - { "update", "update all or specified packages", command_update }, - { "remove", "remove specified packages", command_remove }, - { "diff", "show diff between two package sets", command_diff }, - { "install", "install rpm", command_install }, - { "init", "init razor root", command_init }, - { "download", "download packages", command_download } -}; - -static int -usage(void) -{ - int i; - - printf("usage:\n"); - for (i = 0; i < ARRAY_SIZE(razor_commands); i++) - printf(" %-20s%s\n", - razor_commands[i].name, razor_commands[i].description); - - return 1; -} - -int -main(int argc, const char *argv[]) -{ - char *repo; - int i; - - repo = getenv("RAZOR_REPO"); - if (repo != NULL) - repo_filename = repo; - - yum_url = getenv("YUM_URL"); - if (yum_url == NULL) - yum_url = YUM_URL; - - if (argc < 2) - return usage(); - - for (i = 0; i < ARRAY_SIZE(razor_commands); i++) - if (strcmp(razor_commands[i].name, argv[1]) == 0) - return razor_commands[i].func(argc - 2, argv + 2); - - return usage(); -} diff -r edd5fe0a00ba -r c3eb520e2219 po/.gitignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/po/.gitignore Mon Jun 16 15:40:30 2008 -0400 @@ -0,0 +1,4 @@ +POTFILES +Makefile.in.in +stamp-it + diff -r edd5fe0a00ba -r c3eb520e2219 razor-internal.h --- a/razor-internal.h Sun Jun 15 23:15:59 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -#ifndef _RAZOR_INTERNAL_H_ -#define _RAZOR_INTERNAL_H_ - -#define ALIGN(value, base) (((value) + (base - 1)) & ~((base) - 1)) - -/* 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 -r edd5fe0a00ba -r c3eb520e2219 razor-root.c --- a/razor-root.c Sun Jun 15 23:15:59 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,168 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "razor.h" -#include "razor-internal.h" - -static const char system_repo_filename[] = "system.repo"; -static const char next_repo_filename[] = "system-next.repo"; -static const char razor_root_path[] = "/var/lib/razor"; - -struct razor_root { - struct razor_set *system; - struct razor_set *next; - int fd; - char path[PATH_MAX]; - char new_path[PATH_MAX]; -}; - -int -razor_root_create(const char *root) -{ - struct stat buf; - struct razor_set *set; - char path[PATH_MAX]; - - if (stat(root, &buf) < 0) { - if (mkdir(root, 0777) < 0) { - fprintf(stderr, - "could not create install root \"%s\"\n", - root); - return -1; - } - fprintf(stderr, "created install root \"%s\"\n", root); - } else if (!S_ISDIR(buf.st_mode)) { - fprintf(stderr, - "install root \"%s\" exists, but is not a directory\n", - root); - return -1; - } - - snprintf(path, sizeof path, "%s/%s", - razor_root_path, system_repo_filename); - if (razor_create_dir(root, path) < 0) { - fprintf(stderr, "could not create %s%s\n", - root, razor_root_path); - return -1; - } - - set = razor_set_create(); - snprintf(path, sizeof path, "%s%s/%s", - root, razor_root_path, system_repo_filename); - if (stat(path, &buf) == 0) { - fprintf(stderr, - "a razor install root is already initialized\n"); - return -1; - } - if (razor_set_write(set, path) < 0) { - 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, root, 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); - 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 -r edd5fe0a00ba -r c3eb520e2219 razor.c --- a/razor.c Sun Jun 15 23:15:59 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2611 +0,0 @@ -/* - * 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 "razor.h" -#include "razor-internal.h" -#include "types.h" - -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_VERSION 1 - -#define RAZOR_STRING_POOL 0 -#define RAZOR_PACKAGES 1 -#define RAZOR_PROPERTIES 2 -#define RAZOR_FILES 3 -#define RAZOR_PACKAGE_POOL 4 -#define RAZOR_PROPERTY_POOL 5 -#define RAZOR_FILE_POOL 6 - -struct razor_package { - uint name : 24; - uint flags : 8; - uint32_t version; - uint32_t arch; - struct list_head properties; - struct list_head files; -}; - -struct razor_property { - uint name : 24; - uint flags : 6; - enum razor_property_type type : 2; - enum razor_version_relation relation : 32; - uint32_t version; - struct list_head packages; -}; - -struct razor_entry { - uint name : 24; - uint 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 razor_set_header *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 razor_package *package; - struct array properties; - struct array files; - struct array file_requires; -}; - -static void * -zalloc(size_t size) -{ - void *p; - - p = malloc(size); - memset(p, 0, size); - - return p; -} - -struct razor_set_section razor_sections[] = { - { RAZOR_STRING_POOL, offsetof(struct razor_set, string_pool) }, - { RAZOR_PACKAGES, offsetof(struct razor_set, packages) }, - { RAZOR_PROPERTIES, offsetof(struct razor_set, properties) }, - { RAZOR_FILES, offsetof(struct razor_set, files) }, - { RAZOR_PACKAGE_POOL, offsetof(struct razor_set, package_pool) }, - { RAZOR_PROPERTY_POOL, offsetof(struct razor_set, property_pool) }, - { RAZOR_FILE_POOL, offsetof(struct razor_set, file_pool) }, -}; - -struct razor_set * -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_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); - } - } - - free(set); -} - -int -razor_set_write_to_fd(struct razor_set *set, int fd) -{ - char data[4096]; - struct razor_set_header *header = (struct razor_set_header *) data; - struct array *a; - uint32_t offset; - int i; - - memset(data, 0, sizeof data); - header->magic = RAZOR_MAGIC; - header->version = RAZOR_VERSION; - offset = sizeof data; - - for (i = 0; i < ARRAY_SIZE(razor_sections); i++) { - if (razor_sections[i].type != i) - continue; - a = (void *) set + razor_sections[i].offset; - header->sections[i].type = i; - header->sections[i].offset = offset; - header->sections[i].size = a->size; - offset += ALIGN(a->size, 4096); - } - - header->sections[i].type = ~0; - header->sections[i].offset = 0; - header->sections[i].size = 0; - - razor_write(fd, data, sizeof data); - memset(data, 0, sizeof data); - for (i = 0; i < ARRAY_SIZE(razor_sections); i++) { - if (razor_sections[i].type != i) - continue; - a = (void *) set + razor_sections[i].offset; - razor_write(fd, a->data, a->size); - razor_write(fd, data, ALIGN(a->size, 4096) - a->size); - } - - return 0; -} - -int -razor_set_write(struct razor_set *set, const char *filename) -{ - 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); - 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); -} - -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_property(struct razor_importer *importer, - const char *name, - enum razor_version_relation relation, - const char *version, - enum razor_property_type type) -{ - struct razor_property *p; - uint32_t *r; - - p = array_add(&importer->set->properties, sizeof *p); - p->name = hashtable_tokenize(&importer->table, name); - p->flags = 0; - p->type = type; - p->relation = relation; - 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 (type == 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); - - return importer; -} - -/* Destroy an importer without creating the set. */ -void -razor_importer_destroy(struct razor_importer *importer) -{ - /* FIXME: write this */ -} - -static int -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 versioncmp(p1, p2); - } - - return *p1 - *p2; -} - -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 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->type != prop2->type) - return prop1->type - prop2->type; - else if (prop1->relation != prop2->relation) - return prop1->relation - prop2->relation; - else - return 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->type != up->type || - rp->relation != up->relation || rp->version != up->version) { - up++; - up->name = rp->name; - up->flags = 0; - up->type = rp->type; - up->relation = rp->relation; - 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 struct razor_entry * -find_entry(struct razor_set *set, struct razor_entry *dir, const char *pattern); - -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 = 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->type = RAZOR_PROPERTY_PROVIDES; - prop->relation = RAZOR_VERSION_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; -} - -struct razor_package_iterator { - struct razor_set *set; - struct razor_package *package, *end; - struct list *index; - int free_index; -}; - -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; -} - -static 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); -} - -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_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; -} - -struct razor_property_iterator { - struct razor_set *set; - struct razor_property *property, *end; - struct list *index; -}; - -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, - enum razor_version_relation *relation, - const char **version, - enum razor_property_type *type) -{ - 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]; - *relation = p->relation; - *version = &pool[p->version]; - *type = p->type; - } else { - *property = NULL; - } - - return valid; -} - -void -razor_property_iterator_destroy(struct razor_property_iterator *pi) -{ - free(pi); -} - -static struct razor_entry * -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 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 = 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 = find_entry(set, set->files.data, buffer); - if (e->start != 0) - list_dir(set, e, buffer, base); -} - -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 = 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); -} - -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); -} - -#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; -}; - -static 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; -} - -static 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, enum razor_version_relation relation, - const char *version, int type) -{ - struct razor_property *p; - - p = array_add(&merger->set->properties, sizeof *p); - p->name = hashtable_tokenize(&merger->table, name); - p->flags = 0; - p->type = type; - p->relation = relation; - 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->type - p2->type; - if (cmp == 0) - cmp = p1->relation - p2->relation; - if (cmp == 0) - cmp = versioncmp(&pool1[p1->version], - &pool2[p2->version]); - if (cmp < 0) { - map1[i++] = add_property(merger, - &pool1[p1->name], - p1->relation, - &pool1[p1->version], - p1->type); - } else if (cmp > 0) { - map2[j++] = add_property(merger, - &pool2[p2->name], - p2->relation, - &pool2[p2->version], - p2->type); - } else { - map1[i++] = map2[j++] = add_property(merger, - &pool1[p1->name], - p1->relation, - &pool1[p1->version], - p1->type); - } - } -} - -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); -} - -static 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; -} - -/* 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 = 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); -} - -static int -provider_satisfies_requirement(struct razor_property *provider, - const char *provider_strings, - enum razor_version_relation relation, - const char *required) -{ - int cmp, len; - const char *provided = &provider_strings[provider->version]; - - if (!*required) - return 1; - if (!*provided) { - if (relation >= RAZOR_VERSION_EQUAL) - return 1; - else - return 0; - } - - cmp = versioncmp(provided, required); - - switch (relation) { - case RAZOR_VERSION_LESS: - return cmp < 0; - - case RAZOR_VERSION_LESS_OR_EQUAL: - if (cmp <= 0) - return 1; - /* fall through: FIXME, make sure this is correct */ - - case RAZOR_VERSION_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_VERSION_GREATER_OR_EQUAL: - return cmp >= 0; - - case RAZOR_VERSION_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, - enum razor_property_type type, struct razor_property **p) -{ - while (pi->p < pi->end) { - if ((pi->present[pi->p - pi->start] & ~TRANS_PROPERTY_SATISFIED) && - pi->p->type == type) { - *p = pi->p++; - return 1; - } - pi->p++; - } - - return 0; -} - -static struct razor_property * -prop_iter_seek_to(struct prop_iter *pi, - enum razor_property_type type, 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->type != type) - 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, - enum razor_version_relation relation, - 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; - - if (ppi->present == trans->system.properties) - set = trans->system.set; - else - set = trans->upstream.set; - - pkgs = (struct razor_package *) set->packages.data; - for (p = ppi->p; - p < ppi->end && - p->name == ppi->p->name && - p->type == ppi->p->type; - p++) { - if (!ppi->present[p - ppi->start]) - continue; - if (!provider_satisfies_requirement(p, ppi->pool, - relation, 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; - - 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; - for (p = ppi->p; - p < ppi->end && - p->name == ppi->p->name && - p->type == ppi->p->type; - p++) { - if (!ppi->present[p - ppi->start]) - continue; - if (!provider_satisfies_requirement(p, ppi->pool, - r->relation, - &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, - enum razor_version_relation relation, - const char *version) -{ - struct razor_property *p; - struct razor_package *pkgs; - struct list *i; - - /* 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; - for (p = ppi->p; - p < ppi->end && - p->name == ppi->p->name && - p->type == ppi->p->type && - ppi->present[p - ppi->start] == 0; - p++) { - if (!provider_satisfies_requirement(p, ppi->pool, - relation, 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->relation, - &upi.pool[up->version]); - } -} - -static int -any_provider_satisfies_requirement(struct prop_iter *ppi, - enum razor_version_relation relation, - const char *version) -{ - struct razor_property *p; - - for (p = ppi->p; - p < ppi->end && - p->name == ppi->p->name && - p->type == ppi->p->type; - p++) { - if (ppi->present[p - ppi->start] > 0 && - provider_satisfies_requirement(p, ppi->pool, - relation, 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; - } -} - -static const char *relation_string[] = { "<", "<=", "=", ">=", ">" }; - -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->relation, - &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, - relation_string[sp->relation], - 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->relation, - &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->relation, - &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, - relation_string[pp->relation], - ppi->pool + pp->version, - &rpi->pool[rp->name], - relation_string[rp->relation], - &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_VERSION_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)) - continue; - remove_matching_providers(trans, &spi, - RAZOR_VERSION_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); - - 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], - relation_string[rp->relation], - &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, - enum razor_version_relation rel, - const char *version, - enum razor_property_type type) -{ - struct prop_iter pi; - struct razor_property *p; - - prop_iter_init(&pi, &trans->system); - while (prop_iter_next(&pi, type, &p)) { - if (!(trans->system.properties[p - pi.start] & TRANS_PROPERTY_SATISFIED) && - p->relation == rel && - 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, type, &p)) { - if (!(trans->upstream.properties[p - pi.start] & TRANS_PROPERTY_SATISFIED) && - p->relation == rel && - 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); -} - -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 -r edd5fe0a00ba -r c3eb520e2219 razor.h --- a/razor.h Sun Jun 15 23:15:59 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,189 +0,0 @@ -/* - * 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. - */ - -#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_property_type { - RAZOR_PROPERTY_REQUIRES, - RAZOR_PROPERTY_PROVIDES, - RAZOR_PROPERTY_CONFLICTS, - RAZOR_PROPERTY_OBSOLETES -}; - -enum razor_version_relation { - RAZOR_VERSION_LESS, - RAZOR_VERSION_LESS_OR_EQUAL, - RAZOR_VERSION_EQUAL, - RAZOR_VERSION_GREATER_OR_EQUAL, - RAZOR_VERSION_GREATER -}; -extern const char * const razor_version_relations[]; - -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); -int razor_set_write(struct razor_set *set, const char *filename); - -struct razor_package * -razor_set_get_package(struct razor_set *set, const char *package); - -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, - enum razor_version_relation *relation, - const char **version, - enum razor_property_type *type); -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, - enum razor_version_relation rel, - const char *version, - enum razor_property_type type); - -/* 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_property(struct razor_importer *importer, - const char *name, - enum razor_version_relation relation, - const char *version, - enum razor_property_type type); -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); - -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 -r edd5fe0a00ba -r c3eb520e2219 rpm-razor.c --- a/rpm-razor.c Sun Jun 15 23:15:59 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,897 +0,0 @@ -/* - * 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. - */ - -#include -#include -#include -#include -#include "razor.h" - -enum option_type { - OPTION_LAST, - OPTION_GROUP, - OPTION_BOOL, - OPTION_STRING -}; - -struct option { - enum option_type type; - const char *name; - char short_name; - const char *arg_name; - const char *description; - void *data; -}; - -/* A note about all these options: rpm allows options to mean - * different things depending on what other options are present on the - * command line. For example, if -q or --query is present, -i no - * longer means install, but info. The way we handle this is by - * setting all the options that may match (ie if -i is given we set - * install and info), and then look at the relevent one depending on - * what else in on the command line. */ - -static int option_all, option_whatrequires, option_whatprovides; -static int option_package; - -static const struct option query_options[] = { - { OPTION_BOOL, "configfiles", 'c', NULL, "list all configuration files", NULL }, - { OPTION_BOOL, "docfiles", 'd', NULL, "list all documentation files", NULL }, - { OPTION_BOOL, "dump", 0, NULL, "dump basic file information", NULL }, - { OPTION_BOOL, "list", 0, NULL, "list files in package", NULL }, - { OPTION_STRING, "queryformat", 0, "QUERYFORMAT", "use the following query format", NULL }, - { OPTION_BOOL, "state", 's', NULL, "display the states of the listed files", NULL }, - { OPTION_BOOL, "all", 'a', NULL, "query/verify all packages", &option_all }, - { OPTION_BOOL, "file", 'f', NULL, "query/verify package(s) owning file", NULL }, - { OPTION_BOOL, "group", 'g', NULL, "query/verify package(s) in group", NULL }, - { OPTION_BOOL, "package", 'p', NULL, "query/verify a package file", &option_package }, - { OPTION_BOOL, "ftswalk", 'W', NULL, "query/verify package(s) from TOP file tree walk", NULL }, - { OPTION_BOOL, "pkgid", 0, NULL, "query/verify package(s) with package identifier", NULL }, - { OPTION_BOOL, "hdrid", 0, NULL, "query/verify package(s) with header identifier", NULL }, - { OPTION_BOOL, "fileid", 0, NULL, "query/verify package(s) with file identifier", NULL }, - { OPTION_BOOL, "specfile", 0, NULL, "query a spec file", NULL, }, - { OPTION_BOOL, "triggeredby", 0, NULL, "query the package(s) triggered by the package", NULL }, - { OPTION_BOOL, "whatrequires", 0, NULL, "query/verify the package(s) which require a dependency", &option_whatrequires }, - { OPTION_BOOL, "whatprovides", 0, NULL, "query/verify the package(s) which provide a dependency", &option_whatprovides }, - { OPTION_BOOL, "nomanifest", 0, NULL, "do not process non-package files as manifests", NULL }, - { } -}; - -static int option_nodeps; - -static const struct option verify_options[] = { - { OPTION_BOOL, "nomd5", 0, NULL, "don't verify MD5 digest of files", NULL }, - { OPTION_BOOL, "nofiles", 0, NULL, "don't verify files in package", NULL }, - { OPTION_BOOL, "nodeps", 0, NULL, "don't verify package dependencies", &option_nodeps }, - { OPTION_BOOL, "noscript", 0, NULL, "don't execute verify script(s)", NULL, }, - { OPTION_BOOL, "all", 'a', NULL, "query/verify all packages", &option_all }, - { OPTION_BOOL, "file", 'f', NULL, "query/verify package(s) owning file", NULL }, - { OPTION_BOOL, "group", 'g', NULL, "query/verify package(s) in group", NULL }, - { OPTION_BOOL, "package", 'p', NULL, "query/verify a package file", &option_package }, - { OPTION_BOOL, "ftswalk", 'W', NULL, "query/verify package(s) from TOP file tree walk", NULL }, - { OPTION_BOOL, "pkgid", 0, NULL, "query/verify package(s) with package identifier", NULL }, - { OPTION_BOOL, "hdrid", 0, NULL, "query/verify package(s) with header identifier", NULL }, - { OPTION_BOOL, "fileid", 0, NULL, "query/verify package(s) with file identifier", NULL }, - { OPTION_BOOL, "specfile", 0, NULL, "query a spec file", NULL }, - { OPTION_BOOL, "triggeredby", 0, NULL, "query the package(s) triggered by the package", NULL }, - { OPTION_BOOL, "whatrequires", 0, NULL, "query/verify the package(s) which require a dependency", &option_whatrequires }, - { OPTION_BOOL, "whatprovides", 0, NULL, "query/verify the package(s) which provide a dependency", &option_whatprovides }, - { OPTION_BOOL, "nomanifest", 0, NULL, "do not process non-package files as manifests", NULL }, - { } -}; - -static const struct option ftw_options[] = { - { OPTION_BOOL, "comfollow", 0, NULL, "FTS_COMFOLLOW: follow command line symlinks", NULL }, - { OPTION_BOOL, "logical", 0, NULL, "FTS_LOGICAL: logical walk", NULL }, - { OPTION_BOOL, "nochdir", 0, NULL, "FTS_NOCHDIR: don't change directories", NULL }, - { OPTION_BOOL, "nostat", 0, NULL, "FTS_NOSTAT: don't get stat info", NULL }, - { OPTION_BOOL, "physical", 0, NULL, "FTS_PHYSICAL: physical walk", NULL }, - { OPTION_BOOL, "seedot", 0, NULL, "FTS_SEEDOT: return dot and dot-dot", NULL }, - { OPTION_BOOL, "xdev", 0, NULL, "FTS_XDEV: don't cross devices", NULL }, - { OPTION_BOOL, "whiteout", 0, NULL, "FTS_WHITEOUT: return whiteout information", NULL }, - { } -}; - -static const struct option signature_options[] = { - { OPTION_BOOL, "addsign", 0, NULL, "sign package(s) (identical to --resign)", NULL, }, - { OPTION_BOOL, "checksig", 'K', NULL, "verify package signature(s)", NULL, }, - { OPTION_BOOL, "delsign", 0, NULL, "delete package signatures", NULL, }, - { OPTION_BOOL, "import", 0, NULL, "import an armored public key", NULL, }, - { OPTION_BOOL, "resign", 0, NULL, "sign package(s) (identical to --addsign)", NULL, }, - { OPTION_BOOL, "nodigest", 0, NULL, "don't verify package digest(s)", NULL, }, - { OPTION_BOOL, "nosignature", 0, NULL, "don't verify package signature(s)", NULL }, - { } -}; - -static int option_initdb; - -static const struct option database_options[] = { - { OPTION_BOOL, "initdb", 0, NULL, "initialize database", &option_initdb }, - { OPTION_BOOL, "rebuilddb", 0, NULL, "rebuild database inverted lists from installed package headers", NULL }, - { } -}; - -static int option_erase, option_install, option_upgrade, option_justdb; -static int option_test; - -static const struct option install_options[] = { - { OPTION_BOOL, "aid", 0, NULL, "add suggested packages to transaction", NULL, }, - { OPTION_BOOL, "allfiles", 0, NULL, "install all files, even configurations which might otherwise be skipped", NULL, }, - { OPTION_BOOL, "allmatches", 0, NULL, "remove all packages which match (normally an error is generated if specified multiple packages)", NULL, }, - { OPTION_BOOL, "badreloc", 0, NULL, "relocate files in non-relocatable package", NULL }, - { OPTION_BOOL, "erase", 'e', "", "erase (uninstall) package", &option_erase }, - { OPTION_BOOL, "excludedocs", 0, NULL, "do not install documentation", NULL, }, - { OPTION_BOOL, "excludepath", 0, "", "skip files with leading component ", NULL, }, - { OPTION_BOOL, "fileconflicts", 0, NULL, "detect file conflicts between packages", NULL, }, - { OPTION_BOOL, "force", 0, NULL, "short hand for --replacepkgs --replacefiles", NULL }, - { OPTION_BOOL, "freshen", 'F', "+", "upgrade package(s) if already installed", NULL }, - { OPTION_BOOL, "hash", 'h', NULL, "print hash marks as package installs (good with -v)", NULL }, - { OPTION_BOOL, "ignorearch", 0, NULL, "don't verify package architecture", NULL, }, - { OPTION_BOOL, "ignoreos", 0, NULL, "don't verify package operating system", NULL, }, - { OPTION_BOOL, "ignoresize", 0, NULL, "don't check disk space before installing", NULL }, - { OPTION_BOOL, "install", 'i', NULL, "install package(s)", &option_install }, - { OPTION_BOOL, "justdb", 0, NULL, "update the database, but do not modify the filesystem", &option_justdb, }, - { OPTION_BOOL, "nodeps", 0, NULL, "do not verify package dependencies", &option_nodeps, }, - { OPTION_BOOL, "nomd5", 0, NULL, "don't verify MD5 digest of files", NULL, }, - { OPTION_BOOL, "nocontexts", 0, NULL, "don't install file security contexts", NULL, }, - { OPTION_BOOL, "noorder", 0, NULL, "do not reorder package installation to satisfy dependencies", NULL, }, - { OPTION_BOOL, "nosuggest", 0, NULL, "do not suggest missing dependency resolution(s)", NULL, }, - { OPTION_BOOL, "noscripts", 0, NULL, "do not execute package scriptlet(s)", NULL, }, - { OPTION_BOOL, "notriggers", 0, NULL, "do not execute any scriptlet(s) triggered by this package", NULL, }, - { OPTION_BOOL, "oldpackage", 0, NULL, "upgrade to an old version of the package (--force on upgrades does this automatically)", NULL }, - { OPTION_BOOL, "percent", 0, NULL, "print percentages as package installs", NULL, }, - { OPTION_STRING, "prefix", 0, "", "relocate the package to , if relocatable", NULL, }, - { OPTION_STRING, "relocate", 0, "=", "relocate files from path to ", NULL, }, - { OPTION_BOOL, "repackage", 0, NULL, "save erased package files by repackaging", NULL, }, - { OPTION_BOOL, "replacefiles", 0, NULL, "ignore file conflicts between packages", NULL, }, - { OPTION_BOOL, "replacepkgs", 0, NULL, "reinstall if the package is already present", NULL, }, - { OPTION_BOOL, "test", 0, NULL, "don't install, but tell if it would work or not", &option_test }, - { OPTION_BOOL, "upgrade", 'U', "+", "upgrade package(s)", &option_upgrade }, - { } -}; - -static int option_version; -static const char *option_root = "install"; - -static const struct option common_options[] = { - { OPTION_STRING, "define", 'D', "MACRO EXPR", "define MACRO with value EXPR", NULL, }, - { OPTION_STRING, "eval", 'E', "EXPR", "print macro expansion of EXPR", NULL }, - { OPTION_STRING, "macros", 0, "", "read instead of default file(s)", NULL }, - { OPTION_BOOL, "nodigest", 0, NULL, "don't verify package digest(s)", NULL, }, - { OPTION_BOOL, "nosignature", 0, NULL, "don't verify package signature(s)", NULL, }, - { OPTION_STRING, "rcfile", 0, "", "read instead of default file(s)", NULL }, - { OPTION_STRING, "root", 'r', "ROOT", "use ROOT as top level directory (default: \"/\")", &option_root }, - { OPTION_BOOL, "querytags", 0, NULL, "display known query tags", NULL, }, - { OPTION_BOOL, "showrc", 0, NULL, "display final rpmrc and macro configuration", NULL, }, - { OPTION_BOOL, "quiet", 0, NULL, "provide less detailed output", NULL }, - { OPTION_BOOL, "verbose", 'v', NULL, "provide more detailed output", NULL }, - { OPTION_BOOL, "version", 0, NULL, "print the version of rpm being used", &option_version }, - { } -}; - -static int option_conflicts, option_obsoletes, option_requires; -static int option_provides, option_info, option_changelog; - -static const struct option alias_options[] = { - { OPTION_BOOL, "scripts", 0, NULL, "list install/erase scriptlets from package(s)", NULL, }, - { OPTION_BOOL, "setperms", 0, NULL, "set permissions of files in a package", NULL, }, - { OPTION_BOOL, "setugids", 0, NULL, "set user/group ownership of files in a package", NULL, }, - { OPTION_BOOL, "conflicts", 0, NULL, "list capabilities this package conflicts with", &option_conflicts, }, - { OPTION_BOOL, "obsoletes", 0, NULL, "list other packages removed by installing this package", &option_obsoletes, }, - { OPTION_BOOL, "provides", 0, NULL, "list capabilities that this package provides", &option_provides, }, - { OPTION_BOOL, "requires", 0, NULL, "list capabilities required by package(s)", &option_requires, }, - { OPTION_BOOL, "info", 'i', NULL, "list descriptive information from package(s)", &option_info, }, - { OPTION_BOOL, "changelog", 0, NULL, "list change logs for this package", &option_changelog, }, - { OPTION_BOOL, "xml", 0, NULL, "list metadata in xml", NULL, }, - { OPTION_BOOL, "triggers", 0, NULL, "list trigger scriptlets from package(s)", NULL, }, - { OPTION_BOOL, "last", 0, NULL, "list package(s) by install time, most recent first", NULL, }, - { OPTION_BOOL, "dupes", 0, NULL, "list duplicated packages", NULL, }, - { OPTION_BOOL, "filesbypkg", 0, NULL, "list all files from each package", NULL, }, - { OPTION_BOOL, "fileclass", 0, NULL, "list file names with classes", NULL, }, - { OPTION_BOOL, "filecolor", 0, NULL, "list file names with colors", NULL, }, - { OPTION_BOOL, "filecontext", 0, NULL, "list file names with security context from header", NULL, }, - { OPTION_BOOL, "fscontext", 0, NULL, "list file names with security context from file system", NULL, }, - { OPTION_BOOL, "recontext", 0, NULL, "list file names with security context from policy RE", NULL, }, - { OPTION_BOOL, "fileprovide", 0, NULL, "list file names with provides", NULL, }, - { OPTION_BOOL, "filerequire", 0, NULL, "list file names with requires", NULL, }, - { OPTION_BOOL, "redhatprovides", 0, NULL, "find package name that contains a provided capability (needs rpmdb-redhat package installed)", NULL, }, - { OPTION_BOOL, "redhatrequires", 0, NULL, "find package name that contains a required capability (needs rpmdb-redhat package installed)", NULL, }, - { OPTION_STRING, "buildpolicy", 0, "", "set buildroot (e.g. compress man pages)", NULL, }, - { OPTION_BOOL, "with", 0, "