minor code clean-up in dirnode.py
authorZooko O'Whielacronx <zooko@zooko.com>
Wed, 14 Jul 2010 06:02:55 +0000 (23:02 -0700)
committerZooko O'Whielacronx <zooko@zooko.com>
Wed, 14 Jul 2010 06:02:55 +0000 (23:02 -0700)
Impose micro-POLA by passing only the writekey instead of the whole node object to {{{_encrypt_rw_uri()}}}. Remove DummyImmutableFileNode in nodemaker.py, which is obviated by this. Add micro-optimization by precomputing the netstring of the empty string and branching on whether the writekey is present or not outside of {{{_encrypt_rw_uri()}}}. Add doc about writekey to docstring.
fixes #967

src/allmydata/dirnode.py
src/allmydata/nodemaker.py
src/allmydata/test/test_dirnode.py

index 9d41af19955d06cb3e5590c0eae30da6aa2603af..022a6e00f6e4d602c805d34ac3ae22d3fe5f89b5 100644 (file)
@@ -171,12 +171,10 @@ class Adder:
         new_contents = self.node._pack_contents(children)
         return new_contents
 
+def _encrypt_rw_uri(writekey, rw_uri):
+    precondition(isinstance(rw_uri, str), rw_uri)
+    precondition(isinstance(writekey, str), writekey)
 
-def _encrypt_rw_uri(filenode, rw_uri):
-    assert isinstance(rw_uri, str)
-    writekey = filenode.get_writekey()
-    if not writekey:
-        return ""
     salt = hashutil.mutable_rwcap_salt_hash(rw_uri)
     key = hashutil.mutable_rwcap_key_hash(salt, writekey)
     cryptor = AES(key)
@@ -187,8 +185,7 @@ def _encrypt_rw_uri(filenode, rw_uri):
     # The MAC is not checked by readers in Tahoe >= 1.3.0, but we still
     # produce it for the sake of older readers.
 
-
-def pack_children(filenode, childrenx, deep_immutable=False):
+def pack_children(childrenx, writekey, deep_immutable=False):
     # initial_children must have metadata (i.e. {} instead of None)
     children = {}
     for (namex, (node, metadata)) in childrenx.iteritems():
@@ -196,10 +193,11 @@ def pack_children(filenode, childrenx, deep_immutable=False):
                      "directory creation requires metadata to be a dict, not None", metadata)
         children[normalize(namex)] = (node, metadata)
 
-    return _pack_normalized_children(filenode, children, deep_immutable=deep_immutable)
+    return _pack_normalized_children(children, writekey=writekey, deep_immutable=deep_immutable)
 
 
-def _pack_normalized_children(filenode, children, deep_immutable=False):
+ZERO_LEN_NETSTR=netstring('')
+def _pack_normalized_children(children, writekey, deep_immutable=False):
     """Take a dict that maps:
          children[unicode_nfc_name] = (IFileSystemNode, metadata_dict)
     and pack it into a single string, for use as the contents of the backing
@@ -208,9 +206,13 @@ def _pack_normalized_children(filenode, children, deep_immutable=False):
     as the pre-packed entry, which is faster than re-packing everything each
     time.
 
+    If writekey is provided then I will superencrypt the child's writecap with
+    writekey.
+
     If deep_immutable is True, I will require that all my children are deeply
     immutable, and will raise a MustBeDeepImmutableError if not.
     """
+    precondition((writekey is None) or isinstance(writekey, str), writekey)
 
     has_aux = isinstance(children, AuxValueDict)
     entries = []
@@ -231,7 +233,7 @@ def _pack_normalized_children(filenode, children, deep_immutable=False):
             if rw_uri is None:
                 rw_uri = ""
             assert isinstance(rw_uri, str), rw_uri
-            
+
             # should be prevented by MustBeDeepImmutableError check above
             assert not (rw_uri and deep_immutable)
 
@@ -239,9 +241,13 @@ def _pack_normalized_children(filenode, children, deep_immutable=False):
             if ro_uri is None:
                 ro_uri = ""
             assert isinstance(ro_uri, str), ro_uri
+            if writekey is not None:
+                writecap = netstring(_encrypt_rw_uri(writekey, rw_uri))
+            else:
+                writecap = ZERO_LEN_NETSTR
             entry = "".join([netstring(name.encode("utf-8")),
                              netstring(strip_prefix_for_ro(ro_uri, deep_immutable)),
-                             netstring(_encrypt_rw_uri(filenode, rw_uri)),
+                             writecap,
                              netstring(simplejson.dumps(metadata))])
         entries.append(netstring(entry))
     return "".join(entries)
