From a84916d4bbf76d62836ca4a9f783c50d9eafab99 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Sat, 16 Mar 2024 03:54:31 +0000 Subject: [PATCH] libalpm: disallow partial upgrades Detect if there's an upgrade avaliable, and if so disallow the upgrade. --- lib/libalpm/alpm.h | 18 ++++++- lib/libalpm/db.c | 13 +++++ lib/libalpm/db.h | 2 + lib/libalpm/error.c | 2 + lib/libalpm/sync.c | 113 ++++++++++++++++++++++++++++++++++++++++++++ lib/libalpm/trans.h | 1 + 6 files changed, 148 insertions(+), 1 deletion(-) diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index e7fc7bbf..69c9d8af 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -273,6 +273,8 @@ typedef enum _alpm_errno_t { ALPM_ERR_TRANS_NOT_LOCKED, /** A hook failed to run */ ALPM_ERR_TRANS_HOOK_FAILED, + /* The transaction is a partial upgrade */ + ALPM_ERR_TRANS_PARTIAL_UPGRADE, /* Packages */ /** Package not found */ ALPM_ERR_PKG_NOT_FOUND, @@ -1477,6 +1479,19 @@ int alpm_db_set_usage(alpm_db_t *db, int usage); */ int alpm_db_get_usage(alpm_db_t *db, int *usage); +/** Set if a database may perform partial upgrades. + * @param db pointer to the package database + * @param partial 1 to allow, 0 to disallow + * @return 0 on success, or -1 on error + */ +int alpm_db_set_allow_partial_upgrades(alpm_db_t *db, int allow); + +/** Set if a database allows partial upgrades. + * @param db pointer to the package database + * @return 1 if allowed, 0 if not, -1 on error + */ +int alpm_db_get_allow_partial_upgrades(alpm_db_t *db); + /* End of usage accessors */ /** @} */ @@ -2775,7 +2790,8 @@ typedef enum _alpm_transflag_t { ALPM_TRANS_FLAG_NOSCRIPTLET = (1 << 10), /** Ignore dependency conflicts. */ ALPM_TRANS_FLAG_NOCONFLICTS = (1 << 11), - /* (1 << 12) flag can go here */ + /** Allow partial upgrades */ + ALPM_TRANS_FLAG_ALLOWPARTIAL = (1 << 12), /** Do not install a package if it is already installed and up to date. */ ALPM_TRANS_FLAG_NEEDED = (1 << 13), /** Use ALPM_PKG_REASON_EXPLICIT when installing packages. */ diff --git a/lib/libalpm/db.c b/lib/libalpm/db.c index d7431bd6..ce7e269f 100644 --- a/lib/libalpm/db.c +++ b/lib/libalpm/db.c @@ -368,6 +368,19 @@ int SYMEXPORT alpm_db_get_usage(alpm_db_t *db, int *usage) return 0; } +int SYMEXPORT alpm_db_set_allow_partial_upgrades(alpm_db_t *db, int allow) +{ + ASSERT(db != NULL, return -1); + db->allow_partial = allow ? 1 : 0; + return 0; +} + +int SYMEXPORT alpm_db_get_allow_partial_upgrades(alpm_db_t *db) +{ + ASSERT(db != NULL, return -1); + return db->allow_partial; +} + alpm_db_t *_alpm_db_new(const char *treename, int is_local) { alpm_db_t *db; diff --git a/lib/libalpm/db.h b/lib/libalpm/db.h index ae1f0f74..e53b90fb 100644 --- a/lib/libalpm/db.h +++ b/lib/libalpm/db.h @@ -80,6 +80,8 @@ struct _alpm_db_t { int siglevel; /* alpm_db_usage_t */ int usage; + + int allow_partial; }; diff --git a/lib/libalpm/error.c b/lib/libalpm/error.c index 8929fcb9..a0b37898 100644 --- a/lib/libalpm/error.c +++ b/lib/libalpm/error.c @@ -104,6 +104,8 @@ const char SYMEXPORT *alpm_strerror(alpm_errno_t err) return _("transaction commit attempt when database is not locked"); case ALPM_ERR_TRANS_HOOK_FAILED: return _("failed to run transaction hooks"); + case ALPM_ERR_TRANS_PARTIAL_UPGRADE: + return _("partial upgrade"); /* Packages */ case ALPM_ERR_PKG_NOT_FOUND: return _("could not find or read package"); diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c index 1653b4b2..b453d25c 100644 --- a/lib/libalpm/sync.c +++ b/lib/libalpm/sync.c @@ -249,6 +249,8 @@ int SYMEXPORT alpm_sync_sysupgrade(alpm_handle_t *handle, int enable_downgrade) } } + trans->sysupgrade = enable_downgrade ? 2 : 1; + return 0; } @@ -362,6 +364,111 @@ finish: return ret; } +static int has_replacment(alpm_db_t *sdb, alpm_pkg_t *lpkg) +{ + alpm_list_t *j, *k; + alpm_handle_t *handle = lpkg->handle; + + for(j = _alpm_db_get_pkgcache(sdb); j; j = j->next) { + alpm_pkg_t *spkg = j->data; + + if(alpm_pkg_should_ignore(handle, spkg)) { + continue; + } + + for(k = alpm_pkg_get_replaces(spkg); k; k = k->next) { + alpm_depend_t *replace = k->data; + /* we only want to consider literal matches at this point. */ + if(_alpm_depcmp_literal(lpkg, replace)) { + _alpm_log(handle, ALPM_LOG_DEBUG, "partial upgrade due to %s haivng replacment\n", lpkg->name); + return 1; + } + } + } + + return 0; +} + +static int is_partial_upgrade(alpm_handle_t *handle) +{ + alpm_list_t *i, *j; + alpm_trans_t *trans = handle->trans; + alpm_db_t *localdb = handle->db_local; + int from_sync = 0; + + if(trans->sysupgrade == 2) { + return 0; + } + + for(i = trans->add; i; i = i->next) { + alpm_pkg_t *pkg = i->data; + + if (pkg->origin == ALPM_PKG_FROM_SYNCDB){ + alpm_db_t *db = alpm_pkg_get_db(pkg); + if(!db->allow_partial) { + alpm_pkg_t *lpkg = alpm_db_get_pkg(localdb, pkg->name); + /* let transaction pass if reinstall */ + if(!lpkg || _alpm_pkg_compare_versions(pkg, lpkg) != 0) { + from_sync = 1; + break; + } + } + } + } + + for(i = _alpm_db_get_pkgcache(localdb); i; i = i->next) { + alpm_pkg_t *lpkg = i->data; + + if(alpm_pkg_find(trans->add, lpkg->name) || alpm_pkg_find(trans->remove, lpkg->name) + || alpm_pkg_should_ignore(handle, lpkg)) { + continue; + } + + /* Search for replacers then literal (if no replacer) in each sync database. */ + for(j = handle->dbs_sync; j; j = j->next) { + alpm_db_t *sdb = j->data; + alpm_pkg_t *spkg; + + if(!(sdb->usage & ALPM_DB_USAGE_UPGRADE)) { + /* jump to next db */ + continue; + } + + if(!sdb->allow_partial && !trans->sysupgrade && has_replacment(sdb, lpkg)) { + return 1; + } + + if(!from_sync) { + /* jump to next local package */ + break; + } + + spkg = _alpm_db_get_pkgfromcache(sdb, lpkg->name); + + if(!spkg || alpm_pkg_should_ignore(handle, spkg)) { + /* jump to next db */ + continue; + } + + if(sdb->allow_partial) { + /* jump to next local package */ + break; + } + + /* Check sdb */ + if(_alpm_pkg_compare_versions(spkg, lpkg) != 0) { + _alpm_log(handle, ALPM_LOG_DEBUG, "partial upgrade due to upgrade for %s\n", lpkg->name); + return 1; + } + + /* jump to next local package */ + break; + } + } + + return 0; +} + int _alpm_sync_prepare(alpm_handle_t *handle, alpm_list_t **data) { alpm_list_t *i, *j; @@ -680,6 +787,12 @@ int _alpm_sync_prepare(alpm_handle_t *handle, alpm_list_t **data) } } + if(!(trans->flags & ALPM_TRANS_FLAG_ALLOWPARTIAL) && !(trans->flags & ALPM_TRANS_FLAG_NODEPVERSION) + && is_partial_upgrade(handle)) { + handle->pm_errno = ALPM_ERR_TRANS_PARTIAL_UPGRADE; + ret = -1; + } + cleanup: return ret; } diff --git a/lib/libalpm/trans.h b/lib/libalpm/trans.h index 9ee7015e..6fe99227 100644 --- a/lib/libalpm/trans.h +++ b/lib/libalpm/trans.h @@ -39,6 +39,7 @@ typedef enum _alpm_transstate_t { typedef struct _alpm_trans_t { /* bitfield of alpm_transflag_t flags */ int flags; + int sysupgrade; alpm_transstate_t state; alpm_list_t *unresolvable; /* list of (alpm_pkg_t *) */ alpm_list_t *add; /* list of (alpm_pkg_t *) */