]> git.ipfire.org Git - thirdparty/libsolv.git/commitdiff
Add support for blacklisted packages
authorMichael Schroeder <mls@suse.de>
Fri, 8 Nov 2019 13:34:18 +0000 (14:34 +0100)
committerMichael Schroeder <mls@suse.de>
Fri, 8 Nov 2019 13:34:18 +0000 (14:34 +0100)
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).

14 files changed:
ext/libsolvext.ver
ext/repo_updateinfoxml.c
ext/repo_updateinfoxml.h
ext/testcase.c
src/problems.c
src/problems.h
src/rules.c
src/rules.h
src/solver.c
src/solver.h
src/solverdebug.c
test/testcases/blacklist/ptf [new file with mode: 0644]
test/testcases/blacklist/retracted [new file with mode: 0644]
tools/repo2solv.c

index ef028f0f71c06d5bcf7872c0b870ec5385c4ec7d..02cf6d14d8ec00669380317fd0e46a6ed12c1378 100644 (file)
@@ -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;
index 984f491bb30a0c1b3ffd28ec8746658d21aaf10a..51c3f5abd6fb7aee1f46a6cb577b2a8aee111bb3 100644 (file)
@@ -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
index bd0a61b6603a0ede08cdcd6aa53a887b93dfadbf..c3da0f67068be1d5f5ccf42b57cde4791ef08c6f 100644 (file)
@@ -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);
+
index bd0643a8a8a5d8bf547f3c39ea3fa14c3721b6a7..d6c4a577828245475765d8665dd51dfb309b1b79 100644 (file)
@@ -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 }
 };
index 6f2ad4b1629054ecf549f165a639a70c6312d9ad..b46d6249013fe0102b587100dee77161c9df5b4b 100644 (file)
@@ -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)
index 37021e1d5149029c9fc09ebe907d56d63a288de3..45e4e7c6c09166ca66a155dcf9f394f73ef03ce4 100644 (file)
@@ -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);
index 57895c01619284f840b29fa4cbc8ed72a8d06ba4..611029e3c12dd52f3a48f745d4fd01c34000c8a6 100644 (file)
@@ -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;
index 6b8511f7c4f16971b9fba108805560d5c29111ce..92860908f809513f9f83cf7b5fe7d9f120f957ae 100644 (file)
@@ -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);
 
index 45f9dbfd1cb5f8a0a209e19f51f584ca2c6cf748..6a9d66e61d4fd6e7fd7717dbbd7288a6cc044d33 100644 (file)
@@ -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;
index daf4f636d5b190707f47d977d481f418c71db22c..2dec2590a226b2f056b86190f261073dbefe8cf4 100644 (file)
@@ -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
 
index b1b55f484ae2eb2e31334c38b18576135982b22e..0b2879b08dfba5cba2a05b7edd9b44b5655defd3 100644 (file)
@@ -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 (file)
index 0000000..b8765d0
--- /dev/null
@@ -0,0 +1,52 @@
+repo system 0 testtags <inline>
+#>=Pkg: ptf-2 1 1 noarch
+#>=Prv: ptf-package()
+repo available 0 testtags <inline>
+#>=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 <inline>
+#>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 <inline>
+#>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 <inline>
+#>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 <inline>
+#>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 (file)
index 0000000..d75f17d
--- /dev/null
@@ -0,0 +1,22 @@
+repo system 0 testtags <inline>
+#>=Pkg: B 1 1 noarch
+repo available 0 testtags <inline>
+#>=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 <inline>
+#>upgrade B-1-1.noarch@system B-2-1.noarch@available
index 03491a67d05dfaff5eab2c4b237d8cce05559547..b3907439c596e95321ad266603052c458865f035 100644 (file)
@@ -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);