comparison mercurial/filelog.py @ 22596:27e2317efe89

filelog: raise CensoredNodeError when hash checks fail with censor metadata With this change, when a revlog revision hash does not match its content, and the content is empty with a special metadata key, the integrity failure is assumed to be intentionally caused to remove sensitive content from repository history. To allow different Mercurial functionality to handle this scenario differently a more specific exception is raised than "ordinary" hash failures. Alternatives to this approach include, but are not limited to: - Calling a hook when hashes mismatch to allow arbitrary tombstone validation. Cons: Irresponsibly easy to disable integrity checking altogether. - Returning empty revision data eagerly instead of raising, masking the error. Cons: Push/pull won't roundtrip the tombstone, so client repos are unusable. - Doing nothing differently at this layer. Callers must do their own detection of tombstoned data if they want to handle some hash checks and not others. - Impacts dozens of callsites, many of which don't have the revision data - Would probably be missing one or two callsites at any given time - Currently we throw a RevlogError, as do 12 other places in revlog.py. Callers would need to parse the exception message and/or ensure RevlogError is not thrown from any other part of their call tree.
author Mike Edgar <adgar@google.com>
date Wed, 03 Sep 2014 22:14:20 -0400
parents 75bb7c702317
children 58ec36686f0e
comparison
equal deleted inserted replaced
22595:244478687edd 22596:27e2317efe89
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # 4 #
5 # This software may be used and distributed according to the terms of the 5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version. 6 # GNU General Public License version 2 or any later version.
7 7
8 import revlog 8 import error, revlog
9 import re 9 import re
10 10
11 _mdre = re.compile('\1\n') 11 _mdre = re.compile('\1\n')
12 def parsemeta(text): 12 def parsemeta(text):
13 """return (metadatadict, keylist, metadatasize)""" 13 """return (metadatadict, keylist, metadatasize)"""
24 24
25 def packmeta(meta, text): 25 def packmeta(meta, text):
26 keys = sorted(meta.iterkeys()) 26 keys = sorted(meta.iterkeys())
27 metatext = "".join("%s: %s\n" % (k, meta[k]) for k in keys) 27 metatext = "".join("%s: %s\n" % (k, meta[k]) for k in keys)
28 return "\1\n%s\1\n%s" % (metatext, text) 28 return "\1\n%s\1\n%s" % (metatext, text)
29
30 def _censoredtext(text):
31 m, offs = parsemeta(text)
32 return m and "censored" in m and not text[offs:]
29 33
30 class filelog(revlog.revlog): 34 class filelog(revlog.revlog):
31 def __init__(self, opener, path): 35 def __init__(self, opener, path):
32 super(filelog, self).__init__(opener, 36 super(filelog, self).__init__(opener,
33 "/".join(("data", path + ".i"))) 37 "/".join(("data", path + ".i")))
84 t2 = self.read(node) 88 t2 = self.read(node)
85 return t2 != text 89 return t2 != text
86 90
87 return True 91 return True
88 92
93 def checkhash(self, text, p1, p2, node, rev=None):
94 try:
95 super(filelog, self).checkhash(text, p1, p2, node, rev=rev)
96 except error.RevlogError:
97 if _censoredtext(text):
98 raise error.CensoredNodeError(self.indexfile, node)
99 raise
100
89 def _file(self, f): 101 def _file(self, f):
90 return filelog(self.opener, f) 102 return filelog(self.opener, f)