2 from zope.interface import implements
3 from twisted.internet import defer
4 from allmydata.interfaces import IFilesystemNode, MustNotBeUnknownRWError, \
5 MustBeDeepImmutableError
6 from allmydata import uri
7 from allmydata.uri import ALLEGED_READONLY_PREFIX, ALLEGED_IMMUTABLE_PREFIX
10 # See ticket #833 for design rationale of UnknownNodes.
12 def strip_prefix_for_ro(ro_uri, deep_immutable):
13 """Strip prefixes when storing an URI in a ro_uri slot."""
15 # It is possible for an alleged-immutable URI to be put into a
16 # mutable directory. In that case the ALLEGED_IMMUTABLE_PREFIX
17 # should not be stripped. In other cases, the prefix can safely
18 # be stripped because it is implied by the context.
20 if ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX):
21 if not deep_immutable:
23 return ro_uri[len(ALLEGED_IMMUTABLE_PREFIX):]
24 elif ro_uri.startswith(ALLEGED_READONLY_PREFIX):
25 return ro_uri[len(ALLEGED_READONLY_PREFIX):]
30 implements(IFilesystemNode)
32 def __init__(self, given_rw_uri, given_ro_uri, deep_immutable=False,
33 name=u"<unknown name>"):
34 assert given_rw_uri is None or isinstance(given_rw_uri, str)
35 assert given_ro_uri is None or isinstance(given_ro_uri, str)
36 given_rw_uri = given_rw_uri or None
37 given_ro_uri = given_ro_uri or None
39 # We don't raise errors when creating an UnknownNode; we instead create an
40 # opaque node (with rw_uri and ro_uri both None) that records the error.
41 # This avoids breaking operations that never store the opaque node.
42 # Note that this means that if a stored dirnode has only a rw_uri, it
43 # might be dropped. Any future "write-only" cap formats should have a dummy
44 # unusable readcap to stop that from happening.
47 self.rw_uri = self.ro_uri = None
50 if given_rw_uri.startswith(ALLEGED_IMMUTABLE_PREFIX) and not given_ro_uri:
51 # We needed an immutable cap, and were given one. It was given in the
52 # rw_uri slot, but that's fine; we'll move it to ro_uri below.
54 elif not given_ro_uri:
55 self.error = MustNotBeUnknownRWError("cannot attach unknown rw cap as immutable child",
57 return # node will be opaque
59 # We could report either error, but this probably makes more sense.
60 self.error = MustBeDeepImmutableError("cannot attach unknown rw cap as immutable child",
62 return # node will be opaque
65 # We were given a single cap argument, or a rw_uri with no ro_uri.
67 if not (given_rw_uri.startswith(ALLEGED_READONLY_PREFIX)
68 or given_rw_uri.startswith(ALLEGED_IMMUTABLE_PREFIX)):
69 # If the single cap is unprefixed, then we cannot tell whether it is a
70 # writecap, and we don't know how to diminish it to a readcap if it is one.
71 # If it didn't *already* have at least an ALLEGED_READONLY_PREFIX, then
72 # prefixing it would be a bad idea because we have been given no reason
73 # to believe that it is a readcap, so we might be letting a client
74 # inadvertently grant excess write authority.
75 self.error = MustNotBeUnknownRWError("cannot attach unknown rw cap as child",
77 return # node will be opaque
79 # OTOH, if the single cap already had a prefix (which is of the required
80 # strength otherwise an error would have been thrown above), then treat it
81 # as though it had been given in the ro_uri slot. This has a similar effect
82 # to the use for known caps of 'bigcap = writecap or readcap' in
83 # nodemaker.py: create_from_cap. It enables copying of unknown readcaps to
84 # work in as many cases as we can securely allow.
85 given_ro_uri = given_rw_uri
87 elif given_ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX):
88 # Strange corner case: we were given a cap in both slots, with the ro_uri
89 # alleged to be immutable. A real immutable object wouldn't have a writecap.
90 self.error = MustBeDeepImmutableError("cannot accept a child entry that specifies "
91 "both rw_uri, and ro_uri with an imm. prefix",
93 return # node will be opaque
95 # If the ro_uri definitely fails the constraint, it should be treated as opaque and
98 read_cap = uri.from_string(given_ro_uri, deep_immutable=deep_immutable, name=name)
99 if isinstance(read_cap, uri.UnknownURI):
100 self.error = read_cap.get_error()
102 assert self.rw_uri is None and self.ro_uri is None
106 assert self.rw_uri is None
107 # strengthen the constraint on ro_uri to ALLEGED_IMMUTABLE_PREFIX
109 if given_ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX):
110 self.ro_uri = given_ro_uri
111 elif given_ro_uri.startswith(ALLEGED_READONLY_PREFIX):
112 self.ro_uri = ALLEGED_IMMUTABLE_PREFIX + given_ro_uri[len(ALLEGED_READONLY_PREFIX):]
114 self.ro_uri = ALLEGED_IMMUTABLE_PREFIX + given_ro_uri
116 # not immutable, so a writecap is allowed
117 self.rw_uri = given_rw_uri
118 # strengthen the constraint on ro_uri to ALLEGED_READONLY_PREFIX
120 if (given_ro_uri.startswith(ALLEGED_READONLY_PREFIX) or
121 given_ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX)):
122 self.ro_uri = given_ro_uri
124 self.ro_uri = ALLEGED_READONLY_PREFIX + given_ro_uri
127 return uri.UnknownURI(self.rw_uri or self.ro_uri)
129 def get_readcap(self):
130 return uri.UnknownURI(self.ro_uri)
132 def is_readonly(self):
133 raise AssertionError("an UnknownNode might be either read-only or "
134 "read/write, so we shouldn't be calling is_readonly")
136 def is_mutable(self):
137 raise AssertionError("an UnknownNode might be either mutable or immutable, "
138 "so we shouldn't be calling is_mutable")
140 def is_unknown(self):
143 def is_allowed_in_immutable_directory(self):
144 # An UnknownNode consisting only of a ro_uri is allowed in an
145 # immutable directory, even though we do not know that it is
146 # immutable (or even read-only), provided that no error was detected.
147 return not self.error and not self.rw_uri
149 def is_alleged_immutable(self):
150 return not self.error and not self.rw_uri and (not self.ro_uri or self.ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX))
152 def raise_error(self):
153 if self.error is not None:
157 return self.rw_uri or self.ro_uri
159 def get_write_uri(self):
162 def get_readonly_uri(self):
165 def get_storage_index(self):
168 def get_verify_cap(self):
171 def get_repair_cap(self):
177 def get_current_size(self):
178 return defer.succeed(None)
180 def check(self, monitor, verify, add_lease):
181 return defer.succeed(None)
183 def check_and_repair(self, monitor, verify, add_lease):
184 return defer.succeed(None)