Patchwork [6,of,7,V3] util: add a file handle wrapper class that does hash digest validation

login
register
mail settings
Submitter Mike Hommey
Date Oct. 16, 2014, 8:22 a.m.
Message ID <dad191f49c3587341926.1413447751@zenigata.glandium.org>
Download mbox | patch
Permalink /patch/6316/
State Accepted
Headers show

Comments

Mike Hommey - Oct. 16, 2014, 8:22 a.m.
# HG changeset patch
# User Mike Hommey <mh@glandium.org>
# Date 1413446601 -32400
#      Thu Oct 16 17:03:21 2014 +0900
# Node ID dad191f49c35873419268b31cef19dbc251ad812
# Parent  f6cbabdb852b211b67797d9775989f614ff56528
util: add a file handle wrapper class that does hash digest validation

It is going to be used for the remote-changegroup feature in bundle2.
Pierre-Yves David - Oct. 16, 2014, 10:16 a.m.
On 10/16/2014 01:22 AM, Mike Hommey wrote:
> # HG changeset patch
> # User Mike Hommey <mh@glandium.org>
> # Date 1413446601 -32400
> #      Thu Oct 16 17:03:21 2014 +0900
> # Node ID dad191f49c35873419268b31cef19dbc251ad812
> # Parent  f6cbabdb852b211b67797d9775989f614ff56528
> util: add a file handle wrapper class that does hash digest validation

I pushed the first 6 to the clowncopter. I sent some feedback on the 
last one. We are getting close to completion

I did two tweak to the the pushed patch. I drop sha512 from the doctest 
since the ambiguity was preventing the test to pass. And I did some 
change to the hookargs business as discussed on IRC.

Patch

diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -179,16 +179,47 @@  class digester(object):
     def preferred(supported):
         """returns the strongest digest type in both supported and DIGESTS."""
 
         for k in DIGESTS_BY_STRENGTH:
             if k in supported:
                 return k
         return None
 
+class digestchecker(object):
+    """file handle wrapper that additionally checks content against a given
+    size and digests.
+
+        d = digestchecker(fh, size, {'md5': '...'})
+
+    When multiple digests are given, all of them are validated.
+    """
+
+    def __init__(self, fh, size, digests):
+        self._fh = fh
+        self._size = size
+        self._got = 0
+        self._digests = dict(digests)
+        self._digester = digester(self._digests.keys())
+
+    def read(self, length=-1):
+        content = self._fh.read(length)
+        self._digester.update(content)
+        self._got += len(content)
+        return content
+
+    def validate(self):
+        if self._size != self._got:
+            raise Abort(_('size mismatch: expected %d, got %d') %
+                (self._size, self._got))
+        for k, v in self._digests.items():
+            if v != self._digester[k]:
+                raise Abort(_('%s mismatch: expected %s, got %s') %
+                    (k, v, self._digester[k]))
+
 try:
     buffer = buffer
 except NameError:
     if sys.version_info[0] < 3:
         def buffer(sliceable, offset=0):
             return sliceable[offset:]
     else:
         def buffer(sliceable, offset=0):