From: Michael Schroeder Date: Fri, 8 Nov 2019 13:34:18 +0000 (+0100) Subject: Add support for blacklisted packages X-Git-Tag: 0.7.8~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4120051be87d2cccc2146a30c5128edd5c78d76d;p=thirdparty%2Flibsolv.git 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). --- 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);