From b6a2a23926d54ccfa9a1ce331c1bc97dbe2c73d1 Mon Sep 17 00:00:00 2001 From: fuzzyray Date: Wed, 20 May 2009 21:49:39 +0000 Subject: [PATCH] Restructure system affection detection. Store "vulnerable" and "upgrade" packages in a table, and use that data to determine which packages cannot be upgraded, and which packages actually cause upgrades svn path=/trunk/gentoolkit/; revision=648 --- bin/glsa-check | 71 +++++++++++++++------------------ pym/gentoolkit/glsa/__init__.py | 57 ++++++++++++++++---------- 2 files changed, 69 insertions(+), 59 deletions(-) diff --git a/bin/glsa-check b/bin/glsa-check index f1e8912..d5ac4e1 100644 --- a/bin/glsa-check +++ b/bin/glsa-check @@ -265,52 +265,47 @@ if mode in ["dump", "fix", "inject", "pretend"]: if mode == "dump": myglsa.dump() elif mode == "fix": - sys.stdout.write("fixing "+myid+"\n") - mergelist = myglsa.getMergeList(least_change=least_change) - if mergelist == None: + sys.stdout.write("Fixing GLSA "+myid+"\n") + if not myglsa.isVulnerable(): sys.stdout.write(">>> no vulnerable packages installed\n") - elif mergelist == []: - sys.stdout.write(">>> cannot fix GLSA, no unaffected packages available\n") - sys.exit(2) - for pkg in mergelist: - sys.stdout.write(">>> merging "+pkg+"\n") - # using emerge for the actual merging as it contains the dependency - # code and we want to be consistent in behaviour. Also this functionality - # will be integrated in emerge later, so it shouldn't hurt much. - emergecmd = "emerge --oneshot " + glsaconfig["EMERGE_OPTS"] + " =" + pkg - if verbose: - sys.stderr.write(emergecmd+"\n") - exitcode = os.system(emergecmd) - # system() returns the exitcode in the high byte of a 16bit integer - if exitcode >= 1<<8: - exitcode >>= 8 - if exitcode: - sys.exit(exitcode) + else: + mergelist = myglsa.getMergeList(least_change=least_change) + if mergelist == []: + sys.stdout.write(">>> cannot fix GLSA, no unaffected packages available\n") + sys.exit(2) + for pkg in mergelist: + sys.stdout.write(">>> merging "+pkg+"\n") + # using emerge for the actual merging as it contains the dependency + # code and we want to be consistent in behaviour. Also this functionality + # will be integrated in emerge later, so it shouldn't hurt much. + emergecmd = "emerge --oneshot " + glsaconfig["EMERGE_OPTS"] + " =" + pkg + if verbose: + sys.stderr.write(emergecmd+"\n") + exitcode = os.system(emergecmd) + # system() returns the exitcode in the high byte of a 16bit integer + if exitcode >= 1<<8: + exitcode >>= 8 + if exitcode: + sys.exit(exitcode) if len(mergelist): sys.stdout.write("\n") myglsa.inject() elif mode == "pretend": sys.stdout.write("Checking GLSA "+myid+"\n") - mergelist = myglsa.getMergeList(least_change=least_change) - if mergelist == None: + if not myglsa.isVulnerable(): sys.stdout.write(">>> no vulnerable packages installed\n") - elif mergelist == []: - sys.stdout.write(">>> cannot fix GLSA, no unaffected packages available\n") - sys.exit(2) - if mergelist: - sys.stdout.write("The following updates will be performed for this GLSA:\n") - for pkg in mergelist: - oldver = None - for x in vardb.match(portage.dep_getkey(pkg)): - if vardb.aux_get(x, ["SLOT"]) == portdb.aux_get(pkg, ["SLOT"]): - oldver = x - if oldver == None: - raise ValueError("could not find old version for package %s" % pkg) - oldver = oldver[len(portage.dep_getkey(oldver))+1:] - sys.stdout.write(" " + pkg + " (" + oldver + ")\n") else: - sys.stdout.write("Nothing to do for this GLSA\n") - sys.stdout.write("\n") + mergedict = {} + for (vuln, update) in myglsa.getAffectionTable(least_change=least_change): + mergedict.setdefault(update, []).append(vuln) + + sys.stdout.write(">>> The following updates will be performed for this GLSA:\n") + for pkg in mergedict: + if pkg != "": + sys.stdout.write(" " + pkg + " (vulnerable: " + ", ".join(mergedict[pkg]) + ")\n") + if "" in mergedict: + sys.stdout.write("\n>>> For the following packages, no upgrade path exists:\n") + sys.stdout.write(" " + ", ".join(mergedict[""])) elif mode == "inject": sys.stdout.write("injecting " + myid + "\n") myglsa.inject() diff --git a/pym/gentoolkit/glsa/__init__.py b/pym/gentoolkit/glsa/__init__.py index 121f066..2ee294f 100644 --- a/pym/gentoolkit/glsa/__init__.py +++ b/pym/gentoolkit/glsa/__init__.py @@ -398,29 +398,37 @@ def getMinUpgrade(vulnerableList, unaffectedList, minimize=True): v_installed = reduce(operator.add, [match(v, "vartree") for v in vulnerableList], []) u_installed = reduce(operator.add, [match(u, "vartree") for u in unaffectedList], []) - install_unaffected = True - for i in v_installed: - if i not in u_installed: - install_unaffected = False + # remove all unaffected atoms from vulnerable list + v_installed = list(set(v_installed).difference(set(u_installed))) - if install_unaffected: + if not v_installed: return None + # this tuple holds all vulnerable atoms, and the related upgrade atom + vuln_update = [] + avail_updates = set() for u in unaffectedList: - mylist = match(u, "porttree", match_type="match-all") - for c in mylist: + # TODO: This had match_type="match-all" before. I don't think it should + # since we disregarded masked items later anyway (match(=rValue, "porttree")) + avail_updates.update(match(u, "porttree")) + # if an atom is already installed, we should not consider it for upgrades + avail_updates.difference_update(u_installed) + + for vuln in v_installed: + update = "" + for c in avail_updates: c_pv = portage.catpkgsplit(c) - i_pv = portage.catpkgsplit(portage.best(v_installed)) + i_pv = portage.catpkgsplit(vuln) if portage.pkgcmp(c_pv[1:], i_pv[1:]) > 0 \ - and (rValue == "" \ - or not match("="+rValue, "porttree") \ - or (minimize ^ (portage.pkgcmp(c_pv[1:], portage.catpkgsplit(rValue)[1:]) > 0)) \ - and match("="+c, "porttree")) \ - and portage.db["/"]["porttree"].dbapi.aux_get(c, ["SLOT"]) == portage.db["/"]["vartree"].dbapi.aux_get(portage.best(v_installed), ["SLOT"]): - rValue = c_pv[0]+"/"+c_pv[1]+"-"+c_pv[2] + and (update == "" \ + or (minimize ^ (portage.pkgcmp(c_pv[1:], portage.catpkgsplit(rValue)[1:]) > 0))) \ + and portage.db["/"]["porttree"].dbapi.aux_get(c, ["SLOT"]) == portage.db["/"]["vartree"].dbapi.aux_get(vuln, ["SLOT"]): + update = c_pv[0]+"/"+c_pv[1]+"-"+c_pv[2] if c_pv[3] != "r0": # we don't like -r0 for display - rValue += "-"+c_pv[3] - return rValue + update += "-"+c_pv[3] + vuln_update.append([vuln, update]) + + return vuln_update def format_date(datestr): """ @@ -658,8 +666,7 @@ class Glsa: if path["arch"] == "*" or self.config["ARCH"] in path["arch"].split(): for v in path["vul_atoms"]: rValue = rValue \ - or (len(match(v, "vartree")) > 0 \ - and None != getMinUpgrade(path["vul_atoms"], path["unaff_atoms"])) + or (None != getMinUpgrade([v,], path["unaff_atoms"])) return rValue def isApplied(self): @@ -700,10 +707,18 @@ class Glsa: @rtype: List of Strings @return: list of package-versions that have to be merged """ - rValue = [] + return list(set(update for (vuln, update) in self.getAffectionTable(least_change) if update)) + + def getAffectionTable(self, least_change=True): + """ + Will initialize the self.systemAffection list of + atoms installed on the system that are affected + by this GLSA, and the atoms that are minimal upgrades. + """ + systemAffection = [] for pkg in self.packages.keys(): for path in self.packages[pkg]: update = getMinUpgrade(path["vul_atoms"], path["unaff_atoms"], minimize=least_change) if update: - rValue.append(update) - return rValue + systemAffection.extend(update) + return systemAffection -- 2.26.2