예제 #1
0
    def move_child_to(self, current_child_namex, new_parent,
                      new_child_namex=None, overwrite=True):
        """
        I take one of my child links and move it to a new parent. The child
        link is referenced by name. In the new parent, the child link will live
        at 'new_child_namex', which defaults to 'current_child_namex'. I return
        a Deferred that fires when the operation finishes.
        'new_child_namex' and 'current_child_namex' need not be normalized.

        The overwrite parameter may be True (overwrite any existing child),
        False (error if the new child link already exists), or "only-files"
        (error if the new child link exists and points to a directory).
        """
        if self.is_readonly() or new_parent.is_readonly():
            return defer.fail(NotWriteableError())

        current_child_name = normalize(current_child_namex)
        if new_child_namex is None:
            new_child_name = current_child_name
        else:
            new_child_name = normalize(new_child_namex)

        from_uri = self.get_write_uri()
        if new_parent.get_write_uri() == from_uri and new_child_name == current_child_name:
            # needed for correctness, otherwise we would delete the child
            return defer.succeed("redundant rename/relink")

        d = self.get_child_and_metadata(current_child_name)
        def _got_child(child_and_metadata):
            (child, metadata) = child_and_metadata
            return new_parent.set_node(new_child_name, child, metadata,
                                       overwrite=overwrite)
        d.addCallback(_got_child)
        d.addCallback(lambda child: self.delete(current_child_name))
        return d
예제 #2
0
 def has_child(self, namex):
     """I return a Deferred that fires with a boolean, True if there
     exists a child of the given name, False if not."""
     name = normalize(namex)
     d = self._read()
     d.addCallback(lambda children: children.has_key(name))
     return d
예제 #3
0
 def get(self, namex):
     """I return a Deferred that fires with the named child node,
     which is an IFilesystemNode."""
     name = normalize(namex)
     d = self._read()
     d.addCallback(self._get, name)
     return d
예제 #4
0
    def modify(self, old_contents, servermap, first_time):
        children = self.node._unpack_contents(old_contents)
        now = time.time()
        for (namex, (child, new_metadata)) in self.entries.iteritems():
            name = normalize(namex)
            precondition(IFilesystemNode.providedBy(child), child)

            # Strictly speaking this is redundant because we would raise the
            # error again in _pack_normalized_children.
            child.raise_error()

            metadata = None
            if name in children:
                if not self.overwrite:
                    raise ExistingChildError("child %s already exists" % quote_output(name, encoding='utf-8'))

                if self.overwrite == "only-files" and IDirectoryNode.providedBy(children[name][0]):
                    raise ExistingChildError("child %s already exists as a directory" % quote_output(name, encoding='utf-8'))
                metadata = children[name][1].copy()

            metadata = update_metadata(metadata, new_metadata, now)
            if self.create_readonly_node and metadata.get('no-write', False):
                child = self.create_readonly_node(child, name)

            children[name] = (child, metadata)
        new_contents = self.node._pack_contents(children)
        return new_contents
예제 #5
0
    def test_mkdir_open_exists_abspath_listdir_expanduser(self):
        skip_if_cannot_represent_filename(lumiere_nfc)

        try:
            os.mkdir(lumiere_nfc)
        except EnvironmentError as e:
            raise unittest.SkipTest("%r\nIt is possible that the filesystem on which this test is being run "
                                    "does not support Unicode, even though the platform does." % (e,))

        fn = lumiere_nfc + u'/' + lumiere_nfc + u'.txt'
        open(fn, 'wb').close()
        self.failUnless(os.path.exists(fn))
        if PY2:
            getcwdu = os.getcwdu
        else:
            getcwdu = os.getcwd
        self.failUnless(os.path.exists(os.path.join(getcwdu(), fn)))
        filenames = listdir_unicode(lumiere_nfc)

        # We only require that the listing includes a filename that is canonically equivalent
        # to lumiere_nfc (on Mac OS X, it will be the NFD equivalent).
        self.failUnlessIn(lumiere_nfc + u".txt", set([encodingutil.normalize(fname) for fname in filenames]))

        expanded = fileutil.expanduser(u"~/" + lumiere_nfc)
        self.failIfIn(u"~", expanded)
        self.failUnless(expanded.endswith(lumiere_nfc), expanded)
