Patchwork [3,of,6,censor,RFC] revlog: in addgroup, reject ill-formed deltas based on censored nodes

login
register
mail settings
Submitter adgar@google.com
Date Jan. 23, 2015, 11:53 p.m.
Message ID <b532b15853310060626d.1422057203@adgar.nyc.corp.google.com>
Download mbox | patch
Permalink /patch/7551/
State Superseded
Commit 521c330b1ad7af3e9da78750defc534ab981c1ff
Headers show

Comments

adgar@google.com - Jan. 23, 2015, 11:53 p.m.
# HG changeset patch
# User Mike Edgar <adgar@google.com>
# Date 1422050467 18000
#      Fri Jan 23 17:01:07 2015 -0500
# Node ID b532b15853310060626d192942a7946bab1e5900
# Parent  64ceb83e160e6d7ed6df9f59829ac4bf2eb605d6
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

Patch

diff -r 64ceb83e160e -r b532b1585331 mercurial/changegroup.py
--- a/mercurial/changegroup.py	Fri Jan 23 17:01:39 2015 -0500
+++ b/mercurial/changegroup.py	Fri Jan 23 17:01:07 2015 -0500
@@ -625,8 +625,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:
diff -r 64ceb83e160e -r b532b1585331 mercurial/error.py
--- a/mercurial/error.py	Fri Jan 23 17:01:39 2015 -0500
+++ b/mercurial/error.py	Fri Jan 23 17:01:07 2015 -0500
@@ -139,3 +139,11 @@ 
     def __init__(self, filename, node):
         from node import short
         RevlogError.__init__(self, '%s:%s' % (filename, short(node)))
+
+class CensoredBaseError(CensoredNodeError):
+    """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.
+    """
diff -r 64ceb83e160e -r b532b1585331 mercurial/revlog.py
--- a/mercurial/revlog.py	Fri Jan 23 17:01:39 2015 -0500
+++ b/mercurial/revlog.py	Fri Jan 23 17:01:07 2015 -0500
@@ -1401,6 +1401,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)