From e967bfd71a068d6c3f5831cfa93de92d78136065 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 16 Jun 2008 14:43:03 -0400 Subject: [PATCH] Autoconfify razor. --- .gitignore | 38 +- AUTHORS | 2 + DEPSOLVE.txt | 364 ------- Makefile | 27 - Makefile.am | 38 + REPO.txt | 172 --- autogen.sh | 95 ++ bash-completion.sh | 47 - configure.ac | 201 ++++ data/.gitignore | 2 + data/Makefile.am | 11 + data/bash-completion.sh | 47 + data/razor.pc.in | 12 + docs/DEPSOLVE.txt | 364 +++++++ docs/Makefile.am | 5 + docs/REPO.txt | 172 +++ librazor/.gitignore | 5 + librazor/Makefile.am | 34 + librazor/razor-internal.h | 19 + librazor/razor-root.c | 168 +++ librazor/razor.c | 2611 +++++++++++++++++++++++++++++++++++++++++++++ librazor/razor.h | 189 ++++ librazor/rpm.c | 813 ++++++++++++++ librazor/types.c | 246 +++++ librazor/types.h | 59 + librazor/util.c | 167 +++ main.c | 804 -------------- po/.gitignore | 4 + razor-internal.h | 19 - razor-root.c | 168 --- razor.c | 2611 --------------------------------------------- razor.h | 189 ---- rpm-razor.c | 897 ---------------- rpm.c | 748 ------------- src/.gitignore | 5 + src/Makefile.am | 26 + src/import-rpmdb.c | 157 +++ src/import-yum.c | 337 ++++++ src/main.c | 804 ++++++++++++++ src/rpm.c | 897 ++++++++++++++++ src/test-driver.c | 458 ++++++++ src/test.xml | 858 +++++++++++++++ test-driver.c | 458 -------- test.xml | 858 --------------- types.c | 246 ----- types.h | 59 - util.c | 167 --- yum.c | 337 ------ 48 files changed, 8836 insertions(+), 8179 deletions(-) create mode 100644 AUTHORS delete mode 100644 DEPSOLVE.txt delete mode 100644 Makefile create mode 100644 Makefile.am create mode 100644 NEWS delete mode 100644 REPO.txt create mode 100755 autogen.sh delete mode 100644 bash-completion.sh create mode 100644 configure.ac create mode 100644 contrib/razor.spec create mode 100644 data/.gitignore create mode 100644 data/Makefile.am create mode 100644 data/bash-completion.sh create mode 100644 data/razor.pc.in create mode 100644 docs/DEPSOLVE.txt create mode 100644 docs/Makefile.am create mode 100644 docs/REPO.txt create mode 100644 librazor/.gitignore create mode 100644 librazor/Makefile.am create mode 100644 librazor/razor-internal.h create mode 100644 librazor/razor-root.c create mode 100644 librazor/razor.c create mode 100644 librazor/razor.h create mode 100644 librazor/rpm.c create mode 100644 librazor/types.c create mode 100644 librazor/types.h create mode 100644 librazor/util.c delete mode 100644 main.c create mode 100644 po/.gitignore create mode 100644 po/LINGUAS create mode 100644 po/POTFILES.in delete mode 100644 razor-internal.h delete mode 100644 razor-root.c delete mode 100644 razor.c delete mode 100644 razor.h delete mode 100644 rpm-razor.c delete mode 100644 rpm.c create mode 100644 src/.gitignore create mode 100644 src/Makefile.am create mode 100644 src/import-rpmdb.c create mode 100644 src/import-yum.c create mode 100644 src/main.c create mode 100644 src/rpm.c create mode 100644 src/test-driver.c create mode 100644 src/test.xml delete mode 100644 test-driver.c delete mode 100644 test.xml delete mode 100644 types.c delete mode 100644 types.h delete mode 100644 util.c delete mode 100644 yum.c diff --git a/.gitignore b/.gitignore index e928d93..f3542fb 100644 --- a/.gitignore +++ b/.gitignore @@ -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 --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..a998399 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ +Kristian Høgsberg + diff --git a/DEPSOLVE.txt b/DEPSOLVE.txt deleted file mode 100644 index fce8698..0000000 --- a/DEPSOLVE.txt +++ /dev/null @@ -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 --git a/Makefile b/Makefile deleted file mode 100644 index 5e26373..0000000 --- a/Makefile +++ /dev/null @@ -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 --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..d54ddd4 --- /dev/null +++ b/Makefile.am @@ -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 --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/REPO.txt b/REPO.txt deleted file mode 100644 index 92a4f56..0000000 --- a/REPO.txt +++ /dev/null @@ -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 --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..347c3e1 --- /dev/null +++ b/autogen.sh @@ -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 --git a/bash-completion.sh b/bash-completion.sh deleted file mode 100644 index a4e9f4e..0000000 --- a/bash-completion.sh +++ /dev/null @@ -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 --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..a6559d4 --- /dev/null +++ b/configure.ac @@ -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 --git a/contrib/razor.spec b/contrib/razor.spec new file mode 100644 index 0000000..e69de29 diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 0000000..16bc098 --- /dev/null +++ b/data/.gitignore @@ -0,0 +1,2 @@ +razor.pc + diff --git a/data/Makefile.am b/data/Makefile.am new file mode 100644 index 0000000..9405c68 --- /dev/null +++ b/data/Makefile.am @@ -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 --git a/data/bash-completion.sh b/data/bash-completion.sh new file mode 100644 index 0000000..a4e9f4e --- /dev/null +++ b/data/bash-completion.sh @@ -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 --git a/data/razor.pc.in b/data/razor.pc.in new file mode 100644 index 0000000..ddf0ba0 --- /dev/null +++ b/data/razor.pc.in @@ -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 --git a/docs/DEPSOLVE.txt b/docs/DEPSOLVE.txt new file mode 100644 index 0000000..fce8698 --- /dev/null +++ b/docs/DEPSOLVE.txt @@ -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 --git a/docs/Makefile.am b/docs/Makefile.am new file mode 100644 index 0000000..cfefb5c --- /dev/null +++ b/docs/Makefile.am @@ -0,0 +1,5 @@ + +EXTRA_DIST = \ + DEPSOLVE.txt \ + REPO.txt + diff --git a/docs/REPO.txt b/docs/REPO.txt new file mode 100644 index 0000000..92a4f56 --- /dev/null +++ b/docs/REPO.txt @@ -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 --git a/librazor/.gitignore b/librazor/.gitignore new file mode 100644 index 0000000..7fe452b --- /dev/null +++ b/librazor/.gitignore @@ -0,0 +1,5 @@ +.deps +.libs +*.lo +*.la + diff --git a/librazor/Makefile.am b/librazor/Makefile.am new file mode 100644 index 0000000..0231424 --- /dev/null +++ b/librazor/Makefile.am @@ -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 --git a/librazor/razor-internal.h b/librazor/razor-internal.h new file mode 100644 index 0000000..f2a922f --- /dev/null +++ b/librazor/razor-internal.h @@ -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 --git a/librazor/razor-root.c b/librazor/razor-root.c new file mode 100644 index 0000000..12de470 --- /dev/null +++ b/librazor/razor-root.c @@ -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 --git a/librazor/razor.c b/librazor/razor.c new file mode 100644 index 0000000..c5b986b --- /dev/null +++ b/librazor/razor.c @@ -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 --git a/librazor/razor.h b/librazor/razor.h new file mode 100644 index 0000000..4f2cc05 --- /dev/null +++ b/librazor/razor.h @@ -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 --git a/librazor/rpm.c b/librazor/rpm.c new file mode 100644 index 0000000..c87de7f --- /dev/null +++ b/librazor/rpm.c @@ -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 --git a/librazor/types.c b/librazor/types.c new file mode 100644 index 0000000..adb5b89 --- /dev/null +++ b/librazor/types.c @@ -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 --git a/librazor/types.h b/librazor/types.h new file mode 100644 index 0000000..6e36754 --- /dev/null +++ b/librazor/types.h @@ -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 --git a/librazor/util.c b/librazor/util.c new file mode 100644 index 0000000..29a7716 --- /dev/null +++ b/librazor/util.c @@ -0,0 +1,167 @@ +#include +#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 --git a/main.c b/main.c deleted file mode 100644 index c8488e0..0000000 --- a/main.c +++ /dev/null @@ -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 --git a/po/.gitignore b/po/.gitignore new file mode 100644 index 0000000..7398a05 --- /dev/null +++ b/po/.gitignore @@ -0,0 +1,4 @@ +POTFILES +Makefile.in.in +stamp-it + diff --git a/po/LINGUAS b/po/LINGUAS new file mode 100644 index 0000000..e69de29 diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..e69de29 diff --git a/razor-internal.h b/razor-internal.h deleted file mode 100644 index f2a922f..0000000 --- a/razor-internal.h +++ /dev/null @@ -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 --git a/razor-root.c b/razor-root.c deleted file mode 100644 index fafbcc2..0000000 --- a/razor-root.c +++ /dev/null @@ -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 --git a/razor.c b/razor.c deleted file mode 100644 index b74f3dd..0000000 --- a/razor.c +++ /dev/null @@ -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 --git a/razor.h b/razor.h deleted file mode 100644 index 4f2cc05..0000000 --- a/razor.h +++ /dev/null @@ -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 --git a/rpm-razor.c b/rpm-razor.c deleted file mode 100644 index cb9edda..0000000 --- a/rpm-razor.c +++ /dev/null @@ -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, "