예제 #6
0
    def test_listdir_unicode(self):
        if 'dirlist' not in dir(self):
            return

        try:
            u"test".encode(self.filesystem_encoding)
        except (LookupError, AttributeError):
            raise unittest.SkipTest("This platform does not support the '%s' filesystem encoding "
                                    "that we are testing for the benefit of a different platform."
                                    % (self.filesystem_encoding,))

        def call_os_listdir(path):
            if PY2:
                return self.dirlist
            else:
                # Python 3 always lists unicode filenames:
                return [d.decode(self.filesystem_encoding) if isinstance(d, bytes)
                        else d
                        for d in self.dirlist]

        self.patch(os, 'listdir', call_os_listdir)

        def call_sys_getfilesystemencoding():
            return self.filesystem_encoding
        self.patch(sys, 'getfilesystemencoding', call_sys_getfilesystemencoding)

        _reload()
        filenames = listdir_unicode(u'/dummy')

        self.failUnlessEqual(set([encodingutil.normalize(fname) for fname in filenames]),
                             set(TEST_FILENAMES))
예제 #7
0
    def _unpack_contents(self, data):
        # the directory is serialized as a list of netstrings, one per child.
        # Each child is serialized as a list of four netstrings: (name, ro_uri,
        # rwcapdata, metadata), in which the name, ro_uri, metadata are in
        # cleartext. The 'name' is UTF-8 encoded, and should be normalized to NFC.
        # The rwcapdata is formatted as:
        # pack("16ss32s", iv, AES(H(writekey+iv), plaintext_rw_uri), mac)
        assert isinstance(data, str), (repr(data), type(data))
        # an empty directory is serialized as an empty string
        if data == "":
            return AuxValueDict()
        writeable = not self.is_readonly()
        mutable = self.is_mutable()
        children = AuxValueDict()
        position = 0
        while position < len(data):
            entries, position = split_netstring(data, 1, position)
            entry = entries[0]
            (namex_utf8, ro_uri, rwcapdata, metadata_s), subpos = split_netstring(entry, 4)
            if not mutable and len(rwcapdata) > 0:
                raise ValueError("the rwcapdata field of a dirnode in an immutable directory was not empty")

            # A name containing characters that are unassigned in one version of Unicode might
            # not be normalized wrt a later version. See the note in section 'Normalization Stability'
            # at <http://unicode.org/policies/stability_policy.html>.
            # Therefore we normalize names going both in and out of directories.
            name = normalize(namex_utf8.decode("utf-8"))

            rw_uri = ""
            if writeable:
                rw_uri = self._decrypt_rwcapdata(rwcapdata)

            # Since the encryption uses CTR mode, it currently leaks the length of the
            # plaintext rw_uri -- and therefore whether it is present, i.e. whether the
            # dirnode is writeable (ticket #925). By stripping trailing spaces in
            # Tahoe >= 1.6.0, we may make it easier for future versions to plug this leak.
            # ro_uri is treated in the same way for consistency.
            # rw_uri and ro_uri will be either None or a non-empty string.

            rw_uri = rw_uri.rstrip(' ') or None
            ro_uri = ro_uri.rstrip(' ') or None

            try:
                child = self._create_and_validate_node(rw_uri, ro_uri, name)
                if mutable or child.is_allowed_in_immutable_directory():
                    metadata = json.loads(metadata_s)
                    assert isinstance(metadata, dict)
                    children[name] = (child, metadata)
                    children.set_with_aux(name, (child, metadata), auxilliary=entry)
                else:
                    log.msg(format="mutable cap for child %(name)s unpacked from an immutable directory",
                            name=quote_output(name, encoding='utf-8'),
                            facility="tahoe.webish", level=log.UNUSUAL)
            except CapConstraintError as e:
                log.msg(format="unmet constraint on cap for child %(name)s unpacked from a directory:\n"
                               "%(message)s", message=e.args[0], name=quote_output(name, encoding='utf-8'),
                               facility="tahoe.webish", level=log.UNUSUAL)

        return children
