changeset 24120:a450e0a2ba0a

revlog: in addgroup, reject ill-formed deltas based on censored nodes To ensure interoperability when clones disagree about which file nodes are censored, a restriction is made on deltas based on censored nodes. Any such delta must replace the full text of the base in a single patch. If the recipient of a delta considers the base to be censored and the delta is not in the expected form, the recipient must reject it, as it can't know if the source has also censored the base. For background and broader design of the censorship feature, see: http://mercurial.selenic.com/wiki/CensorPlan
author Mike Edgar <adgar@google.com>
date Fri, 06 Feb 2015 00:55:29 +0000
parents a5a06c9c7407
children 9d0b6ef92eb2
files mercurial/changegroup.py mercurial/error.py mercurial/revlog.py
diffstat 3 files changed, 24 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/changegroup.py	Wed Jan 21 16:35:09 2015 -0500
+++ b/mercurial/changegroup.py	Fri Feb 06 00:55:29 2015 +0000
@@ -659,8 +659,11 @@
         pr()
         fl = repo.file(f)
         o = len(fl)
-        if not fl.addgroup(source, revmap, trp):
-            raise util.Abort(_("received file revlog group is empty"))
+        try:
+            if not fl.addgroup(source, revmap, trp):
+                raise util.Abort(_("received file revlog group is empty"))
+        except error.CensoredBaseError, e:
+            raise util.Abort(_("received delta base is censored: %s") % e)
         revisions += len(fl) - o
         files += 1
         if f in needfiles:
--- a/mercurial/error.py	Wed Jan 21 16:35:09 2015 -0500
+++ b/mercurial/error.py	Fri Feb 06 00:55:29 2015 +0000
@@ -141,3 +141,11 @@
     def __init__(self, filename, node):
         from node import short
         RevlogError.__init__(self, '%s:%s' % (filename, short(node)))
+
+class CensoredBaseError(RevlogError):
+    """error raised when a delta is rejected because its base is censored
+
+    A delta based on a censored revision must be formed as single patch
+    operation which replaces the entire base with new content. This ensures
+    the delta may be applied by clones which have not censored the base.
+    """
--- a/mercurial/revlog.py	Wed Jan 21 16:35:09 2015 -0500
+++ b/mercurial/revlog.py	Fri Feb 06 00:55:29 2015 +0000
@@ -1403,6 +1403,17 @@
                                       _('unknown delta base'))
 
                 baserev = self.rev(deltabase)
+
+                if baserev != nullrev and self.iscensored(baserev):
+                    # if base is censored, delta must be full replacement in a
+                    # single patch operation
+                    hlen = struct.calcsize(">lll")
+                    oldlen = self.rawsize(baserev)
+                    newlen = len(delta) - hlen
+                    if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
+                        raise error.CensoredBaseError(self.indexfile,
+                                                      self.node(baserev))
+
                 chain = self._addrevision(node, None, transaction, link,
                                           p1, p2, REVIDX_DEFAULT_FLAGS,
                                           (baserev, delta), ifh, dfh)