Mercurial > public > mercurial-scm > hg
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) |