]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blobdiff - src/allmydata/nodemaker.py
Implementation, tests and docs for blacklists. This version allows listing directorie...
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / nodemaker.py
index 495070f944e9670e56d7921a83748bb99563c8ab..fbf527c2a8c18658aa0921eec786abfdc20bd64b 100644 (file)
@@ -1,32 +1,33 @@
 import weakref
 from zope.interface import implements
 from allmydata.util.assertutil import precondition
-from allmydata.interfaces import INodeMaker, NotDeepImmutableError
-from allmydata.immutable.filenode import ImmutableFileNode, LiteralFileNode
+from allmydata.interfaces import INodeMaker, SDMF_VERSION
+from allmydata.immutable.literal import LiteralFileNode
+from allmydata.immutable.filenode import ImmutableFileNode, CiphertextFileNode
 from allmydata.immutable.upload import Data
 from allmydata.mutable.filenode import MutableFileNode
+from allmydata.mutable.publish import MutableData
 from allmydata.dirnode import DirectoryNode, pack_children
 from allmydata.unknown import UnknownNode
+from allmydata.blacklist import ProhibitedNode
 from allmydata import uri
 
-class DummyImmutableFileNode:
-    def get_writekey(self):
-        return None
 
 class NodeMaker:
     implements(INodeMaker)
 
     def __init__(self, storage_broker, secret_holder, history,
-                 uploader, downloader, download_cache_dirman,
-                 default_encoding_parameters, key_generator):
+                 uploader, terminator,
+                 default_encoding_parameters, key_generator,
+                 blacklist=None):
         self.storage_broker = storage_broker
         self.secret_holder = secret_holder
         self.history = history
         self.uploader = uploader
-        self.downloader = downloader
-        self.download_cache_dirman = download_cache_dirman
+        self.terminator = terminator
         self.default_encoding_parameters = default_encoding_parameters
         self.key_generator = key_generator
+        self.blacklist = blacklist
 
         self._node_cache = weakref.WeakValueDictionary() # uri -> node
 
@@ -34,8 +35,10 @@ class NodeMaker:
         return LiteralFileNode(cap)
     def _create_immutable(self, cap):
         return ImmutableFileNode(cap, self.storage_broker, self.secret_holder,
-                                 self.downloader, self.history,
-                                 self.download_cache_dirman)
+                                 self.terminator, self.history)
+    def _create_immutable_verifier(self, cap):
+        return CiphertextFileNode(cap, self.storage_broker, self.secret_holder,
+                                  self.terminator, self.history)
     def _create_mutable(self, cap):
         n = MutableFileNode(self.storage_broker, self.secret_holder,
                             self.default_encoding_parameters,
@@ -44,80 +47,97 @@ class NodeMaker:
     def _create_dirnode(self, filenode):
         return DirectoryNode(filenode, self, self.uploader)
 
-    def create_from_cap(self, writecap, readcap=None):
+    def create_from_cap(self, writecap, readcap=None, deep_immutable=False, name=u"<unknown name>"):
         # 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
             # directory, and the future cap format doesn't have a readcap, or
             # something.
-            return UnknownNode(writecap, readcap)
-        if bigcap in self._node_cache:
-            return self._node_cache[bigcap]
-        cap = uri.from_string(bigcap)
-        node = self._create_from_cap(cap)
-        if node:
-            self._node_cache[bigcap] = node  # note: WeakValueDictionary
+            return UnknownNode(None, None)  # deep_immutable and name not needed
+
+        # The name doesn't matter for caching since it's only used in the error
+        # attribute of an UnknownNode, and we don't cache those.
+        if deep_immutable:
+            memokey = "I" + bigcap
         else:
-            node = UnknownNode(writecap, readcap) # don't cache UnknownNode
+            memokey = "M" + bigcap
+        if memokey in self._node_cache:
+            node = self._node_cache[memokey]
+        else:
+            cap = uri.from_string(bigcap, deep_immutable=deep_immutable,
+                                  name=name)
+            node = self._create_from_single_cap(cap)
+            if node:
+                self._node_cache[memokey] = node  # note: WeakValueDictionary
+            else:
+                # don't cache UnknownNode
+                node = UnknownNode(writecap, readcap,
+                                   deep_immutable=deep_immutable, name=name)
+
+        if self.blacklist:
+            si = node.get_storage_index()
+            # if this node is blacklisted, return the reason, otherwise return None
+            reason = self.blacklist.check_storageindex(si)
+            if reason is not None:
+                # The original node object is cached above, not the ProhibitedNode wrapper.
+                # This ensures that removing the blacklist entry will make the node
+                # accessible if create_from_cap is called again.
+                node = ProhibitedNode(node, reason)
         return node
 
-    def _create_from_cap(self, cap):
-        # This starts with a "cap instance"
+    def _create_from_single_cap(self, cap):
         if isinstance(cap, uri.LiteralFileURI):
             return self._create_lit(cap)
         if isinstance(cap, uri.CHKFileURI):
             return self._create_immutable(cap)
-        if isinstance(cap, (uri.ReadonlySSKFileURI, uri.WriteableSSKFileURI)):
+        if isinstance(cap, uri.CHKFileVerifierURI):
+            return self._create_immutable_verifier(cap)
+        if isinstance(cap, (uri.ReadonlySSKFileURI, uri.WriteableSSKFileURI,
+                            uri.WritableMDMFFileURI, uri.ReadonlyMDMFFileURI)):
             return self._create_mutable(cap)
         if isinstance(cap, (uri.DirectoryURI,
                             uri.ReadonlyDirectoryURI,
                             uri.ImmutableDirectoryURI,
-                            uri.LiteralDirectoryURI)):
-            filenode = self._create_from_cap(cap.get_filenode_cap())
+                            uri.LiteralDirectoryURI,
+                            uri.MDMFDirectoryURI,
+                            uri.ReadonlyMDMFDirectoryURI)):
+            filenode = self._create_from_single_cap(cap.get_filenode_cap())
             return self._create_dirnode(filenode)
         return None
 
