def _pack_normalized_children(children, writekey, deep_immutable=False): """Take a dict that maps: children[unicode_nfc_name] = (IFileSystemNode, metadata_dict) and pack it into a single string, for use as the contents of the backing file. This is the same format as is returned by _unpack_contents. I also accept an AuxValueDict, in which case I'll use the auxilliary cached data as the pre-packed entry, which is faster than re-packing everything each time. If writekey is provided then I will superencrypt the child's writecap with writekey. If deep_immutable is True, I will require that all my children are deeply immutable, and will raise a MustBeDeepImmutableError if not. """ precondition((writekey is None) or isinstance(writekey, str), writekey) has_aux = isinstance(children, AuxValueDict) entries = [] for name in sorted(children.keys()): assert isinstance(name, unicode) entry = None (child, metadata) = children[name] child.raise_error() if deep_immutable and not child.is_allowed_in_immutable_directory(): raise MustBeDeepImmutableError( "child %s is not allowed in an immutable directory" % quote_output(name, encoding='utf-8'), name) if has_aux: entry = children.get_aux(name) if not entry: assert IFilesystemNode.providedBy(child), (name, child) assert isinstance(metadata, dict) rw_uri = child.get_write_uri() if rw_uri is None: rw_uri = "" assert isinstance(rw_uri, str), rw_uri # should be prevented by MustBeDeepImmutableError check above assert not (rw_uri and deep_immutable) ro_uri = child.get_readonly_uri() if ro_uri is None: ro_uri = "" assert isinstance(ro_uri, str), ro_uri if writekey is not None: writecap = netstring(_encrypt_rw_uri(writekey, rw_uri)) else: writecap = ZERO_LEN_NETSTR entry = "".join([ netstring(name.encode("utf-8")), netstring(strip_prefix_for_ro(ro_uri, deep_immutable)), writecap, netstring(json.dumps(metadata)) ]) entries.append(netstring(entry)) return "".join(entries)
def from_string(u, deep_immutable=False, name=u"<unknown name>"): """Create URI from either unicode or byte string.""" if isinstance(u, unicode): u = u.encode("utf-8") if not isinstance(u, bytes): raise TypeError("URI must be unicode string or bytes: %r" % (u, )) # We allow and check ALLEGED_READONLY_PREFIX or ALLEGED_IMMUTABLE_PREFIX # on all URIs, even though we would only strictly need to do so for caps of # new formats (post Tahoe-LAFS 1.6). URIs that are not consistent with their # prefix are treated as unknown. This should be revisited when we add the # new cap formats. See ticket #833 comment:31. s = u can_be_mutable = can_be_writeable = not deep_immutable if s.startswith(ALLEGED_IMMUTABLE_PREFIX): can_be_mutable = can_be_writeable = False s = s[len(ALLEGED_IMMUTABLE_PREFIX):] elif s.startswith(ALLEGED_READONLY_PREFIX): can_be_writeable = False s = s[len(ALLEGED_READONLY_PREFIX):] error = None try: if s.startswith(b'URI:CHK:'): return CHKFileURI.init_from_string(s) elif s.startswith(b'URI:CHK-Verifier:'): return CHKFileVerifierURI.init_from_string(s) elif s.startswith(b'URI:LIT:'): return LiteralFileURI.init_from_string(s) elif s.startswith(b'URI:SSK:'): if can_be_writeable: return WriteableSSKFileURI.init_from_string(s) kind = "URI:SSK file writecap" elif s.startswith(b'URI:SSK-RO:'): if can_be_mutable: return ReadonlySSKFileURI.init_from_string(s) kind = "URI:SSK-RO readcap to a mutable file" elif s.startswith(b'URI:SSK-Verifier:'): return SSKVerifierURI.init_from_string(s) elif s.startswith(b'URI:MDMF:'): if can_be_writeable: return WriteableMDMFFileURI.init_from_string(s) kind = "URI:MDMF file writecap" elif s.startswith(b'URI:MDMF-RO:'): if can_be_mutable: return ReadonlyMDMFFileURI.init_from_string(s) kind = "URI:MDMF-RO readcap to a mutable file" elif s.startswith(b'URI:MDMF-Verifier:'): return MDMFVerifierURI.init_from_string(s) elif s.startswith(b'URI:DIR2:'): if can_be_writeable: return DirectoryURI.init_from_string(s) kind = "URI:DIR2 directory writecap" elif s.startswith(b'URI:DIR2-RO:'): if can_be_mutable: return ReadonlyDirectoryURI.init_from_string(s) kind = "URI:DIR2-RO readcap to a mutable directory" elif s.startswith(b'URI:DIR2-Verifier:'): return DirectoryURIVerifier.init_from_string(s) elif s.startswith(b'URI:DIR2-CHK:'): return ImmutableDirectoryURI.init_from_string(s) elif s.startswith(b'URI:DIR2-CHK-Verifier:'): return ImmutableDirectoryURIVerifier.init_from_string(s) elif s.startswith(b'URI:DIR2-LIT:'): return LiteralDirectoryURI.init_from_string(s) elif s.startswith(b'URI:DIR2-MDMF:'): if can_be_writeable: return MDMFDirectoryURI.init_from_string(s) kind = "URI:DIR2-MDMF directory writecap" elif s.startswith(b'URI:DIR2-MDMF-RO:'): if can_be_mutable: return ReadonlyMDMFDirectoryURI.init_from_string(s) kind = "URI:DIR2-MDMF-RO readcap to a mutable directory" elif s.startswith(b'URI:DIR2-MDMF-Verifier:'): return MDMFDirectoryURIVerifier.init_from_string(s) elif s.startswith( b'x-tahoe-future-test-writeable:') and not can_be_writeable: # For testing how future writeable caps would behave in read-only contexts. kind = "x-tahoe-future-test-writeable: testing cap" elif s.startswith( b'x-tahoe-future-test-mutable:') and not can_be_mutable: # For testing how future mutable readcaps would behave in immutable contexts. kind = "x-tahoe-future-test-mutable: testing cap" else: return UnknownURI(u) # We fell through because a constraint was not met. # Prefer to report the most specific constraint. if not can_be_mutable: error = MustBeDeepImmutableError( kind + " used in an immutable context", name) else: error = MustBeReadonlyError(kind + " used in a read-only context", name) except BadURIError as e: error = e return UnknownURI(u, error=error)
def __init__(self, given_rw_uri, given_ro_uri, deep_immutable=False, name=u"<unknown name>"): assert given_rw_uri is None or isinstance(given_rw_uri, str) assert given_ro_uri is None or isinstance(given_ro_uri, str) given_rw_uri = given_rw_uri or None given_ro_uri = given_ro_uri or None # We don't raise errors when creating an UnknownNode; we instead create an # opaque node (with rw_uri and ro_uri both None) that records the error. # This avoids breaking operations that never store the opaque node. # Note that this means that if a stored dirnode has only a rw_uri, it # might be dropped. Any future "write-only" cap formats should have a dummy # unusable readcap to stop that from happening. self.error = None self.rw_uri = self.ro_uri = None if given_rw_uri: if deep_immutable: if given_rw_uri.startswith( ALLEGED_IMMUTABLE_PREFIX) and not given_ro_uri: # We needed an immutable cap, and were given one. It was given in the # rw_uri slot, but that's fine; we'll move it to ro_uri below. pass elif not given_ro_uri: self.error = MustNotBeUnknownRWError( "cannot attach unknown rw cap as immutable child", name, True) return # node will be opaque else: # We could report either error, but this probably makes more sense. self.error = MustBeDeepImmutableError( "cannot attach unknown rw cap as immutable child", name) return # node will be opaque if not given_ro_uri: # We were given a single cap argument, or a rw_uri with no ro_uri. if not (given_rw_uri.startswith(ALLEGED_READONLY_PREFIX) or given_rw_uri.startswith(ALLEGED_IMMUTABLE_PREFIX)): # If the single cap is unprefixed, then we cannot tell whether it is a # writecap, and we don't know how to diminish it to a readcap if it is one. # If it didn't *already* have at least an ALLEGED_READONLY_PREFIX, then # prefixing it would be a bad idea because we have been given no reason # to believe that it is a readcap, so we might be letting a client # inadvertently grant excess write authority. self.error = MustNotBeUnknownRWError( "cannot attach unknown rw cap as child", name, False) return # node will be opaque # OTOH, if the single cap already had a prefix (which is of the required # strength otherwise an error would have been thrown above), then treat it # as though it had been given in the ro_uri slot. This has a similar effect # to the use for known caps of 'bigcap = writecap or readcap' in # nodemaker.py: create_from_cap. It enables copying of unknown readcaps to # work in as many cases as we can securely allow. given_ro_uri = given_rw_uri given_rw_uri = None elif given_ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX): # Strange corner case: we were given a cap in both slots, with the ro_uri # alleged to be immutable. A real immutable object wouldn't have a writecap. self.error = MustBeDeepImmutableError( "cannot accept a child entry that specifies " "both rw_uri, and ro_uri with an imm. prefix", name) return # node will be opaque # If the ro_uri definitely fails the constraint, it should be treated as opaque and # the error recorded. if given_ro_uri: read_cap = uri.from_string(given_ro_uri, deep_immutable=deep_immutable, name=name) if isinstance(read_cap, uri.UnknownURI): self.error = read_cap.get_error() if self.error: assert self.rw_uri is None and self.ro_uri is None return if deep_immutable: assert self.rw_uri is None # strengthen the constraint on ro_uri to ALLEGED_IMMUTABLE_PREFIX if given_ro_uri: if given_ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX): self.ro_uri = given_ro_uri elif given_ro_uri.startswith(ALLEGED_READONLY_PREFIX): self.ro_uri = ALLEGED_IMMUTABLE_PREFIX + given_ro_uri[ len(ALLEGED_READONLY_PREFIX):] else: self.ro_uri = ALLEGED_IMMUTABLE_PREFIX + given_ro_uri else: # not immutable, so a writecap is allowed self.rw_uri = given_rw_uri # strengthen the constraint on ro_uri to ALLEGED_READONLY_PREFIX if given_ro_uri: if (given_ro_uri.startswith(ALLEGED_READONLY_PREFIX) or given_ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX)): self.ro_uri = given_ro_uri else: self.ro_uri = ALLEGED_READONLY_PREFIX + given_ro_uri
def from_string(u, deep_immutable=False, name=u"<unknown name>"): if not isinstance(u, str): raise TypeError("unknown URI type: %s.." % str(u)[:100]) # We allow and check ALLEGED_READONLY_PREFIX or ALLEGED_IMMUTABLE_PREFIX # on all URIs, even though we would only strictly need to do so for caps of # new formats (post Tahoe-LAFS 1.6). URIs that are not consistent with their # prefix are treated as unknown. This should be revisited when we add the # new cap formats. See <http://allmydata.org/trac/tahoe/ticket/833#comment:31>. s = u can_be_mutable = can_be_writeable = not deep_immutable if s.startswith(ALLEGED_IMMUTABLE_PREFIX): can_be_mutable = can_be_writeable = False s = s[len(ALLEGED_IMMUTABLE_PREFIX):] elif s.startswith(ALLEGED_READONLY_PREFIX): can_be_writeable = False s = s[len(ALLEGED_READONLY_PREFIX):] error = None kind = "cap" try: if s.startswith('URI:CHK:'): return CHKFileURI.init_from_string(s) elif s.startswith('URI:CHK-Verifier:'): return CHKFileVerifierURI.init_from_string(s) elif s.startswith('URI:LIT:'): return LiteralFileURI.init_from_string(s) elif s.startswith('URI:SSK:'): if can_be_writeable: return WriteableSSKFileURI.init_from_string(s) kind = "URI:SSK file writecap" elif s.startswith('URI:SSK-RO:'): if can_be_mutable: return ReadonlySSKFileURI.init_from_string(s) kind = "URI:SSK-RO readcap to a mutable file" elif s.startswith('URI:SSK-Verifier:'): return SSKVerifierURI.init_from_string(s) elif s.startswith('URI:DIR2:'): if can_be_writeable: return DirectoryURI.init_from_string(s) kind = "URI:DIR2 directory writecap" elif s.startswith('URI:DIR2-RO:'): if can_be_mutable: return ReadonlyDirectoryURI.init_from_string(s) kind = "URI:DIR2-RO readcap to a mutable directory" elif s.startswith('URI:DIR2-Verifier:'): return DirectoryURIVerifier.init_from_string(s) elif s.startswith('URI:DIR2-CHK:'): return ImmutableDirectoryURI.init_from_string(s) elif s.startswith('URI:DIR2-LIT:'): return LiteralDirectoryURI.init_from_string(s) elif s.startswith('x-tahoe-future-test-writeable:') and not can_be_writeable: # For testing how future writeable caps would behave in read-only contexts. kind = "x-tahoe-future-test-writeable: testing cap" elif s.startswith('x-tahoe-future-test-mutable:') and not can_be_mutable: # For testing how future mutable readcaps would behave in immutable contexts. kind = "x-tahoe-future-test-mutable: testing cap" else: return UnknownURI(u) # We fell through because a constraint was not met. # Prefer to report the most specific constraint. if not can_be_mutable: error = MustBeDeepImmutableError(kind + " used in an immutable context", name) else: error = MustBeReadonlyError(kind + " used in a read-only context", name) except BadURIError, e: error = e