This patch adds support for ignoring specific version of packages in IgnorePkg. This is useful for ignoring a version that is known to be buggy where a fix is going to be included in the next version. Only equality comparisons (e.g. "foo=1.0-1" or "bar=1.0") are supported. Signed-off-by: Daniel Schoepe <daniel@schoepe.org> --- NEWS | 2 + doc/pacman.conf.5.txt | 4 +- lib/libalpm/alpm.h | 4 +- lib/libalpm/handle.c | 97 +++++++++++++++++++++++++----------------- lib/libalpm/package.c | 27 ++++++++++-- src/pacman/conf.c | 62 ++++++++++++++++++++------- test/pacman/tests/TESTS | 5 +++ test/pacman/tests/ignore009.py | 14 ++++++ test/pacman/tests/ignore010.py | 13 ++++++ test/pacman/tests/ignore011.py | 13 ++++++ test/pacman/tests/ignore012.py | 13 ++++++ test/pacman/tests/ignore013.py | 13 ++++++ 12 files changed, 205 insertions(+), 62 deletions(-) create mode 100644 test/pacman/tests/ignore009.py create mode 100644 test/pacman/tests/ignore010.py create mode 100644 test/pacman/tests/ignore011.py create mode 100644 test/pacman/tests/ignore012.py create mode 100644 test/pacman/tests/ignore013.py diff --git a/NEWS b/NEWS index a4d3dba..f42a185 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,8 @@ VERSION DESCRIPTION 5.0.0 - pacman can check the validity of the local and sync databases (-Dk and -Dkk respectively). This replaces the 'testdb' software + - pacman supports specific version bounds on packages in + IgnorePkg. 4.2.1 - Remove warnings about incorrect directory ownership until packaging files with dynamic users/groups is improved - Do not require a specific automake version when building from diff --git a/doc/pacman.conf.5.txt b/doc/pacman.conf.5.txt index 383e072..2e29ad0 100644 --- a/doc/pacman.conf.5.txt +++ b/doc/pacman.conf.5.txt @@ -92,7 +92,9 @@ Options *IgnorePkg =* package ...:: Instructs pacman to ignore any upgrades for this package when performing - a '\--sysupgrade'. Shell-style glob patterns are allowed. + a '\--sysupgrade'. Shell-style glob patterns are allowed. Specific + package versions can be ignored; for example 'foo=1.0' ignores only + updates to version 1.0 of foo. *IgnoreGroup =* group ...:: Instructs pacman to ignore any upgrades for all packages in this diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index 06e080b..34753b0 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -821,9 +821,9 @@ int alpm_option_match_noextract(alpm_handle_t *handle, const char *path); * @{ */ alpm_list_t *alpm_option_get_ignorepkgs(alpm_handle_t *handle); -int alpm_option_add_ignorepkg(alpm_handle_t *handle, const char *pkg); +int alpm_option_add_ignorepkg(alpm_handle_t *handle, const alpm_depend_t *pkg); int alpm_option_set_ignorepkgs(alpm_handle_t *handle, alpm_list_t *ignorepkgs); -int alpm_option_remove_ignorepkg(alpm_handle_t *handle, const char *pkg); +int alpm_option_remove_ignorepkg(alpm_handle_t *handle, const alpm_depend_t *pkg); /** @} */ /** @name Accessors to the list of ignored groups. diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c index 4915d0b..9980201 100644 --- a/lib/libalpm/handle.c +++ b/lib/libalpm/handle.c @@ -88,7 +88,8 @@ void _alpm_handle_free(alpm_handle_t *handle) FREE(handle->gpgdir); FREELIST(handle->noupgrade); FREELIST(handle->noextract); - FREELIST(handle->ignorepkg); + alpm_list_free_inner(handle->ignorepkg, (alpm_list_fn_free)alpm_dep_free); + alpm_list_free(handle->ignorepkg); FREELIST(handle->ignoregroup); alpm_list_free_inner(handle->assumeinstalled, (alpm_list_fn_free)alpm_dep_free); @@ -512,6 +513,54 @@ static int _alpm_option_strlist_rem(alpm_handle_t *handle, alpm_list_t **list, c return 0; } +/* Note that, at the moment, the following functions don't copy their + arguments like the _strlist equivalents do. */ +static int _alpm_option_deplist_add(alpm_handle_t *handle, alpm_list_t **list, const alpm_depend_t *dep) +{ + CHECK_HANDLE(handle, return -1); + *list = alpm_list_add(*list, (void *)dep); + return 0; +} + +static int _alpm_option_deplist_set(alpm_handle_t *handle, alpm_list_t **list, alpm_list_t *newlist) +{ + CHECK_HANDLE(handle, return -1); + if(list) { + alpm_list_free_inner(*list, (alpm_list_fn_free)alpm_dep_free); + alpm_list_free(*list); + } + *list = newlist; + return 0; +} + +static int depend_cmp(const void *d1, const void *d2) +{ + const alpm_depend_t *dep1 = d1; + const alpm_depend_t *dep2 = d2; + + if(strcmp(dep1->name, dep2->name) == 0 && + strcmp(dep1->version, dep2->version) == 0 && + dep1->mod == dep2->mod) { + return 0; + } + + return -1; +} + +static int _alpm_option_deplist_rem(alpm_handle_t *handle, alpm_list_t **list, const alpm_depend_t *dep) +{ + alpm_depend_t *vdata = NULL; + CHECK_HANDLE(handle, return -1); + + *list = alpm_list_remove(handle->assumeinstalled, dep, &depend_cmp, (void **)&vdata); + if(vdata != NULL) { + alpm_dep_free(vdata); + return 1; + } + + return 0; +} + int SYMEXPORT alpm_option_add_noupgrade(alpm_handle_t *handle, const char *pkg) { return _alpm_option_strlist_add(handle, &(handle->noupgrade), pkg); @@ -552,19 +601,19 @@ int SYMEXPORT alpm_option_match_noextract(alpm_handle_t *handle, const char *pat return _alpm_fnmatch_patterns(handle->noextract, path); } -int SYMEXPORT alpm_option_add_ignorepkg(alpm_handle_t *handle, const char *pkg) +int SYMEXPORT alpm_option_add_ignorepkg(alpm_handle_t *handle, const alpm_depend_t *pkg) { - return _alpm_option_strlist_add(handle, &(handle->ignorepkg), pkg); + return _alpm_option_deplist_add(handle, &(handle->ignorepkg), pkg); } int SYMEXPORT alpm_option_set_ignorepkgs(alpm_handle_t *handle, alpm_list_t *ignorepkgs) { - return _alpm_option_strlist_set(handle, &(handle->ignorepkg), ignorepkgs); + return _alpm_option_deplist_set(handle, &(handle->ignorepkg), ignorepkgs); } -int SYMEXPORT alpm_option_remove_ignorepkg(alpm_handle_t *handle, const char *pkg) +int SYMEXPORT alpm_option_remove_ignorepkg(alpm_handle_t *handle, const alpm_depend_t *pkg) { - return _alpm_option_strlist_rem(handle, &(handle->ignorepkg), pkg); + return _alpm_option_deplist_rem(handle, &(handle->ignorepkg), pkg); } int SYMEXPORT alpm_option_add_ignoregroup(alpm_handle_t *handle, const char *grp) @@ -584,47 +633,17 @@ int SYMEXPORT alpm_option_remove_ignoregroup(alpm_handle_t *handle, const char * int SYMEXPORT alpm_option_add_assumeinstalled(alpm_handle_t *handle, const alpm_depend_t *dep) { - CHECK_HANDLE(handle, return -1); - - handle->assumeinstalled = alpm_list_add(handle->assumeinstalled, (void *)dep); - return 0; + return _alpm_option_deplist_add(handle, &(handle->assumeinstalled), dep); } int SYMEXPORT alpm_option_set_assumeinstalled(alpm_handle_t *handle, alpm_list_t *deps) { - CHECK_HANDLE(handle, return -1); - if(handle->assumeinstalled) { - alpm_list_free_inner(handle->assumeinstalled, (alpm_list_fn_free)alpm_dep_free); - alpm_list_free(handle->assumeinstalled); - } - handle->assumeinstalled = deps; - return 0; -} - -static int assumeinstalled_cmp(const void *d1, const void *d2) -{ - const alpm_depend_t *dep1 = d1; - const alpm_depend_t *dep2 = d2; - - if(strcmp(dep1->name, dep2->name) == 0 && strcmp(dep1->version, dep2->version) == 0) { - return 0; - } - - return -1; + return _alpm_option_deplist_set(handle, &(handle->assumeinstalled), deps); } int SYMEXPORT alpm_option_remove_assumeinstalled(alpm_handle_t *handle, const alpm_depend_t *dep) { - alpm_depend_t *vdata = NULL; - CHECK_HANDLE(handle, return -1); - - handle->assumeinstalled = alpm_list_remove(handle->assumeinstalled, dep, &assumeinstalled_cmp, (void **)&vdata); - if(vdata != NULL) { - alpm_dep_free(vdata); - return 1; - } - - return 0; + return _alpm_option_deplist_rem(handle, &(handle->assumeinstalled), dep); } int SYMEXPORT alpm_option_set_arch(alpm_handle_t *handle, const char *arch) diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c index 4b5120b..ac27cab 100644 --- a/lib/libalpm/package.c +++ b/lib/libalpm/package.c @@ -768,10 +768,29 @@ alpm_pkg_t SYMEXPORT *alpm_pkg_find(alpm_list_t *haystack, const char *needle) int SYMEXPORT alpm_pkg_should_ignore(alpm_handle_t *handle, alpm_pkg_t *pkg) { alpm_list_t *groups = NULL; - - /* first see if the package is ignored */ - if(alpm_list_find(handle->ignorepkg, pkg->name, _alpm_fnmatch)) { - return 1; + alpm_list_t *pkgs = NULL; + + /* first see if package is matched by IgnorePkg */ + for(pkgs = handle->ignorepkg; pkgs; pkgs = pkgs->next) { + alpm_depend_t *ignored = pkgs->data; + char *oldname = NULL; + /* check names using fnmatch first: */ + if(strcmp(ignored->name, pkg->name) != 0 && + _alpm_fnmatch(ignored->name, pkg->name) == 0) { + oldname = ignored->name; + /* If the name matched a glob expression, replace the pattern by + the concrete name for the version comparison to work. */ + ignored->name = strdup(pkg->name); + ignored->name_hash = _alpm_hash_sdbm(pkg->name); + } + int result = _alpm_depcmp_literal(pkg, ignored); + if(oldname != NULL) { + /* Restore the original glob expression. */ + ignored->name = oldname; + } + if(result) { + return 1; + } } /* next see if the package is in a group that is ignored */ diff --git a/src/pacman/conf.c b/src/pacman/conf.c index ccf8183..177ff86 100644 --- a/src/pacman/conf.c +++ b/src/pacman/conf.c @@ -677,6 +677,42 @@ static int register_repo(config_repo_t *repo) return 0; } +typedef int (*add_fn)(alpm_handle_t *, const alpm_depend_t *); +static int _add_dep_list(alpm_handle_t *handle, alpm_list_t *list, add_fn add, + const char *name) +{ + int ret; + for(; list; list = list->next) { + char *entry = list->data; + alpm_depend_t *dep = alpm_dep_from_string(entry); + if(!dep) { + return 1; + } + pm_printf(ALPM_LOG_DEBUG, "parsed %s: %s %s\n", name, dep->name, + dep->version); + + ret = (*add)(handle, dep); + if(ret != 0) { + pm_printf(ALPM_LOG_ERROR, _("Failed to pass %s entry to libalpm\n"), + name); + alpm_dep_free(dep); + return ret; + } + } + return 0; +} +/* This is basically alpm_option_add_ignorepkg with an extra sanity + * check. */ +static int _add_ignorepkg(alpm_handle_t *handle, const alpm_depend_t *dep) +{ + if (dep->mod != ALPM_DEP_MOD_ANY && dep->mod != ALPM_DEP_MOD_EQ) { + pm_printf(ALPM_LOG_ERROR, "Invalid version constraint in IgnorePkg: %s", + dep->name); + return 1; + } + return alpm_option_add_ignorepkg(handle, dep); +} + /** Sets up libalpm global stuff in one go. Called after the command line * and initial config file parsing. Once this is complete, we can see if any * paths were defined. If a rootdir was defined and nothing else, we want all @@ -783,26 +819,20 @@ static int setup_libalpm(void) alpm_option_set_usesyslog(handle, config->usesyslog); alpm_option_set_deltaratio(handle, config->deltaratio); - alpm_option_set_ignorepkgs(handle, config->ignorepkg); alpm_option_set_ignoregroups(handle, config->ignoregrp); alpm_option_set_noupgrades(handle, config->noupgrade); alpm_option_set_noextracts(handle, config->noextract); - for(i = config->assumeinstalled; i; i = i->next) { - char *entry = i->data; - alpm_depend_t *dep = alpm_dep_from_string(entry); - if(!dep) { - return 1; - } - pm_printf(ALPM_LOG_DEBUG, "parsed assume installed: %s %s\n", dep->name, dep->version); - - ret = alpm_option_add_assumeinstalled(handle, dep); - if(ret) { - pm_printf(ALPM_LOG_ERROR, _("Failed to pass %s entry to libalpm"), "assume-installed"); - alpm_dep_free(dep); - return ret; - } - } + ret = _add_dep_list(handle, config->assumeinstalled, + &alpm_option_add_assumeinstalled, "assume-installed"); + if(ret != 0) { + return ret; + } + ret = _add_dep_list(handle, config->ignorepkg, &_add_ignorepkg, + "ignorepkg"); + if(ret != 0) { + return ret; + } return 0; } diff --git a/test/pacman/tests/TESTS b/test/pacman/tests/TESTS index 3ff0b0c..fd9999c 100644 --- a/test/pacman/tests/TESTS +++ b/test/pacman/tests/TESTS @@ -57,6 +57,11 @@ TESTS += test/pacman/tests/ignore005.py TESTS += test/pacman/tests/ignore006.py TESTS += test/pacman/tests/ignore007.py TESTS += test/pacman/tests/ignore008.py +TESTS += test/pacman/tests/ignore009.py +TESTS += test/pacman/tests/ignore010.py +TESTS += test/pacman/tests/ignore011.py +TESTS += test/pacman/tests/ignore012.py +TESTS += test/pacman/tests/ignore013.py TESTS += test/pacman/tests/ldconfig001.py TESTS += test/pacman/tests/ldconfig002.py TESTS += test/pacman/tests/ldconfig003.py diff --git a/test/pacman/tests/ignore009.py b/test/pacman/tests/ignore009.py new file mode 100644 index 0000000..52125c8 --- /dev/null +++ b/test/pacman/tests/ignore009.py @@ -0,0 +1,14 @@ +self.description = "Sync with specific ignored package version" + +package1 = pmpkg("package1", "1.0-1") +self.addpkg2db("local", package1) + +package1up = pmpkg("package1", "1.1-1") +self.addpkg2db("sync", package1up) + +self.option["IgnorePkg"] = ["package1=1.1-1"] +self.args = "-Su" + +self.addrule("PACMAN_RETCODE=0") +self.addrule("PKG_VERSION=package1|1.0-1") + diff --git a/test/pacman/tests/ignore010.py b/test/pacman/tests/ignore010.py new file mode 100644 index 0000000..c96c9de --- /dev/null +++ b/test/pacman/tests/ignore010.py @@ -0,0 +1,13 @@ +self.description = "Sync with invalid bound on IgnorePkg" + +package1 = pmpkg("package1", "1.0-1") +self.addpkg2db("local", package1) + +package1up = pmpkg("package1", "1.1-1") +self.addpkg2db("sync", package1up) + +self.option["IgnorePkg"] = ["package1<1.1-1"] +self.args = "-Su" + +self.addrule("PACMAN_RETCODE=0") +self.expectfailure = True diff --git a/test/pacman/tests/ignore011.py b/test/pacman/tests/ignore011.py new file mode 100644 index 0000000..4fae819 --- /dev/null +++ b/test/pacman/tests/ignore011.py @@ -0,0 +1,13 @@ +self.description = "Sync with specific ignored, non-matching package version" + +package1 = pmpkg("package1", "1.0-1") +self.addpkg2db("local", package1) + +package1up = pmpkg("package1", "1.2-1") +self.addpkg2db("sync", package1up) + +self.option["IgnorePkg"] = ["package1=1.1-1"] +self.args = "-Su" + +self.addrule("PACMAN_RETCODE=0") +self.addrule("PKG_VERSION=package1|1.2-1") diff --git a/test/pacman/tests/ignore012.py b/test/pacman/tests/ignore012.py new file mode 100644 index 0000000..ffbcc5e --- /dev/null +++ b/test/pacman/tests/ignore012.py @@ -0,0 +1,13 @@ +self.description = "Sync with ignored package version and fnmatch" + +package1 = pmpkg("package1", "1.0-1") +self.addpkg2db("local", package1) + +package1up = pmpkg("package1", "1.1-1") +self.addpkg2db("sync", package1up) + +self.option["IgnorePkg"] = ["packag*=1.1-1"] +self.args = "-Su" + +self.addrule("PACMAN_RETCODE=0") +self.addrule("PKG_VERSION=package1|1.0-1") diff --git a/test/pacman/tests/ignore013.py b/test/pacman/tests/ignore013.py new file mode 100644 index 0000000..9a8edfc --- /dev/null +++ b/test/pacman/tests/ignore013.py @@ -0,0 +1,13 @@ +self.description = "Sync with ignored package version without pkgrel" + +package1 = pmpkg("package1", "1.0-1") +self.addpkg2db("local", package1) + +package1up = pmpkg("package1", "1.1-1") +self.addpkg2db("sync", package1up) + +self.option["IgnorePkg"] = ["package1=1.1"] +self.args = "-Su" + +self.addrule("PACMAN_RETCODE=0") +self.addrule("PKG_VERSION=package1|1.0-1") -- 2.4.4