diff --git a/NEWS b/NEWS index eefae9ef..8e101d79 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,10 @@ xbps-0.25 (???): + * Added support to validate reverse dependencies when updating packages. + Such example would be updating pkg X to 2.0 when an installed package + depends on pkg X < 2.0; abort transaction when such condition is + detected; this also works with virtual packages. + * Dropped requirement of confuse when using the libxbps API. * Dropped support for external libfetch and proplib; the included copies diff --git a/bin/xbps-install/transaction.c b/bin/xbps-install/transaction.c index 4524a828..7c6a0f3d 100644 --- a/bin/xbps-install/transaction.c +++ b/bin/xbps-install/transaction.c @@ -51,10 +51,9 @@ show_missing_deps(xbps_array_t a) unsigned int i; const char *str; - fprintf(stderr, "Unable to locate some required packages:\n"); for (i = 0; i < xbps_array_count(a); i++) { xbps_array_get_cstring_nocopy(a, i, &str); - fprintf(stderr, " %s\n", str); + fprintf(stderr, "%s\n", str); } } @@ -64,10 +63,9 @@ show_conflicts(xbps_array_t a) unsigned int i; const char *str; - fprintf(stderr, "Conflicting packages were found:\n"); for (i = 0; i < xbps_array_count(a); i++) { xbps_array_get_cstring_nocopy(a, i, &str); - fprintf(stderr, " %s\n", str); + fprintf(stderr, "%s\n", str); } } @@ -272,11 +270,13 @@ exec_transaction(struct xbps_handle *xhp, int maxcols, bool yes, bool drun) xbps_dictionary_get(xhp->transd, "missing_deps"); /* missing packages */ show_missing_deps(mdeps); + fprintf(stderr, "Transaction aborted due to missing/conflicting packages.\n"); goto out; } else if (rv == EAGAIN) { /* conflicts */ cflicts = xbps_dictionary_get(xhp->transd, "conflicts"); show_conflicts(cflicts); + fprintf(stderr, "Transaction aborted due to missing/conflicting packages.\n"); goto out; } xbps_dbg_printf(xhp, "Empty transaction dictionary: %s\n", @@ -324,6 +324,5 @@ out: xbps_object_iterator_release(trans->iter); if (trans) free(trans); - return rv; } diff --git a/include/xbps_api_impl.h b/include/xbps_api_impl.h index 3d978a73..4b701996 100644 --- a/include/xbps_api_impl.h +++ b/include/xbps_api_impl.h @@ -176,6 +176,12 @@ xbps_dictionary_t HIDDEN xbps_find_pkg_in_dict(xbps_dictionary_t, const char *); xbps_dictionary_t HIDDEN xbps_find_virtualpkg_in_dict(struct xbps_handle *, xbps_dictionary_t, const char *); +/** + * @private + * From lib/transaction_revdeps.c + */ +void HIDDEN xbps_transaction_revdeps(struct xbps_handle *); + /** * @private * From lib/transaction_sortdeps.c diff --git a/lib/Makefile b/lib/Makefile index 329a0546..b8772260 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -38,6 +38,7 @@ OBJS += package_remove.o package_find_obsoletes.o package_state.o OBJS += package_unpack.o package_register.o package_script.o OBJS += transaction_commit.o transaction_package_replace.o OBJS += transaction_dictionary.o transaction_sortdeps.o transaction_ops.o +OBJS += transaction_revdeps.o OBJS += download.o initend.o pkgdb.o package_conflicts.o OBJS += plist.o plist_find.o plist_match.o archive.o OBJS += plist_remove.o plist_fetch.o util.o util_hash.o diff --git a/lib/package_conflicts.c b/lib/package_conflicts.c index e44ea6e5..b257e9ba 100644 --- a/lib/package_conflicts.c +++ b/lib/package_conflicts.c @@ -75,7 +75,7 @@ xbps_pkg_find_conflicts(struct xbps_handle *xhp, xbps_dbg_printf(xhp, "found conflicting installed " "pkg %s with pkg in transaction %s\n", pkgver, repopkgver); - buf = xbps_xasprintf("%s conflicts with " + buf = xbps_xasprintf("CONFLICT: %s with " "installed pkg %s", repopkgver, pkgver); xbps_array_add_cstring(trans_cflicts, buf); free(buf); @@ -97,7 +97,7 @@ xbps_pkg_find_conflicts(struct xbps_handle *xhp, free(pkgname); xbps_dbg_printf(xhp, "found conflicting pkgs in " "transaction %s <-> %s\n", pkgver, repopkgver); - buf = xbps_xasprintf("%s conflicts with " + buf = xbps_xasprintf("CONFLICT: %s with " "%s in transaction", repopkgver, pkgver); xbps_array_add_cstring(trans_cflicts, buf); free(buf); diff --git a/lib/repo_pkgdeps.c b/lib/repo_pkgdeps.c index 4917c139..4eb9949d 100644 --- a/lib/repo_pkgdeps.c +++ b/lib/repo_pkgdeps.c @@ -135,9 +135,12 @@ out: xbps_object_iterator_release(iter); if (update_pkgdep) xbps_array_remove(mdeps, idx); - if (add_pkgdep && !xbps_add_obj_to_array(mdeps, reqpkg_str)) { - xbps_object_release(reqpkg_str); - return errno; + if (add_pkgdep) { + char *str; + + str = xbps_xasprintf("MISSING: %s", reqpkg_str); + xbps_array_add_cstring(mdeps, str); + free(str); } return rv; diff --git a/lib/transaction_dictionary.c b/lib/transaction_dictionary.c index 30a7e051..72aba74e 100644 --- a/lib/transaction_dictionary.c +++ b/lib/transaction_dictionary.c @@ -244,17 +244,18 @@ xbps_transaction_prepare(struct xbps_handle *xhp) return ENXIO; /* - * If there are missing deps bail out. + * If there are missing deps or revdeps bail out. */ + xbps_transaction_revdeps(xhp); mdeps = xbps_dictionary_get(xhp->transd, "missing_deps"); - if (xbps_array_count(mdeps) > 0) + if (xbps_array_count(mdeps)) return ENODEV; /* * If there are package conflicts bail out. */ conflicts = xbps_dictionary_get(xhp->transd, "conflicts"); - if (xbps_array_count(conflicts) > 0) + if (xbps_array_count(conflicts)) return EAGAIN; /* diff --git a/lib/transaction_revdeps.c b/lib/transaction_revdeps.c new file mode 100644 index 00000000..749c43c8 --- /dev/null +++ b/lib/transaction_revdeps.c @@ -0,0 +1,176 @@ +/*- + * Copyright (c) 2013 Juan Romero Pardines. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include + +#include "xbps_api_impl.h" + +/* + * Verify reverse dependencies for packages in transaction. + * This will catch cases where a package update would break its reverse dependencies: + * + * - foo-1.0 is being updated to 2.0. + * - baz-1.1 depends on foo<2.0. + * - foo is updated to 2.0, hence baz-1.1 is currently broken. + * + * Abort transaction if such case is found. + */ +static bool +check_virtual_pkgs(struct xbps_handle *xhp, + xbps_dictionary_t trans_pkgd, + xbps_dictionary_t rev_pkgd) +{ + xbps_array_t provides, rundeps, mdeps; + const char *pkgver, *revpkgver, *pkgpattern; + char *pkgname, *vpkgname, *vpkgver, *str; + unsigned int i, x; + bool matched = false; + + provides = xbps_dictionary_get(trans_pkgd, "provides"); + for (i = 0; i < xbps_array_count(provides); i++) { + char *tmp = NULL; + + xbps_array_get_cstring(provides, i, &vpkgver); + if (strchr(vpkgver, '_') == NULL) { + tmp = xbps_xasprintf("%s_1", vpkgver); + vpkgver = strdup(tmp); + } + vpkgname = xbps_pkg_name(vpkgver); + assert(vpkgname); + rundeps = xbps_dictionary_get(rev_pkgd, "run_depends"); + for (x = 0; x < xbps_array_count(rundeps); x++) { + xbps_array_get_cstring_nocopy(rundeps, x, &pkgpattern); + if (((pkgname = xbps_pkgpattern_name(pkgpattern)) == NULL) && + ((pkgname = xbps_pkg_name(pkgpattern)) == NULL)) + continue; + + if (strcmp(vpkgname, pkgname)) { + free(pkgname); + continue; + } + free(pkgname); + if (xbps_pkgpattern_match(vpkgver, pkgpattern)) + continue; + + mdeps = xbps_dictionary_get(xhp->transd, "missing_deps"); + xbps_dictionary_get_cstring_nocopy(trans_pkgd, "pkgver", &pkgver); + xbps_dictionary_get_cstring_nocopy(rev_pkgd, "pkgver", &revpkgver); + str = xbps_xasprintf("CONFLICT: `%s' update " + "breaks `%s', needs `%s' virtual pkg (got `%s`)", + pkgver, revpkgver, pkgpattern, vpkgver); + xbps_array_add_cstring(mdeps, str); + free(str); + matched = true; + } + free(vpkgver); + } + return matched; +} + +void HIDDEN +xbps_transaction_revdeps(struct xbps_handle *xhp) +{ + xbps_array_t mdeps, unsorted, pkgrdeps, rundeps; + xbps_dictionary_t revpkgd; + xbps_object_t obj; + const char *pkgver, *revpkgver, *curpkgver, *tract; + char *pkgname, *str; + unsigned int i, x; + + unsorted = xbps_dictionary_get(xhp->transd, "unsorted_deps"); + + for (i = 0; i < xbps_array_count(unsorted); i++) { + obj = xbps_array_get(unsorted, i); + /* + * Only check packages in transaction being updated. + */ + xbps_dictionary_get_cstring_nocopy(obj, "transaction", &tract); + if (strcmp(tract, "update")) + continue; + /* + * if pkg in transaction is not installed, + * pass to next one. + */ + xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); + pkgname = xbps_pkg_name(pkgver); + assert(pkgname); + if (xbps_pkg_is_installed(xhp, pkgname) == 0) { + free(pkgname); + continue; + } + /* + * If pkg is installed but does not have revdeps, + * pass to next one. + */ + pkgrdeps = xbps_pkgdb_get_pkg_revdeps(xhp, pkgname); + if (!xbps_array_count(pkgrdeps)) { + free(pkgname); + continue; + } + free(pkgname); + /* + * Time to validate revdeps for current pkg. + */ + for (x = 0; x < xbps_array_count(pkgrdeps); x++) { + xbps_array_get_cstring_nocopy(pkgrdeps, x, &curpkgver); + revpkgd = xbps_pkgdb_get_pkg(xhp, curpkgver); + /* + * First try to match any supported virtual package. + */ + if (check_virtual_pkgs(xhp, obj, revpkgd)) + continue; + /* + * Try to match real dependencies. + */ + rundeps = xbps_dictionary_get(revpkgd, "run_depends"); + if (xbps_match_pkgdep_in_array(rundeps, pkgver)) + continue; + /* + * Installed package conflicts with package + * in transaction being updated, check + * if a new version of this conflicting package + * is in the transaction. + */ + pkgname = xbps_pkg_name(curpkgver); + if (xbps_find_pkg_in_array(unsorted, pkgname)) { + free(pkgname); + continue; + } + free(pkgname); + mdeps = xbps_dictionary_get(xhp->transd, "missing_deps"); + xbps_dictionary_get_cstring_nocopy(revpkgd, + "pkgver", &revpkgver); + str = xbps_xasprintf("CONFLICT: `%s' " + "update breaks `%s', needs `%s'", + pkgver, revpkgver, curpkgver); + xbps_array_add_cstring(mdeps, str); + free(str); + } + + } +}