예제 #8
0
 def get_child_and_metadata(self, namex):
     """I return a Deferred that fires with the (node, metadata) pair for
     the named child. The node is an IFilesystemNode, and the metadata
     is a dictionary."""
     name = normalize(namex)
     d = self._read()
     d.addCallback(self._get_with_metadata, name)
     return d
예제 #9
0
def pack_children(childrenx, writekey, deep_immutable=False):
    # initial_children must have metadata (i.e. {} instead of None)
    children = {}
    for (namex, (node, metadata)) in childrenx.iteritems():
        precondition(isinstance(metadata, dict),
                     "directory creation requires metadata to be a dict, not None", metadata)
        children[normalize(namex)] = (node, metadata)

    return _pack_normalized_children(children, writekey=writekey, deep_immutable=deep_immutable)
예제 #10
0
 def set_metadata_for(self, namex, metadata):
     name = normalize(namex)
     if self.is_readonly():
         return defer.fail(NotWriteableError())
     assert isinstance(metadata, dict)
     s = MetadataSetter(self, name, metadata,
                        create_readonly_node=self._create_readonly_node)
     d = self._node.modify(s.modify)
     d.addCallback(lambda res: self)
     return d
예제 #11
0
    def add_file(self, namex, uploadable, metadata=None, overwrite=True, progress=None):
        """I upload a file (using the given IUploadable), then attach the
        resulting FileNode to the directory at the given name. I return a
        Deferred that fires (with the IFileNode of the uploaded file) when
        the operation completes."""
        with ADD_FILE(name=namex, metadata=metadata, overwrite=overwrite).context():
            name = normalize(namex)
            if self.is_readonly():
                d = DeferredContext(defer.fail(NotWriteableError()))
            else:
                # XXX should pass reactor arg
                d = DeferredContext(self._uploader.upload(uploadable, progress=progress))
                d.addCallback(lambda results:
                              self._create_and_validate_node(results.get_uri(), None,
                                                             name))
                d.addCallback(lambda node:
                              self.set_node(name, node, metadata, overwrite))

        return d.addActionFinish()
예제 #12
0
 def create_subdirectory(self, namex, initial_children={}, overwrite=True,
                         mutable=True, mutable_version=None, metadata=None):
     name = normalize(namex)
     if self.is_readonly():
         return defer.fail(NotWriteableError())
     if mutable:
         if mutable_version:
             d = self._nodemaker.create_new_mutable_directory(initial_children,
                                                              version=mutable_version)
         else:
             d = self._nodemaker.create_new_mutable_directory(initial_children)
     else:
         # mutable version doesn't make sense for immmutable directories.
         assert mutable_version is None
         d = self._nodemaker.create_immutable_directory(initial_children)
     def _created(child):
         entries = {name: (child, metadata)}
         a = Adder(self, entries, overwrite=overwrite,
                   create_readonly_node=self._create_readonly_node)
         d = self._node.modify(a.modify)
         d.addCallback(lambda res: child)
         return d
     d.addCallback(_created)
     return d
예제 #13
0
 def get_metadata_for(self, namex):
     name = normalize(namex)
     d = self._read()
     d.addCallback(lambda children: children[name][1])
     return d
예제 #14
0
 def __init__(self, node, namex, metadata, create_readonly_node=None):
     self.node = node
     self.name = normalize(namex)
     self.metadata = metadata
     self.create_readonly_node = create_readonly_node
예제 #15
0
 def __init__(self, node, namex, must_exist=True, must_be_directory=False, must_be_file=False):
     self.node = node
     self.name = normalize(namex)
     self.must_exist = must_exist
     self.must_be_directory = must_be_directory
     self.must_be_file = must_be_file