-    def create_mutable_file(self, contents=None, keysize=None):
+    def create_mutable_file(self, contents=None, keysize=None,
+                            version=SDMF_VERSION):
         n = MutableFileNode(self.storage_broker, self.secret_holder,
                             self.default_encoding_parameters, self.history)
         d = self.key_generator.generate(keysize)
-        d.addCallback(n.create_with_keys, contents)
+        d.addCallback(n.create_with_keys, contents, version=version)
         d.addCallback(lambda res: n)
         return d
 
-    def create_new_mutable_directory(self, initial_children={}):
-        # initial_children must have metadata (i.e. {} instead of None), and
-        # should not contain UnknownNodes
+    def create_new_mutable_directory(self, initial_children={},
+                                     version=SDMF_VERSION):
+        # initial_children must have metadata (i.e. {} instead of None)
         for (name, (node, metadata)) in initial_children.iteritems():
-            precondition(not isinstance(node, UnknownNode),
-                         "create_new_mutable_directory does not accept UnknownNode", node)
             precondition(isinstance(metadata, dict),
                          "create_new_mutable_directory requires metadata to be a dict, not None", metadata)
+            node.raise_error()
         d = self.create_mutable_file(lambda n:
-                                     pack_children(n, initial_children))
+                                     MutableData(pack_children(initial_children,
+                                                    n.get_writekey())),
+                                     version=version)
         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()
-        for (name, (node, metadata)) in children.iteritems():
-            precondition(not isinstance(node, UnknownNode),
-                         "create_immutable_directory does not accept UnknownNode", node)
-            precondition(isinstance(metadata, dict),
-                         "create_immutable_directory requires metadata to be a dict, not None", metadata)
-            if node.is_mutable():
-                raise NotDeepImmutableError("%s is not immutable" % (node,))
-        n = DummyImmutableFileNode() # writekey=None
-        packed = pack_children(n, children)
+        packed = pack_children(children, None, deep_immutable=True)
         uploadable = Data(packed, convergence)
         d = self.uploader.upload(uploadable, history=self.history)
-        def _uploaded(results):
-            filecap = self.create_from_cap(results.uri)
-            return filecap
-        d.addCallback(_uploaded)
+        d.addCallback(lambda results: self.create_from_cap(None, results.uri))
         d.addCallback(self._create_dirnode)
         return d