@@ -368,7 +374,7 @@ class DirectoryNode:
 
     def _pack_contents(self, children):
         # expects children in the same format as _unpack_contents returns
-        return _pack_normalized_children(self._node, children)
+        return _pack_normalized_children(children, self._node.get_writekey())
 
     def is_readonly(self):
         return self._node.is_readonly()
index 3d0819e82b640a50825a40b8adf5a8a7200ce95e..c852f68888a675f7c9c47eaad63d25da729c6d06 100644 (file)
@@ -8,10 +8,6 @@ from allmydata.dirnode import DirectoryNode, pack_children
 from allmydata.unknown import UnknownNode
 from allmydata import uri
 
-class DummyImmutableFileNode:
-    def get_writekey(self):
-        return None
-
 class NodeMaker:
     implements(INodeMaker)
 
@@ -47,7 +43,7 @@ class NodeMaker:
         # this returns synchronously. It starts with a "cap string".
         assert isinstance(writecap, (str, type(None))), type(writecap)
         assert isinstance(readcap,  (str, type(None))), type(readcap)
-        
+
         bigcap = writecap or readcap
         if not bigcap:
             # maybe the writecap was hidden because we're in a readonly
@@ -97,15 +93,14 @@ class NodeMaker:
 
     def create_new_mutable_directory(self, initial_children={}):
         d = self.create_mutable_file(lambda n:
-                                     pack_children(n, initial_children))
+                                     pack_children(initial_children, n.get_writekey()))
         d.addCallback(self._create_dirnode)
         return d
 
     def create_immutable_directory(self, children, convergence=None):
         if convergence is None:
             convergence = self.secret_holder.get_convergence_secret()
-        n = DummyImmutableFileNode() # writekey=None
-        packed = pack_children(n, children, deep_immutable=True)
+        packed = pack_children(children, None, deep_immutable=True)
         uploadable = Data(packed, convergence)
         d = self.uploader.upload(uploadable, history=self.history)
         d.addCallback(lambda results: self.create_from_cap(None, results.uri))
index 14a613ad60280027bc07009579c9f0fc66e3fcd1..d5d01afd279dbd06d1354e770a0b64b758ed654d 100644 (file)
@@ -1246,33 +1246,33 @@ class Packing(unittest.TestCase):
 
         kids = self._make_kids(nm, ["imm", "lit", "write", "read",
                                     "dirwrite", "dirread"])
-        packed = dirnode.pack_children(fn, kids, deep_immutable=False)
+        packed = dirnode.pack_children(kids, fn.get_writekey(), deep_immutable=False)
         self.failUnlessIn("lit", packed)
 
         kids = self._make_kids(nm, ["imm", "lit"])
-        packed = dirnode.pack_children(fn, kids, deep_immutable=True)
+        packed = dirnode.pack_children(kids, fn.get_writekey(), deep_immutable=True)
         self.failUnlessIn("lit", packed)
 
         kids = self._make_kids(nm, ["imm", "lit", "write"])
         self.failUnlessRaises(dirnode.MustBeDeepImmutableError,
                               dirnode.pack_children,
-                              fn, kids, deep_immutable=True)
+                              kids, fn.get_writekey(), deep_immutable=True)
 
         # read-only is not enough: all children must be immutable
         kids = self._make_kids(nm, ["imm", "lit", "read"])
         self.failUnlessRaises(dirnode.MustBeDeepImmutableError,
                               dirnode.pack_children,
-                              fn, kids, deep_immutable=True)
+                              kids, fn.get_writekey(), deep_immutable=True)
 
         kids = self._make_kids(nm, ["imm", "lit", "dirwrite"])
         self.failUnlessRaises(dirnode.MustBeDeepImmutableError,
                               dirnode.pack_children,
-                              fn, kids, deep_immutable=True)
+                              kids, fn.get_writekey(), deep_immutable=True)
 
         kids = self._make_kids(nm, ["imm", "lit", "dirread"])
         self.failUnlessRaises(dirnode.MustBeDeepImmutableError,
                               dirnode.pack_children,
-                              fn, kids, deep_immutable=True)
+                              kids, fn.get_writekey(), deep_immutable=True)
 
 class FakeMutableFile:
     implements(IMutableFileNode)