]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/unknown.py
Merge pull request #236 from daira/2725.timezone-test.0
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / unknown.py
1
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
8
9
10 # See ticket #833 for design rationale of UnknownNodes.
11
12 def strip_prefix_for_ro(ro_uri, deep_immutable):
13     """Strip prefixes when storing an URI in a ro_uri slot."""
14
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.
19
20     if ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX):
21         if not deep_immutable:
22             return ro_uri
23         return ro_uri[len(ALLEGED_IMMUTABLE_PREFIX):]
24     elif ro_uri.startswith(ALLEGED_READONLY_PREFIX):
25         return ro_uri[len(ALLEGED_READONLY_PREFIX):]
26     else:
27         return ro_uri
28
29 class UnknownNode:
30     implements(IFilesystemNode)
31
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
38
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.
45
46         self.error = None
47         self.rw_uri = self.ro_uri = None
48         if given_rw_uri:
49             if deep_immutable:
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.
53                     pass
54                 elif not given_ro_uri:
55                     self.error = MustNotBeUnknownRWError("cannot attach unknown rw cap as immutable child",
56                                                          name, True)
57                     return  # node will be opaque
58                 else:
59                     # We could report either error, but this probably makes more sense.
60                     self.error = MustBeDeepImmutableError("cannot attach unknown rw cap as immutable child",
61                                                          name)
62                     return  # node will be opaque
63
64             if not given_ro_uri:
65                 # We were given a single cap argument, or a rw_uri with no ro_uri.
66
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",
76                                                          name, False)
77                     return  # node will be opaque
78
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
86                 given_rw_uri = None
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",
92                                                       name)
93                 return  # node will be opaque
94
95         # If the ro_uri definitely fails the constraint, it should be treated as opaque and
96         # the error recorded.
97         if given_ro_uri:
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()
101                 if self.error:
102                     assert self.rw_uri is None and self.ro_uri is None
103                     return
104
105         if deep_immutable:
106             assert self.rw_uri is None
107             # strengthen the constraint on ro_uri to ALLEGED_IMMUTABLE_PREFIX
108             if given_ro_uri:
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):]
113                 else:
114                     self.ro_uri = ALLEGED_IMMUTABLE_PREFIX + given_ro_uri
115         else:
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
119             if given_ro_uri:
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
123                 else:
124                     self.ro_uri = ALLEGED_READONLY_PREFIX + given_ro_uri
125
126     def get_cap(self):
127         return uri.UnknownURI(self.rw_uri or self.ro_uri)
128
129     def get_readcap(self):
130         return uri.UnknownURI(self.ro_uri)
131
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")
135
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")
139
140     def is_unknown(self):
141         return True
142
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
148
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))
151
152     def raise_error(self):
153         if self.error is not None:
154             raise self.error
155
156     def get_uri(self):
157         return self.rw_uri or self.ro_uri
158
159     def get_write_uri(self):
160         return self.rw_uri
161
162     def get_readonly_uri(self):
163         return self.ro_uri
164
165     def get_storage_index(self):
166         return None
167
168     def get_verify_cap(self):
169         return None
170
171     def get_repair_cap(self):
172         return None
173
174     def get_size(self):
175         return None
176
177     def get_current_size(self):
178         return defer.succeed(None)
179
180     def check(self, monitor, verify, add_lease):
181         return defer.succeed(None)
182
183     def check_and_repair(self, monitor, verify, add_lease):
184         return defer.succeed(None)