* Implement a new binarytree.digestCheck() method and use it to check
authorZac Medico <zmedico@gentoo.org>
Sat, 5 Jul 2008 07:18:31 +0000 (07:18 -0000)
committerZac Medico <zmedico@gentoo.org>
Sat, 5 Jul 2008 07:18:31 +0000 (07:18 -0000)
  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
pym/portage/__init__.py
pym/portage/dbapi/bintree.py

index bfae9e20ab111589ef897091ad37f42b822e46d5..20629c1f94f07e1c56eae297d0119a1b38e77142 100644 (file)
@@ -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")
index 6aa4d266421d154c70bd8f3f3dfef76d1cd0e935..abba1f39ece44a1e85460930cd9a43444b4e5f22 100644 (file)
@@ -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)
index 22f91ceefb6de7aad9f00155a121e809d67002f2..6de9b5d87253a81755dcc058e4ca6b3b9d73045e 100644 (file)
@@ -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 = ""