source: trunk/src/allmydata/nodemaker.py

Last change on this file was 9c23628, checked in by Christopher R. Wood <chris@…>, at 2024-05-03T20:55:38Z

Allow supplying keypair when creating mutable dirs

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