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