From 4120051be87d2cccc2146a30c5128edd5c78d76d Mon Sep 17 00:00:00 2001 From: Michael Schroeder Date: Fri, 8 Nov 2019 14:34:18 +0100 Subject: [PATCH] Add support for blacklisted packages This will be used in SUSE's ptf packages and also to retract released updates. The idea is that it is not possible to pull in a blacklisted package via a dependency, they can only be installed by a job that directly addresses them (the SETEVR bit is set). --- ext/libsolvext.ver | 1 + ext/repo_updateinfoxml.c | 89 ++++++++++++++++++++++ ext/repo_updateinfoxml.h | 3 + ext/testcase.c | 2 + src/problems.c | 15 ++++ src/problems.h | 11 +-- src/rules.c | 118 +++++++++++++++++++++++++++++ src/rules.h | 6 +- src/solver.c | 13 ++++ src/solver.h | 4 + src/solverdebug.c | 2 + test/testcases/blacklist/ptf | 52 +++++++++++++ test/testcases/blacklist/retracted | 22 ++++++ tools/repo2solv.c | 1 + 14 files changed, 333 insertions(+), 6 deletions(-) create mode 100644 test/testcases/blacklist/ptf create mode 100644 test/testcases/blacklist/retracted diff --git a/ext/libsolvext.ver b/ext/libsolvext.ver index ef028f0f..02cf6d14 100644 --- a/ext/libsolvext.ver +++ b/ext/libsolvext.ver @@ -42,6 +42,7 @@ SOLV_1.0 { repo_add_zyppdb_products; repo_find_all_pubkeys; repo_find_pubkey; + repo_mark_retracted_packages; repo_verify_sigdata; rpm_byfp; rpm_byrpmdbid; diff --git a/ext/repo_updateinfoxml.c b/ext/repo_updateinfoxml.c index 984f491b..51c3f5ab 100644 --- a/ext/repo_updateinfoxml.c +++ b/ext/repo_updateinfoxml.c @@ -490,3 +490,92 @@ repo_add_updateinfoxml(Repo *repo, FILE *fp, int flags) return pd.ret; } +#ifdef SUSE + +static int +repo_mark_retracted_packages_cmp(const void *ap, const void *bp, void *dp) +{ + Id *a = (Id *)ap; + Id *b = (Id *)bp; + if (a[1] != b[1]) + return a[1] - b[1]; + if (a[2] != b[2]) + return a[2] - b[2]; + if (a[0] != b[0]) + return a[0] - b[0]; + return 0; +} + + +void +repo_mark_retracted_packages(Repo *repo, Id retractedmarker) +{ + Pool *pool = repo->pool; + int i, p; + Solvable *s; + Id con, *conp; + Id retractedname, retractedevr; + + Queue q; + queue_init(&q); + for (p = 1; p < pool->nsolvables; p++) + { + const char *status; + s = pool->solvables + p; + if (strncmp(pool_id2str(pool, s->name), "patch:", 6) != 0) + { + if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) + continue; + queue_push2(&q, p, s->name); + queue_push2(&q, s->evr, s->arch); + continue; + } + status = solvable_lookup_str(s, UPDATE_STATUS); + if (!status || strcmp(status, "retracted") != 0) + continue; + if (!s->conflicts) + continue; + conp = s->repo->idarraydata + s->conflicts; + while ((con = *conp++) != 0) + { + Reldep *rd; + Id name, evr, arch; + if (!ISRELDEP(con)) + continue; + rd = GETRELDEP(pool, con); + if (rd->flags != REL_LT) + continue; + name = rd->name; + evr = rd->evr; + arch = 0; + if (ISRELDEP(name)) + { + rd = GETRELDEP(pool, name); + name = rd->name; + if (rd->flags == REL_ARCH) + arch = rd->evr; + } + queue_push2(&q, 0, name); + queue_push2(&q, evr, arch); + } + } + if (q.count) + solv_sort(q.elements, q.count / 4, sizeof(Id) * 4, repo_mark_retracted_packages_cmp, repo->pool); + retractedname = retractedevr = 0; + for (i = 0; i < q.count; i += 4) + { + if (!q.elements[i]) + { + retractedname = q.elements[i + 1]; + retractedevr = q.elements[i + 2]; + } + else if (q.elements[i + 1] == retractedname && q.elements[i + 2] == retractedevr) + { + s = pool->solvables + q.elements[i]; + s->provides = repo_addid_dep(repo, s->provides, retractedmarker, 0); + } + } + queue_free(&q); +} + +#endif diff --git a/ext/repo_updateinfoxml.h b/ext/repo_updateinfoxml.h index bd0a61b6..c3da0f67 100644 --- a/ext/repo_updateinfoxml.h +++ b/ext/repo_updateinfoxml.h @@ -6,3 +6,6 @@ */ extern int repo_add_updateinfoxml(Repo *repo, FILE *fp, int flags); + +extern void repo_mark_retracted_packages(Repo *repo, Id retractedmarker); + diff --git a/ext/testcase.c b/ext/testcase.c index bd0643a8..d6c4a577 100644 --- a/ext/testcase.c +++ b/ext/testcase.c @@ -59,6 +59,7 @@ static struct job2str { { SOLVER_ALLOWUNINSTALL, "allowuninstall" }, { SOLVER_FAVOR, "favor" }, { SOLVER_DISFAVOR, "disfavor" }, + { SOLVER_BLACKLIST, "blacklist" }, { 0, 0 } }; @@ -1190,6 +1191,7 @@ static struct rclass2str { { SOLVER_RULE_LEARNT, "learnt" }, { SOLVER_RULE_BEST, "best" }, { SOLVER_RULE_YUMOBS, "yumobs" }, + { SOLVER_RULE_BLACK, "black" }, { SOLVER_RULE_RECOMMENDS, "recommends" }, { 0, 0 } }; diff --git a/src/problems.c b/src/problems.c index 6f2ad4b1..b46d6249 100644 --- a/src/problems.c +++ b/src/problems.c @@ -719,6 +719,12 @@ convertsolution(Solver *solv, Id why, Queue *solutionq) } return; } + if (why >= solv->blackrules && why < solv->blackrules_end) + { + queue_push(solutionq, SOLVER_SOLUTION_BLACK); + assert(solv->rules[why].p < 0); + queue_push(solutionq, -solv->rules[why].p); + } } /* @@ -981,6 +987,8 @@ solver_solutionelement_extrajobflags(Solver *solv, Id problem, Id solution) * -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job * SOLVER_SOLUTION_BEST pkgid * -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job + * SOLVER_SOLUTION_BLACK pkgid + * -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job * SOLVER_SOLUTION_JOB jobidx * -> remove job (jobidx - 1, jobidx) from job queue * SOLVER_SOLUTION_POOLJOB jobidx @@ -1331,6 +1339,8 @@ solver_problemruleinfo2str(Solver *solv, SolverRuleinfo type, Id source, Id targ s = pool_tmpjoin(pool, "both package ", pool_solvid2str(pool, source), " and "); s = pool_tmpjoin(pool, s, pool_solvid2str(pool, target), " obsolete "); return pool_tmpappend(pool, s, pool_dep2str(pool, dep), 0); + case SOLVER_RULE_BLACK: + return pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), " can only be installed by a direct request"); default: return "bad problem rule type"; } @@ -1385,6 +1395,11 @@ solver_solutionelement2str(Solver *solv, Id p, Id rp) else return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " despite the old version"); } + else if (p == SOLVER_SOLUTION_BLACK) + { + Solvable *s = pool->solvables + rp; + return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), 0); + } else if (p > 0 && rp == 0) return pool_tmpjoin(pool, "allow deinstallation of ", pool_solvid2str(pool, p), 0); else if (p > 0 && rp > 0) diff --git a/src/problems.h b/src/problems.h index 37021e1d..45e4e7c6 100644 --- a/src/problems.h +++ b/src/problems.h @@ -22,11 +22,12 @@ extern "C" { struct s_Solver; -#define SOLVER_SOLUTION_JOB (0) -#define SOLVER_SOLUTION_DISTUPGRADE (-1) -#define SOLVER_SOLUTION_INFARCH (-2) -#define SOLVER_SOLUTION_BEST (-3) -#define SOLVER_SOLUTION_POOLJOB (-4) +#define SOLVER_SOLUTION_JOB (0) +#define SOLVER_SOLUTION_DISTUPGRADE (-1) +#define SOLVER_SOLUTION_INFARCH (-2) +#define SOLVER_SOLUTION_BEST (-3) +#define SOLVER_SOLUTION_POOLJOB (-4) +#define SOLVER_SOLUTION_BLACK (-5) void solver_recordproblem(struct s_Solver *solv, Id rid); void solver_fixproblem(struct s_Solver *solv, Id rid); diff --git a/src/rules.c b/src/rules.c index 57895c01..611029e3 100644 --- a/src/rules.c +++ b/src/rules.c @@ -2101,6 +2101,97 @@ reenableduprule(Solver *solv, Id name) } } +/*********************************************************************** + *** + *** Black rule part + ***/ + +static inline void +disableblackrule(Solver *solv, Id p) +{ + Rule *r; + int i; + for (i = solv->blackrules, r = solv->rules + i; i < solv->blackrules_end; i++, r++) + if (r->p == -p) + solver_disablerule(solv, r); +} + +static inline void +reenableblackrule(Solver *solv, Id p) +{ + Pool *pool = solv->pool; + Rule *r; + int i; + for (i = solv->blackrules, r = solv->rules + i; i < solv->blackrules_end; i++, r++) + if (r->p == -p) + { + solver_enablerule(solv, r); + IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS) + { + POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling "); + solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r); + } + } +} + +void +solver_addblackrules(Solver *solv) +{ + int i; + Id how, select, what, p, pp; + Queue *job = &solv->job; + Pool *pool = solv->pool; + Repo *installed = solv->installed; + Map updatemap; + + map_init(&updatemap, 0); + solv->blackrules = solv->nrules; + if (installed) + { + for (i = 0; i < job->count; i += 2) + { + how = job->elements[i]; + select = job->elements[i] & SOLVER_SELECTMASK; + what = job->elements[i + 1]; + switch (how & SOLVER_JOBMASK) + { + case SOLVER_BLACKLIST: + FOR_JOB_SELECT(p, pp, select, what) + { + Solvable *s = pool->solvables + p; + if (s->repo != installed) + continue; + if (!updatemap.size) + map_grow(&updatemap, pool->ss.nstrings); + if (s->name > 0 && s->name < pool->ss.nstrings) + MAPSET(&updatemap, s->name); + } + } + } + } + for (i = 0; i < job->count; i += 2) + { + how = job->elements[i]; + select = job->elements[i] & SOLVER_SELECTMASK; + what = job->elements[i + 1]; + switch (how & SOLVER_JOBMASK) + { + case SOLVER_BLACKLIST: + FOR_JOB_SELECT(p, pp, select, what) + { + Solvable *s = pool->solvables + p; + if (s->repo == installed) + continue; + if (updatemap.size && s->name > 0 && s->name < pool->ss.nstrings && MAPTST(&updatemap, s->name)) + continue; /* installed package with same name is already blacklisted */ + solver_addrule(solv, -p, 0, 0); + } + break; + } + } + map_free(&updatemap); + solv->blackrules_end = solv->nrules; +} /*********************************************************************** *** @@ -2114,6 +2205,7 @@ reenableduprule(Solver *solv, Id name) #define DISABLE_UPDATE 1 #define DISABLE_INFARCH 2 #define DISABLE_DUP 3 +#define DISABLE_BLACK 4 static void jobtodisablelist(Solver *solv, Id how, Id what, Queue *q) @@ -2213,6 +2305,16 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q) } } } + if ((set & SOLVER_SETEVR) != 0 && solv->blackrules != solv->blackrules_end) + { + if (select == SOLVER_SOLVABLE) + queue_push2(q, DISABLE_BLACK, what); + else + { + FOR_JOB_SELECT(p, pp, select, what) + queue_push2(q, DISABLE_BLACK, p); + } + } if (!installed || installed->end == installed->start) return; /* now the hard part: disable some update rules */ @@ -2386,6 +2488,9 @@ solver_disablepolicyrules(Solver *solv) case DISABLE_DUP: disableduprule(solv, arg); break; + case DISABLE_BLACK: + disableblackrule(solv, arg); + break; default: break; } @@ -2489,6 +2594,9 @@ solver_reenablepolicyrules(Solver *solv, int jobidx) case DISABLE_DUP: reenableduprule(solv, arg); break; + case DISABLE_BLACK: + reenableblackrule(solv, arg); + break; } } queue_free(&q); @@ -2815,6 +2923,12 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp) *depp = solv->yumobsrules_info[rid - solv->yumobsrules]; return SOLVER_RULE_YUMOBS; } + if (rid >= solv->blackrules && rid < solv->blackrules_end) + { + if (fromp) + *fromp = -r->p; + return SOLVER_RULE_BLACK; + } if (rid >= solv->choicerules && rid < solv->choicerules_end) return SOLVER_RULE_CHOICE; if (rid >= solv->recommendsrules && rid < solv->recommendsrules_end) @@ -2845,10 +2959,14 @@ solver_ruleclass(Solver *solv, Id rid) return SOLVER_RULE_BEST; if (rid >= solv->yumobsrules && rid < solv->yumobsrules_end) return SOLVER_RULE_YUMOBS; + if (rid >= solv->blackrules && rid < solv->blackrules_end) + return SOLVER_RULE_BLACK; if (rid >= solv->choicerules && rid < solv->choicerules_end) return SOLVER_RULE_CHOICE; if (rid >= solv->recommendsrules && rid < solv->recommendsrules_end) return SOLVER_RULE_RECOMMENDS; + if (rid >= solv->blackrules && rid < solv->blackrules_end) + return SOLVER_RULE_BLACK; if (rid >= solv->learntrules && rid < solv->nrules) return SOLVER_RULE_LEARNT; return SOLVER_RULE_UNKNOWN; diff --git a/src/rules.h b/src/rules.h index 6b8511f7..92860908 100644 --- a/src/rules.h +++ b/src/rules.h @@ -72,7 +72,8 @@ typedef enum { SOLVER_RULE_LEARNT = 0x800, SOLVER_RULE_BEST = 0x900, SOLVER_RULE_YUMOBS = 0xa00, - SOLVER_RULE_RECOMMENDS = 0xb00 + SOLVER_RULE_RECOMMENDS = 0xb00, + SOLVER_RULE_BLACK = 0xc00 } SolverRuleinfo; #define SOLVER_RULE_TYPEMASK 0xff00 @@ -134,6 +135,9 @@ extern void solver_addbestrules(struct s_Solver *solv, int havebestinstalljobs, /* yumobs rules */ extern void solver_addyumobsrules(struct s_Solver *solv); +/* black rules */ +extern void solver_addblackrules(struct s_Solver *solv); + /* recommends rules */ extern void solver_addrecommendsrules(struct s_Solver *solv); diff --git a/src/solver.c b/src/solver.c index 45f9dbfd..6a9d66e6 100644 --- a/src/solver.c +++ b/src/solver.c @@ -3377,6 +3377,7 @@ solver_solve(Solver *solv, Queue *job) int hasbestinstalljob = 0; int hasfavorjob = 0; int haslockjob = 0; + int hasblacklistjob = 0; solve_start = solv_timems(0); @@ -3987,6 +3988,10 @@ solver_solve(Solver *solv, Queue *job) POOL_DEBUG(SOLV_DEBUG_JOB, "job: %s %s\n", (how & SOLVER_JOBMASK) == SOLVER_FAVOR ? "favor" : "disfavor", solver_select2str(pool, select, what)); hasfavorjob = 1; break; + case SOLVER_BLACKLIST: + POOL_DEBUG(SOLV_DEBUG_JOB, "job: blacklist %s\n", solver_select2str(pool, select, what)); + hasblacklistjob = 1; + break; default: POOL_DEBUG(SOLV_DEBUG_JOB, "job: unknown job\n"); break; @@ -4040,6 +4045,11 @@ solver_solve(Solver *solv, Queue *job) else solv->yumobsrules = solv->yumobsrules_end = solv->nrules; + if (hasblacklistjob) + solver_addblackrules(solv); + else + solv->blackrules = solv->blackrules_end = solv->nrules; + if (solv->havedisfavored && solv->strongrecommends && solv->recommendsruleq) solver_addrecommendsrules(solv); else @@ -4851,6 +4861,9 @@ pool_job2str(Pool *pool, Id how, Id what, Id flagmask) case SOLVER_DISFAVOR: strstart = "disfavor "; break; + case SOLVER_BLACKLIST: + strstart = "blacklist "; + break; default: strstart = "unknown job "; break; diff --git a/src/solver.h b/src/solver.h index daf4f636..2dec2590 100644 --- a/src/solver.h +++ b/src/solver.h @@ -76,6 +76,9 @@ struct s_Solver { Id yumobsrules_end; Id *yumobsrules_info; /* the dependency for each rule */ + Id blackrules; /* rules from blacklisted packages */ + Id blackrules_end; + Id choicerules; /* choice rules (always weak) */ Id choicerules_end; Id *choicerules_info; /* the rule we used to generate the choice rule */ @@ -244,6 +247,7 @@ typedef struct s_Solver Solver; #define SOLVER_ALLOWUNINSTALL 0x0b00 #define SOLVER_FAVOR 0x0c00 #define SOLVER_DISFAVOR 0x0d00 +#define SOLVER_BLACKLIST 0x0e00 #define SOLVER_JOBMASK 0xff00 diff --git a/src/solverdebug.c b/src/solverdebug.c index b1b55f48..0b2879b0 100644 --- a/src/solverdebug.c +++ b/src/solverdebug.c @@ -128,6 +128,8 @@ solver_printruleclass(Solver *solv, int type, Rule *r) POOL_DEBUG(type, "FEATURE "); else if (p >= solv->yumobsrules && p < solv->yumobsrules_end) POOL_DEBUG(type, "YUMOBS "); + else if (p >= solv->blackrules && p < solv->blackrules_end) + POOL_DEBUG(type, "BLACK "); else if (p >= solv->recommendsrules && p < solv->recommendsrules_end) POOL_DEBUG(type, "RECOMMENDS "); solver_printrule(solv, type, r); diff --git a/test/testcases/blacklist/ptf b/test/testcases/blacklist/ptf new file mode 100644 index 00000000..b8765d05 --- /dev/null +++ b/test/testcases/blacklist/ptf @@ -0,0 +1,52 @@ +repo system 0 testtags +#>=Pkg: ptf-2 1 1 noarch +#>=Prv: ptf-package() +repo available 0 testtags +#>=Pkg: ptf-1 1 1 noarch +#>=Prv: ptf-package() +#>=Pkg: ptf-2 2 1 noarch +#>=Prv: ptf-package() +#>=Pkg: A 1 1 noarch +#>=Req: ptf-1 + +system i686 * system + +# +# test 1: a ptf package cannot be pulled in via a dependency +# +job blacklist provides ptf-package() +job install name A +result transaction,problems +#>problem 78613afb info package A-1-1.noarch requires ptf-1, but none of the providers can be installed +#>problem 78613afb solution 23f73f5b deljob install name A +#>problem 78613afb solution b79aeb6f allow ptf-1-1-1.noarch@available + +# +# test 2: a ptf package cannot be pulled in via a unspecific job +# +nextjob +job blacklist provides ptf-package() +job install name ptf-1 +result transaction,problems +#>problem 021b17e2 info package ptf-1-1-1.noarch cannot only be installed by a direct request +#>problem 021b17e2 solution 932a6c2f deljob install name ptf-1 +#>problem 021b17e2 solution b79aeb6f allow ptf-1-1-1.noarch@available + +# +# test 3: a ptf package can be pulled in via a specific job +# +nextjob +job blacklist provides ptf-package() +job install name ptf-1 [setevr] +result transaction,problems +#>install ptf-1-1-1.noarch@available + +# +# test 4: a ptf package can be updated +# +nextjob +job blacklist provides ptf-package() +job update all packages +result transaction,problems +#>upgrade ptf-2-1-1.noarch@system ptf-2-2-1.noarch@available + diff --git a/test/testcases/blacklist/retracted b/test/testcases/blacklist/retracted new file mode 100644 index 00000000..d75f17dd --- /dev/null +++ b/test/testcases/blacklist/retracted @@ -0,0 +1,22 @@ +repo system 0 testtags +#>=Pkg: B 1 1 noarch +repo available 0 testtags +#>=Pkg: patch 1 1 noarch +#>=Con: B < 2-1 +#>=Pkg: B 2 1 noarch +#>=Prv: retracted-patch-package() + +system i686 * system + +job blacklist provides retracted-patch-package() +job install name patch +#>problem 3a66200a info package patch-1-1.noarch conflicts with B < 2-1 provided by B-1-1.noarch +#>problem 3a66200a solution 14805cf8 deljob install name patch +#>problem 3a66200a solution 4a9277b8 allow B-2-1.noarch@available +#>problem 3a66200a solution 718064ed erase B-1-1.noarch@system + +nextjob +job blacklist provides retracted-patch-package() +job install pkg B-2-1.noarch@available +result transaction,problems +#>upgrade B-1-1.noarch@system B-2-1.noarch@available diff --git a/tools/repo2solv.c b/tools/repo2solv.c index 03491a67..b3907439 100644 --- a/tools/repo2solv.c +++ b/tools/repo2solv.c @@ -868,6 +868,7 @@ main(int argc, char **argv) #ifdef SUSE if (add_auto) repo_add_autopattern(repo, 0); + repo_mark_retracted_packages(repo, pool_str2id(pool, "retracted-patch-package()", 1)); #endif tool_write(repo, stdout); pool_free(pool); -- 2.47.2