1 | import weakref |
---|
2 | from zope.interface import implementer |
---|
3 | from allmydata.util.assertutil import precondition |
---|
4 | from allmydata.interfaces import INodeMaker |
---|
5 | from allmydata.immutable.literal import LiteralFileNode |
---|
6 | from allmydata.immutable.filenode import ImmutableFileNode, CiphertextFileNode |
---|
7 | from allmydata.immutable.upload import Data |
---|
8 | from allmydata.mutable.filenode import MutableFileNode |
---|
9 | from allmydata.mutable.publish import MutableData |
---|
10 | from allmydata.dirnode import DirectoryNode, pack_children |
---|
11 | from allmydata.unknown import UnknownNode |
---|
12 | from allmydata.blacklist import ProhibitedNode |
---|
13 | from allmydata import uri |
---|
14 | |
---|
15 | |
---|
16 | @implementer(INodeMaker) |
---|
17 | class NodeMaker(object): |
---|
18 | |
---|
19 | def __init__(self, storage_broker, secret_holder, history, |
---|
20 | uploader, terminator, |
---|
21 | default_encoding_parameters, mutable_file_default, |
---|
22 | key_generator, blacklist=None): |
---|
23 | self.storage_broker = storage_broker |
---|
24 | self.secret_holder = secret_holder |
---|
25 | self.history = history |
---|
26 | self.uploader = uploader |
---|
27 | self.terminator = terminator |
---|
28 | self.default_encoding_parameters = default_encoding_parameters |
---|
29 | self.mutable_file_default = mutable_file_default |
---|
30 | self.key_generator = key_generator |
---|
31 | self.blacklist = blacklist |
---|
32 | |
---|
33 | self._node_cache = weakref.WeakValueDictionary() # uri -> node |
---|
34 | |
---|
35 | def _create_lit(self, cap): |
---|
36 | return LiteralFileNode(cap) |
---|
37 | def _create_immutable(self, cap): |
---|
38 | return ImmutableFileNode(cap, self.storage_broker, self.secret_holder, |
---|
39 | self.terminator, self.history) |
---|
40 | def _create_immutable_verifier(self, cap): |
---|
41 | return CiphertextFileNode(cap, self.storage_broker, self.secret_holder, |
---|
42 | self.terminator, self.history) |
---|
43 | def _create_mutable(self, cap): |
---|
44 | n = MutableFileNode(self.storage_broker, self.secret_holder, |
---|
45 | self.default_encoding_parameters, |
---|
46 | self.history) |
---|
47 | return n.init_from_cap(cap) |
---|
48 | def _create_dirnode(self, filenode): |
---|
49 | return DirectoryNode(filenode, self, self.uploader) |
---|
50 | |
---|
51 | def create_from_cap(self, writecap, readcap=None, deep_immutable=False, name=u"<unknown name>"): |
---|
52 | # this returns synchronously. It starts with a "cap string". |
---|
53 | assert isinstance(writecap, (str, type(None))), type(writecap) |
---|
54 | assert isinstance(readcap, (str, type(None))), type(readcap) |
---|
55 | |
---|
56 | bigcap = writecap or readcap |
---|
57 | if not bigcap: |
---|
58 | # maybe the writecap was hidden because we're in a readonly |
---|
59 | # directory, and the future cap format doesn't have a readcap, or |
---|
60 | # something. |
---|
61 | return UnknownNode(None, None) # deep_immutable and name not needed |
---|
62 | |
---|
63 | # The name doesn't matter for caching since it's only used in the error |
---|
64 | # attribute of an UnknownNode, and we don't cache those. |
---|
65 | if deep_immutable: |
---|
66 | memokey = "I" + bigcap |
---|
67 | else: |
---|
68 | memokey = "M" + bigcap |
---|
69 | if memokey in self._node_cache: |
---|
70 | node = self._node_cache[memokey] |
---|
71 | else: |
---|
72 | cap = uri.from_string(bigcap, deep_immutable=deep_immutable, |
---|
73 | name=name) |
---|
74 | node = self._create_from_single_cap(cap) |
---|
75 | |
---|
76 | # node is None for an unknown URI, otherwise it is a type for which |
---|
77 | # is_mutable() is known. We avoid cacheing mutable nodes due to |
---|
78 | # ticket #1679. |
---|
79 | if node is None: |
---|
80 | # don't cache UnknownNode |
---|
81 | node = UnknownNode(writecap, readcap, |
---|
82 | deep_immutable=deep_immutable, name=name) |
---|
83 | elif node.is_mutable(): |
---|
84 | self._node_cache[memokey] = node # note: WeakValueDictionary |
---|
85 | |
---|
86 | if self.blacklist: |
---|
87 | si = node.get_storage_index() |
---|
88 | # if this node is blacklisted, return the reason, otherwise return None |
---|
89 | reason = self.blacklist.check_storageindex(si) |
---|
90 | if reason is not None: |
---|
91 | # The original node object is cached above, not the ProhibitedNode wrapper. |
---|
92 | # This ensures that removing the blacklist entry will make the node |
---|
93 | # accessible if create_from_cap is called again. |
---|
94 | node = ProhibitedNode(node, reason) |
---|
95 | return node |
---|
96 | |
---|
97 | def _create_from_single_cap(self, cap): |
---|
98 | if isinstance(cap, uri.LiteralFileURI): |
---|
99 | return self._create_lit(cap) |
---|
100 | if isinstance(cap, uri.CHKFileURI): |
---|
101 | return self._create_immutable(cap) |
---|
102 | if isinstance(cap, uri.CHKFileVerifierURI): |
---|
103 | return self._create_immutable_verifier(cap) |
---|
104 | if isinstance(cap, (uri.ReadonlySSKFileURI, uri.WriteableSSKFileURI, |
---|
105 | uri.WriteableMDMFFileURI, uri.ReadonlyMDMFFileURI)): |
---|
106 | return self._create_mutable(cap) |
---|
107 | if isinstance(cap, (uri.DirectoryURI, |
---|
108 | uri.ReadonlyDirectoryURI, |
---|
109 | uri.ImmutableDirectoryURI, |
---|
110 | uri.LiteralDirectoryURI, |
---|
111 | uri.MDMFDirectoryURI, |
---|
112 | uri.ReadonlyMDMFDirectoryURI)): |
---|
113 | filenode = self._create_from_single_cap(cap.get_filenode_cap()) |
---|
114 | return self._create_dirnode(filenode) |
---|
115 | return None |
---|
116 | |
---|
117 | def create_mutable_file(self, contents=None, keysize=None, version=None): |
---|
118 | if version is None: |
---|
119 | version = self.mutable_file_default |
---|
120 | n = MutableFileNode(self.storage_broker, self.secret_holder, |
---|
121 | self.default_encoding_parameters, self.history) |
---|
122 | d = self.key_generator.generate(keysize) |
---|
123 | d.addCallback(n.create_with_keys, contents, version=version) |
---|
124 | d.addCallback(lambda res: n) |
---|
125 | return d |
---|
126 | |
---|
127 | def create_new_mutable_directory(self, initial_children={}, version=None): |
---|
128 | # initial_children must have metadata (i.e. {} instead of None) |
---|
129 | for (name, (node, metadata)) in initial_children.iteritems(): |
---|
130 | precondition(isinstance(metadata, dict), |
---|
131 | "create_new_mutable_directory requires metadata to be a dict, not None", metadata) |
---|
132 | node.raise_error() |
---|
133 | d = self.create_mutable_file(lambda n: |
---|
134 | MutableData(pack_children(initial_children, |
---|
135 | n.get_writekey())), |
---|
136 | version=version) |
---|
137 | d.addCallback(self._create_dirnode) |
---|
138 | return d |
---|
139 | |
---|
140 | def create_immutable_directory(self, children, convergence=None): |
---|
141 | if convergence is None: |
---|
142 | convergence = self.secret_holder.get_convergence_secret() |
---|
143 | packed = pack_children(children, None, deep_immutable=True) |
---|
144 | uploadable = Data(packed, convergence) |
---|
145 | # XXX should pass reactor arg |
---|
146 | d = self.uploader.upload(uploadable) |
---|
147 | d.addCallback(lambda results: |
---|
148 | self.create_from_cap(None, results.get_uri())) |
---|
149 | d.addCallback(self._create_dirnode) |
---|
150 | return d |
---|