From ddddc6e7ff41d6c30bde682dac4e2e42e017a13f Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Sat, 5 Jul 2008 07:18:31 +0000 Subject: [PATCH] * Implement a new binarytree.digestCheck() method and use it to check digests for binary packages. * Split out a AsynchronousTask base class from SubProcess. * Derive a new BinpkgVerifier class from AsynchronousTask. Even though this is not really asynchronous yet, it can fake the interface by doing everything in the start() method. svn path=/main/trunk/; revision=10935 --- pym/_emerge/__init__.py | 78 +++++++++++++++++++++++++++++-- pym/portage/__init__.py | 21 +++++---- pym/portage/dbapi/bintree.py | 90 +++++++++++++++++++++++++++--------- 3 files changed, 153 insertions(+), 36 deletions(-) diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py index bfae9e20a..20629c1f9 100644 --- a/pym/_emerge/__init__.py +++ b/pym/_emerge/__init__.py @@ -1454,8 +1454,29 @@ class EbuildFetchPretend(SlotObject): mydbapi=portdb, tree="porttree") return retval -class SubProcess(SlotObject): - __slots__ = ("cancelled", "pid", "returncode") +class AsynchronousTask(SlotObject): + __slots__ = ("cancelled", "returncode") + + def start(self): + """ + Start an asynchronous task and then return as soon as possible. + """ + pass + + def isAlive(self): + return self.returncode is None + + def poll(self): + return self.returncode + + def wait(self): + return self.returncode + + def cancel(self): + pass + +class SubProcess(AsynchronousTask): + __slots__ = ("pid",) def poll(self): if self.returncode is not None: @@ -2175,6 +2196,8 @@ class Binpkg(EbuildBuildDir): tree = "bintree" settings.setcpv(pkg) debug = settings.get("PORTAGE_DEBUG") == "1" + verify = "strict" in settings.features and \ + not opts.pretend # The prefetcher has already completed or it # could be running now. If it's running now, @@ -2227,6 +2250,13 @@ class Binpkg(EbuildBuildDir): if opts.fetchonly: return os.EX_OK + if verify: + verifier = BinpkgVerifier(pkg=pkg) + verifier.start() + retval = verifier.wait() + if retval != os.EX_OK: + return retval + msg = " === (%s of %s) Merging Binary (%s::%s)" % \ (pkg_count.curval, pkg_count.maxval, pkg.cpv, pkg_path) short_msg = "emerge: (%s of %s) %s Merge Binary" % \ @@ -2352,8 +2382,7 @@ class Binpkg(EbuildBuildDir): class BinpkgFetcher(Task): - __slots__ = ("use_locks", "pkg", "pretend", - "pkg_path", "remote") + __slots__ = ("use_locks", "pkg", "pretend", "pkg_path", "remote") def __init__(self, **kwargs): Task.__init__(self, **kwargs) @@ -2498,6 +2527,47 @@ class BinpkgFetcherAsync(SpawnProcess): self._lock_obj = None self.locked = False +class BinpkgVerifier(AsynchronousTask): + __slots__ = ("pkg",) + + def start(self): + """ + Note: Unlike a normal AsynchronousTask.start() method, + this one does all work is synchronously. The returncode + attribute will be set before it returns. + """ + + pkg = self.pkg + root_config = pkg.root_config + bintree = root_config.trees["bintree"] + rval = os.EX_OK + try: + bintree.digestCheck(pkg) + except portage.exception.FileNotFound: + writemsg("!!! Fetching Binary failed " + \ + "for '%s'\n" % pkg.cpv, noiselevel=-1) + rval = 1 + except portage.exception.DigestException, e: + writemsg("\n!!! Digest verification failed:\n", + noiselevel=-1) + writemsg("!!! %s\n" % e.value[0], + noiselevel=-1) + writemsg("!!! Reason: %s\n" % e.value[1], + noiselevel=-1) + writemsg("!!! Got: %s\n" % e.value[2], + noiselevel=-1) + writemsg("!!! Expected: %s\n" % e.value[3], + noiselevel=-1) + rval = 1 + + self.returncode = rval + + def cancel(self): + self.cancelled = True + + def poll(self): + return self.returncode + class BinpkgExtractorAsync(SpawnProcess): __slots__ = ("image_dir", "pkg", "pkg_path") diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index 6aa4d2664..abba1f39e 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -3143,24 +3143,25 @@ def _checksum_failure_temp_file(distdir, basename): os.rename(filename, temp_filename) return temp_filename -def _check_digests(filename, digests): +def _check_digests(filename, digests, show_errors=1): """ Check digests and display a message if an error occurs. @return True if all digests match, False otherwise. """ verified_ok, reason = portage.checksum.verify_all(filename, digests) if not verified_ok: - writemsg("!!! Previously fetched" + \ - " file: '%s'\n" % filename, noiselevel=-1) - writemsg("!!! Reason: %s\n" % reason[0], - noiselevel=-1) - writemsg(("!!! Got: %s\n" + \ - "!!! Expected: %s\n") % \ - (reason[1], reason[2]), noiselevel=-1) + if show_errors: + writemsg("!!! Previously fetched" + \ + " file: '%s'\n" % filename, noiselevel=-1) + writemsg("!!! Reason: %s\n" % reason[0], + noiselevel=-1) + writemsg(("!!! Got: %s\n" + \ + "!!! Expected: %s\n") % \ + (reason[1], reason[2]), noiselevel=-1) return False return True -def _check_distfile(filename, digests, eout): +def _check_distfile(filename, digests, eout, show_errors=1): """ @return a tuple of (match, stat_obj) where match is True if filename matches all given digests (if any) and stat_obj is a stat result, or @@ -3183,7 +3184,7 @@ def _check_distfile(filename, digests, eout): eout.ebegin("%s %s ;-)" % (os.path.basename(filename), "size")) eout.eend(0) else: - if _check_digests(filename, digests): + if _check_digests(filename, digests, show_errors=show_errors): eout.ebegin("%s %s ;-)" % (os.path.basename(filename), " ".join(sorted(digests)))) eout.eend(0) diff --git a/pym/portage/dbapi/bintree.py b/pym/portage/dbapi/bintree.py index 22f91ceef..6de9b5d87 100644 --- a/pym/portage/dbapi/bintree.py +++ b/pym/portage/dbapi/bintree.py @@ -7,12 +7,12 @@ from portage.dep import isvalidatom, isjustname, dep_getkey, match_from_list from portage.dbapi.virtual import fakedbapi from portage.exception import InvalidPackageName, InvalidAtom, \ PermissionDenied, PortageException -from portage.output import green +from portage.output import green, EOutput from portage.util import ensure_dirs, normalize_path, writemsg, writemsg_stdout from portage.versions import best, catpkgsplit, catsplit from portage.update import update_dbentries -from portage import dep_expand, listdir, _movefile +from portage import dep_expand, listdir, _check_distfile, _movefile import portage.xpak, portage.getbinpkg @@ -991,26 +991,6 @@ class binarytree(object): if not fcmd: fcmd = self.settings.get(fcmd_prefix) success = portage.getbinpkg.file_get(url, mydest, fcmd=fcmd) - if success and "strict" in self.settings.features: - metadata = self._remotepkgs[pkgname] - digests = {} - from portage.checksum import hashfunc_map, verify_all - for k in hashfunc_map: - v = metadata.get(k) - if not v: - continue - digests[k] = v - if "SIZE" in metadata: - try: - digests["size"] = long(self._remotepkgs[pkgname]["SIZE"]) - except ValueError: - writemsg("!!! Malformed SIZE attribute in remote " + \ - "metadata for '%s'\n" % pkgname) - if digests: - ok, reason = verify_all(tbz2_path, digests) - if not ok: - raise portage.exception.DigestException( - tuple([tbz2_path]+list(reason))) if not success: try: os.unlink(self.getname(pkgname)) @@ -1019,6 +999,72 @@ class binarytree(object): raise portage.exception.FileNotFound(mydest) self.inject(pkgname) + def _load_pkgindex(self): + pkgindex = self._new_pkgindex() + try: + f = open(self._pkgindex_file) + except EnvironmentError: + pass + else: + try: + pkgindex.read(f) + finally: + f.close() + return pkgindex + + def digestCheck(self, pkg): + """ + Verify digests for the given package and raise DigestException + if verification fails. + @rtype: bool + @returns: True if digests could be located, False otherwise. + """ + cpv = pkg + if not isinstance(cpv, basestring): + cpv = pkg.cpv + pkg = None + + pkg_path = self.getname(cpv) + metadata = None + if self._remotepkgs is None or cpv not in self._remotepkgs: + for d in self._load_pkgindex().packages: + if d["CPV"] == cpv: + metadata = d + break + else: + metadata = self._remotepkgs[cpv] + if metadata is None: + return False + + digests = {} + from portage.checksum import hashfunc_map, verify_all + for k in hashfunc_map: + v = metadata.get(k) + if not v: + continue + digests[k] = v + + if "SIZE" in metadata: + try: + digests["size"] = int(metadata["SIZE"]) + except ValueError: + writemsg("!!! Malformed SIZE attribute in remote " + \ + "metadata for '%s'\n" % cpv) + + if not digests: + return False + + eout = EOutput() + eout.quiet = self.settings.get("PORTAGE_QUIET") == "1" + ok, st = _check_distfile(pkg_path, digests, eout, show_errors=0) + if not ok: + ok, reason = verify_all(pkg_path, digests) + if not ok: + raise portage.exception.DigestException( + (pkg_path,) + tuple(reason)) + + return True + def getslot(self, mycatpkg): "Get a slot for a catpkg; assume it exists." myslot = "" -- 2.26.2