Beispiel #1
0
def make_handler_for(node, client, parentnode=None, name=None):
    if parentnode:
        assert IDirectoryNode.providedBy(parentnode)
    if IFileNode.providedBy(node):
        return FileNodeHandler(client, node, parentnode, name)
    if IDirectoryNode.providedBy(node):
        return DirectoryNodeHandler(client, node, parentnode, name)
    return UnknownNodeHandler(client, node, parentnode, name)
Beispiel #2
0
    def render_check_form(self, ctx, data):
        node = self.original
        quoted_uri = urllib.quote(node.get_uri())
        target = self.get_root(ctx) + "/uri/" + quoted_uri
        if IDirectoryNode.providedBy(node):
            target += "/"
        check = T.form(action=target, method="post",
                       enctype="multipart/form-data")[
            T.fieldset[
            T.input(type="hidden", name="t", value="check"),
            T.input(type="hidden", name="return_to", value="."),
            T.legend(class_="freeform-form-label")["Check on this object"],
            T.div[
            "Verify every bit? (EXPENSIVE):",
            T.input(type="checkbox", name="verify"),
            ],
            T.div["Repair any problems?: ",
                  T.input(type="checkbox", name="repair")],
            T.div["Add/renew lease on all shares?: ",
                  T.input(type="checkbox", name="add-lease")],
            T.div["Emit results in JSON format?: ",
                  T.input(type="checkbox", name="output", value="JSON")],

            T.input(type="submit", value="Check"),

            ]]
        return ctx.tag[check]
Beispiel #3
0
 def _populate_row(self, keys, childnode_and_metadata):
     (childnode, metadata) = childnode_and_metadata
     values = []
     isdir = bool(IDirectoryNode.providedBy(childnode))
     for key in keys:
         if key == "size":
             if isdir:
                 value = 0
             else:
                 value = childnode.get_size() or 0
         elif key == "directory":
             value = isdir
         elif key == "permissions":
             # Twisted-14.0.2 (and earlier) expected an int, and used it
             # in a rendering function that did (mode & NUMBER).
             # Twisted-15.0.0 expects a
             # twisted.python.filepath.Permissions , and calls its
             # .shorthand() method. This provides both.
             value = IntishPermissions(0o600)
         elif key == "hardlinks":
             value = 1
         elif key == "modified":
             # follow sftpd convention (i.e. linkmotime in preference to mtime)
             if "linkmotime" in metadata.get("tahoe", {}):
                 value = metadata["tahoe"]["linkmotime"]
             else:
                 value = metadata.get("mtime", 0)
         elif key == "owner":
             value = self.username
         elif key == "group":
             value = self.username
         else:
             value = "??"
         values.append(value)
     return values
    def __init__(self, client, local_path_u, db, upload_dirnode, pending_delay, clock):
        QueueMixin.__init__(self, client, local_path_u, db, 'uploader', clock, delay=pending_delay)

        self.is_ready = False

        if not IDirectoryNode.providedBy(upload_dirnode):
            raise AssertionError("The URI in '%s' does not refer to a directory."
                                 % os.path.join('private', 'magic_folder_dircap'))
        if upload_dirnode.is_unknown() or upload_dirnode.is_readonly():
            raise AssertionError("The URI in '%s' is not a writecap to a directory."
                                 % os.path.join('private', 'magic_folder_dircap'))

        self._upload_dirnode = upload_dirnode
        self._inotify = get_inotify_module()
        self._notifier = self._inotify.INotify()
        self._pending = set()  # of unicode relpaths

        self._periodic_full_scan_duration = 10 * 60 # perform a full scan every 10 minutes

        if hasattr(self._notifier, 'set_pending_delay'):
            self._notifier.set_pending_delay(pending_delay)

        # TODO: what about IN_MOVE_SELF and IN_UNMOUNT?
        #
        self.mask = ( self._inotify.IN_CREATE
                    | self._inotify.IN_CLOSE_WRITE
                    | self._inotify.IN_MOVED_TO
                    | self._inotify.IN_MOVED_FROM
                    | self._inotify.IN_DELETE
                    | self._inotify.IN_ONLYDIR
                    | IN_EXCL_UNLINK
                    )
        self._notifier.watch(self._local_filepath, mask=self.mask, callbacks=[self._notify],
                             recursive=False)#True)
Beispiel #5
0
    def add_node(self, node, path):
        dirnode.DeepStats.add_node(self, node, path)
        d = {"path": path,
             "cap": node.get_uri()}

        if IDirectoryNode.providedBy(node):
            d["type"] = "directory"
        elif IFileNode.providedBy(node):
            d["type"] = "file"
        else:
            d["type"] = "unknown"

        v = node.get_verify_cap()
        if v:
            v = v.to_string()
        d["verifycap"] = v or ""

        r = node.get_repair_cap()
        if r:
            r = r.to_string()
        d["repaircap"] = r or ""

        si = node.get_storage_index()
        if si:
            si = base32.b2a(si)
        d["storage-index"] = si or ""

        j = json.dumps(d, ensure_ascii=True)
        assert "\n" not in j
        self.req.write(j+"\n")
Beispiel #6
0
    def add_node(self, node, path):
        dirnode.DeepStats.add_node(self, node, path)
        data = {"path": path,
                "cap": node.get_uri()}

        if IDirectoryNode.providedBy(node):
            data["type"] = "directory"
        elif IFileNode.providedBy(node):
            data["type"] = "file"
        else:
            data["type"] = "unknown"

        v = node.get_verify_cap()
        if v:
            v = v.to_string()
        data["verifycap"] = v or ""

        r = node.get_repair_cap()
        if r:
            r = r.to_string()
        data["repaircap"] = r or ""

        si = node.get_storage_index()
        if si:
            si = base32.b2a(si)
        data["storage-index"] = si or ""

        if self.repair:
            d = node.check_and_repair(self.monitor, self.verify, self.add_lease)
            d.addCallback(self.add_check_and_repair, data)
        else:
            d = node.check(self.monitor, self.verify, self.add_lease)
            d.addCallback(self.add_check, data)
        d.addCallback(self.write_line)
        return d
 def _made_upload_dir(n):
     self.failUnless(IDirectoryNode.providedBy(n))
     self.upload_dirnode = n
     self.upload_dircap = n.get_uri()
     self.uploader = DropUploader(self.client, self.upload_dircap, self.local_dir.encode('utf-8'),
                                  inotify=self.inotify)
     return self.uploader.startService()
Beispiel #8
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
Beispiel #9
0
 def render_directory_writecap(self, ctx, data):
     node = self.original
     if not IDirectoryNode.providedBy(node):
         return ""
     if node.is_readonly():
         return ""
     return ctx.tag[node.get_uri()]
Beispiel #10
0
    def got_child(self, node_or_failure, ctx, name):
        req = IRequest(ctx)
        method = req.method
        nonterminal = len(req.postpath) > 1
        t = get_arg(req, "t", "").strip()
        if isinstance(node_or_failure, Failure):
            f = node_or_failure
            f.trap(NoSuchChildError)
            # No child by this name. What should we do about it?
            if nonterminal:
                if should_create_intermediate_directories(req):
                    # create intermediate directories
                    d = self.node.create_subdirectory(name)
                    d.addCallback(make_handler_for,
                                  self.client, self.node, name)
                    return d
            else:
                # terminal node
                if (method,t) in [ ("POST","mkdir"), ("PUT","mkdir"),
                                   ("POST", "mkdir-with-children"),
                                   ("POST", "mkdir-immutable") ]:
                    # final directory
                    kids = {}
                    if t in ("mkdir-with-children", "mkdir-immutable"):
                        req.content.seek(0)
                        kids_json = req.content.read()
                        kids = convert_children_json(self.client.nodemaker,
                                                     kids_json)
                    file_format = get_format(req, None)
                    mutable = True
                    mt = get_mutable_type(file_format)
                    if t == "mkdir-immutable":
                        mutable = False

                    d = self.node.create_subdirectory(name, kids,
                                                      mutable=mutable,
                                                      mutable_version=mt)
                    d.addCallback(make_handler_for,
                                  self.client, self.node, name)
                    return d
                if (method,t) in ( ("PUT",""), ("PUT","uri"), ):
                    # we were trying to find the leaf filenode (to put a new
                    # file in its place), and it didn't exist. That's ok,
                    # since that's the leaf node that we're about to create.
                    # We make a dummy one, which will respond to the PUT
                    # request by replacing itself.
                    return PlaceHolderNodeHandler(self.client, self.node, name)
            # otherwise, we just return a no-such-child error
            return f

        node = node_or_failure
        if nonterminal and should_create_intermediate_directories(req):
            if not IDirectoryNode.providedBy(node):
                # we would have put a new directory here, but there was a
                # file in the way.
                raise WebError("Unable to create directory '%s': "
                               "a file was in the way" % name,
                               http.CONFLICT)
        return make_handler_for(node, self.client, self.node, name)
Beispiel #11
0
 def render_directory_verifycap(self, ctx, data):
     node = self.original
     if not IDirectoryNode.providedBy(node):
         return ""
     verifier = node.get_verify_cap()
     if verifier:
         return ctx.tag[node.get_verify_cap().to_string()]
     return ""
Beispiel #12
0
 def __init__(self, filenode, nodemaker, uploader):
     assert IFileNode.providedBy(filenode), filenode
     assert not IDirectoryNode.providedBy(filenode), filenode
     self._node = filenode
     filenode_cap = filenode.get_cap()
     self._uri = wrap_dirnode_cap(filenode_cap)
     self._nodemaker = nodemaker
     self._uploader = uploader
Beispiel #13
0
 def render_file_writecap(self, ctx, data):
     node = self.original
     if IDirectoryNode.providedBy(node):
         node = node._node
     write_uri = node.get_write_uri()
     if not write_uri:
         return ""
     return ctx.tag[write_uri]
Beispiel #14
0
 def render_is_mutable_file(self, ctx, data):
     node = self.original
     if IDirectoryNode.providedBy(node):
         return ""
     if (IFileNode.providedBy(node)
         and node.is_mutable() and not node.is_readonly()):
         return ctx.tag
     return ""
Beispiel #15
0
 def render_file_readcap(self, ctx, data):
     node = self.original
     if IDirectoryNode.providedBy(node):
         node = node._node
     read_uri = node.get_readonly_uri()
     if not read_uri:
         return ""
     return ctx.tag[read_uri]
Beispiel #16
0
 def render_raw_link(self, ctx, data):
     node = self.original
     if IDirectoryNode.providedBy(node):
         node = node._node
     elif IFileNode.providedBy(node):
         pass
     else:
         return ""
     root = self.get_root(ctx)
     quoted_uri = urllib.quote(node.get_uri())
     text_plain_url = "%s/file/%s/@@named=/raw.txt" % (root, quoted_uri)
     return T.li["Raw data as ", T.a(href=text_plain_url)["text/plain"]]
Beispiel #17
0
    def _got(children):
        kids = {}
        for name, (childnode, metadata) in children.iteritems():
            assert IFilesystemNode.providedBy(childnode), childnode
            rw_uri = childnode.get_write_uri()
            ro_uri = childnode.get_readonly_uri()
            if IFileNode.providedBy(childnode):
                kiddata = ("filenode", {'size': childnode.get_size(),
                                        'mutable': childnode.is_mutable(),
                                        })
                if childnode.is_mutable():
                    mutable_type = childnode.get_version()
                    assert mutable_type in (SDMF_VERSION, MDMF_VERSION)
                    if mutable_type == MDMF_VERSION:
                        file_format = "MDMF"
                    else:
                        file_format = "SDMF"
                else:
                    file_format = "CHK"
                kiddata[1]['format'] = file_format

            elif IDirectoryNode.providedBy(childnode):
                kiddata = ("dirnode", {'mutable': childnode.is_mutable()})
            else:
                kiddata = ("unknown", {})

            kiddata[1]["metadata"] = metadata
            if rw_uri:
                kiddata[1]["rw_uri"] = rw_uri
            if ro_uri:
                kiddata[1]["ro_uri"] = ro_uri
            verifycap = childnode.get_verify_cap()
            if verifycap:
                kiddata[1]['verify_uri'] = verifycap.to_string()

            kids[name] = kiddata

        drw_uri = dirnode.get_write_uri()
        dro_uri = dirnode.get_readonly_uri()
        contents = { 'children': kids }
        if dro_uri:
            contents['ro_uri'] = dro_uri
        if drw_uri:
            contents['rw_uri'] = drw_uri
        verifycap = dirnode.get_verify_cap()
        if verifycap:
            contents['verify_uri'] = verifycap.to_string()
        contents['mutable'] = dirnode.is_mutable()
        data = ("dirnode", contents)
        json = simplejson.dumps(data, indent=1) + "\n"
        return json
Beispiel #18
0
    def _POST_relink(self, req):
        charset = get_arg(req, "_charset", "utf-8")
        replace = parse_replace_arg(get_arg(req, "replace", "true"))

        from_name = get_arg(req, "from_name")
        if from_name is not None:
            from_name = from_name.strip()
            from_name = from_name.decode(charset)
            assert isinstance(from_name, unicode)
        else:
            raise WebError("from_name= is required")

        to_name = get_arg(req, "to_name")
        if to_name is not None:
            to_name = to_name.strip()
            to_name = to_name.decode(charset)
            assert isinstance(to_name, unicode)
        else:
            to_name = from_name

        # Disallow slashes in both from_name and to_name, that would only
        # cause confusion.
        if "/" in from_name:
            raise WebError("from_name= may not contain a slash",
                           http.BAD_REQUEST)
        if "/" in to_name:
            raise WebError("to_name= may not contain a slash",
                           http.BAD_REQUEST)

        to_dir = get_arg(req, "to_dir")
        if to_dir is not None and to_dir != self.node.get_write_uri():
            to_dir = to_dir.strip()
            to_dir = to_dir.decode(charset)
            assert isinstance(to_dir, unicode)
            to_path = to_dir.split(u"/")
            to_root = self.client.nodemaker.create_from_cap(to_str(to_path[0]))
            if not IDirectoryNode.providedBy(to_root):
                raise WebError("to_dir is not a directory", http.BAD_REQUEST)
            d = to_root.get_child_at_path(to_path[1:])
        else:
            d = defer.succeed(self.node)

        def _got_new_parent(new_parent):
            if not IDirectoryNode.providedBy(new_parent):
                raise WebError("to_dir is not a directory", http.BAD_REQUEST)

            return self.node.move_child_to(from_name, new_parent,
                                           to_name, replace)
        d.addCallback(_got_new_parent)
        d.addCallback(lambda res: "thing moved")
        return d
Beispiel #19
0
 def get_type(self):
     node = self.original
     if IDirectoryNode.providedBy(node):
         if not node.is_mutable():
             return "immutable directory"
         return "directory"
     if IFileNode.providedBy(node):
         si = node.get_storage_index()
         if si:
             if node.is_mutable():
                 return "mutable file"
             return "immutable file"
         return "immutable LIT file"
     return "unknown"
    def __init__(self, client, local_path_u, db, collective_dirnode,
                 upload_readonly_dircap, clock, is_upload_pending, umask):
        QueueMixin.__init__(self, client, local_path_u, db, 'downloader', clock, delay=self.scan_interval)

        if not IDirectoryNode.providedBy(collective_dirnode):
            raise AssertionError("The URI in '%s' does not refer to a directory."
                                 % os.path.join('private', 'collective_dircap'))
        if collective_dirnode.is_unknown() or not collective_dirnode.is_readonly():
            raise AssertionError("The URI in '%s' is not a readonly cap to a directory."
                                 % os.path.join('private', 'collective_dircap'))

        self._collective_dirnode = collective_dirnode
        self._upload_readonly_dircap = upload_readonly_dircap
        self._is_upload_pending = is_upload_pending
        self._umask = umask
Beispiel #21
0
 def _get_or_create_directories(self, node, path):
     if not IDirectoryNode.providedBy(node):
         # unfortunately it is too late to provide the name of the
         # blocking directory in the error message.
         raise ftp.FileExistsError("cannot create directory because there "
                                   "is a file in the way")
     if not path:
         return defer.succeed(node)
     d = node.get(path[0])
     def _maybe_create(f):
         f.trap(NoSuchChildError)
         return node.create_subdirectory(path[0])
     d.addErrback(_maybe_create)
     d.addCallback(self._get_or_create_directories, path[1:])
     return d
    def __init__(self, client, upload_dircap, local_dir_utf8, inotify=None):
        service.MultiService.__init__(self)

        try:
            local_dir_u = abspath_expanduser_unicode(local_dir_utf8.decode('utf-8'))
            if sys.platform == "win32":
                local_dir = local_dir_u
            else:
                local_dir = local_dir_u.encode(get_filesystem_encoding())
        except (UnicodeEncodeError, UnicodeDecodeError):
            raise AssertionError("The '[drop_upload] local.directory' parameter %s was not valid UTF-8 or "
                                 "could not be represented in the filesystem encoding."
                                 % quote_output(local_dir_utf8))

        self._client = client
        self._stats_provider = client.stats_provider
        self._convergence = client.convergence
        self._local_path = FilePath(local_dir)

        self.is_upload_ready = False

        if inotify is None:
            from twisted.internet import inotify
        self._inotify = inotify

        if not self._local_path.exists():
            raise AssertionError("The '[drop_upload] local.directory' parameter was %s but there is no directory at that location." % quote_output(local_dir_u))
        if not self._local_path.isdir():
            raise AssertionError("The '[drop_upload] local.directory' parameter was %s but the thing at that location is not a directory." % quote_output(local_dir_u))

        # TODO: allow a path rather than a cap URI.
        self._parent = self._client.create_node_from_uri(upload_dircap)
        if not IDirectoryNode.providedBy(self._parent):
            raise AssertionError("The URI in 'private/drop_upload_dircap' does not refer to a directory.")
        if self._parent.is_unknown() or self._parent.is_readonly():
            raise AssertionError("The URI in 'private/drop_upload_dircap' is not a writecap to a directory.")

        self._uploaded_callback = lambda ign: None

        self._notifier = inotify.INotify()

        # We don't watch for IN_CREATE, because that would cause us to read and upload a
        # possibly-incomplete file before the application has closed it. There should always
        # be an IN_CLOSE_WRITE after an IN_CREATE (I think).
        # TODO: what about IN_MOVE_SELF or IN_UNMOUNT?
        mask = inotify.IN_CLOSE_WRITE | inotify.IN_MOVED_TO | inotify.IN_ONLYDIR
        self._notifier.watch(self._local_path, mask=mask, callbacks=[self._notify])
Beispiel #23
0
    def modify(self, old_contents, servermap, first_time):
        children = self.node._unpack_contents(old_contents)
        if self.name not in children:
            if first_time and self.must_exist:
                raise NoSuchChildError(self.name)
            self.old_child = None
            return None
        self.old_child, metadata = children[self.name]

        # Unknown children can be removed regardless of must_be_directory or must_be_file.
        if self.must_be_directory and IFileNode.providedBy(self.old_child):
            raise ChildOfWrongTypeError("delete required a directory, not a file")
        if self.must_be_file and IDirectoryNode.providedBy(self.old_child):
            raise ChildOfWrongTypeError("delete required a file, not a directory")

        del children[self.name]
        new_contents = self.node._pack_contents(children)
        return new_contents
Beispiel #24
0
 def get_type(self):
     node = self.original
     if IDirectoryNode.providedBy(node):
         if not node.is_mutable():
             return "immutable directory"
         return "directory"
     if IFileNode.providedBy(node):
         si = node.get_storage_index()
         if si:
             if node.is_mutable():
                 ret = "mutable file"
                 if node.get_version() == MDMF_VERSION:
                     ret += " (mdmf)"
                 else:
                     ret += " (sdmf)"
                 return ret
             return "immutable file"
         return "immutable LIT file"
     return "unknown"
Beispiel #25
0
 def _deep_traverse_dirnode_children(self, children, parent, path,
                                     walker, monitor, found):
     monitor.raise_if_cancelled()
     d = defer.maybeDeferred(walker.enter_directory, parent, children)
     # we process file-like children first, so we can drop their FileNode
     # objects as quickly as possible. Tests suggest that a FileNode (held
     # in the client's nodecache) consumes about 2440 bytes. dirnodes (not
     # in the nodecache) seem to consume about 2000 bytes.
     dirkids = []
     filekids = []
     for name, (child, metadata) in sorted(children.iteritems()):
         childpath = path + [name]
         if isinstance(child, UnknownNode):
             walker.add_node(child, childpath)
             continue
         verifier = child.get_verify_cap()
         # allow LIT files (for which verifier==None) to be processed
         if (verifier is not None) and (verifier in found):
             continue
         found.add(verifier)
         if IDirectoryNode.providedBy(child):
             dirkids.append( (child, childpath) )
         else:
             filekids.append( (child, childpath) )
     for i, (child, childpath) in enumerate(filekids):
         d.addCallback(lambda ignored, child=child, childpath=childpath:
                       walker.add_node(child, childpath))
         # to work around the Deferred tail-recursion problem
         # (specifically the defer.succeed flavor) requires us to avoid
         # doing more than 158 LIT files in a row. We insert a turn break
         # once every 100 files (LIT or CHK) to preserve some stack space
         # for other code. This is a different expression of the same
         # Twisted problem as in #237.
         if i % 100 == 99:
             d.addCallback(lambda ignored: fireEventually())
     for (child, childpath) in dirkids:
         d.addCallback(lambda ignored, child=child, childpath=childpath:
                       self._deep_traverse_dirnode(child, childpath,
                                                   walker, monitor,
                                                   found))
     return d
        def _made_upload_dir(n):
            self.failUnless(IDirectoryNode.providedBy(n))
            upload_dircap = n.get_uri()
            readonly_dircap = n.get_readonly_uri()

            self.shouldFail(AssertionError, 'invalid local.directory', 'could not be represented',
                            DropUploader, client, upload_dircap, '\xFF', inotify=fake_inotify)
            self.shouldFail(AssertionError, 'nonexistent local.directory', 'there is no directory',
                            DropUploader, client, upload_dircap, os.path.join(self.basedir, "Laputa"), inotify=fake_inotify)

            fp = filepath.FilePath(self.basedir).child('NOT_A_DIR')
            fp.touch()
            self.shouldFail(AssertionError, 'non-directory local.directory', 'is not a directory',
                            DropUploader, client, upload_dircap, fp.path, inotify=fake_inotify)

            self.shouldFail(AssertionError, 'bad upload.dircap', 'does not refer to a directory',
                            DropUploader, client, 'bad', errors_dir, inotify=fake_inotify)
            self.shouldFail(AssertionError, 'non-directory upload.dircap', 'does not refer to a directory',
                            DropUploader, client, 'URI:LIT:foo', errors_dir, inotify=fake_inotify)
            self.shouldFail(AssertionError, 'readonly upload.dircap', 'is not a writecap to a directory',
                            DropUploader, client, readonly_dircap, errors_dir, inotify=fake_inotify)
Beispiel #27
0
 def add_node(self, node, childpath):
     if isinstance(node, UnknownNode):
         self.add("count-unknown")
     elif IDirectoryNode.providedBy(node):
         self.add("count-directories")
     elif IMutableFileNode.providedBy(node):
         self.add("count-files")
         self.add("count-mutable-files")
         # TODO: update the servermap, compute a size, add it to
         # size-mutable-files, max it into "largest-mutable-file"
     elif IImmutableFileNode.providedBy(node): # CHK and LIT
         self.add("count-files")
         size = node.get_size()
         self.histogram("size-files-histogram", size)
         theuri = from_string(node.get_uri())
         if isinstance(theuri, LiteralFileURI):
             self.add("count-literal-files")
             self.add("size-literal-files", size)
         else:
             self.add("count-immutable-files")
             self.add("size-immutable-files", size)
             self.max("largest-immutable-file", size)
Beispiel #28
0
    def _got(children):
        kids = {}
        for name, (childnode, metadata) in children.iteritems():
            assert IFilesystemNode.providedBy(childnode), childnode
            rw_uri = childnode.get_write_uri()
            ro_uri = childnode.get_readonly_uri()
            if IFileNode.providedBy(childnode):
                kiddata = ("filenode", get_filenode_metadata(childnode))
            elif IDirectoryNode.providedBy(childnode):
                kiddata = ("dirnode", {'mutable': childnode.is_mutable()})
            else:
                kiddata = ("unknown", {})

            kiddata[1]["metadata"] = metadata
            if rw_uri:
                kiddata[1]["rw_uri"] = rw_uri
            if ro_uri:
                kiddata[1]["ro_uri"] = ro_uri
            verifycap = childnode.get_verify_cap()
            if verifycap:
                kiddata[1]['verify_uri'] = verifycap.to_string()

            kids[name] = kiddata

        drw_uri = dirnode.get_write_uri()
        dro_uri = dirnode.get_readonly_uri()
        contents = { 'children': kids }
        if dro_uri:
            contents['ro_uri'] = dro_uri
        if drw_uri:
            contents['rw_uri'] = drw_uri
        verifycap = dirnode.get_verify_cap()
        if verifycap:
            contents['verify_uri'] = verifycap.to_string()
        contents['mutable'] = dirnode.is_mutable()
        data = ("dirnode", contents)
        json = simplejson.dumps(data, indent=1) + "\n"
        return json
Beispiel #29
0
 def _list((node, metadata)):
     if IDirectoryNode.providedBy(node):
         return node.list()
     return {path[-1]: (node, metadata)}  # need last-edge metadata
Beispiel #30
0
 def _render((node, metadata)):
     assert not IDirectoryNode.providedBy(node)
     return self._populate_row(keys, (node, metadata))
Beispiel #31
0
 def _got_child(child):
     if must_be_directory and not IDirectoryNode.providedBy(child):
         raise ftp.IsNotADirectoryError("rmdir called on a file")
     if must_be_file and IDirectoryNode.providedBy(child):
         raise ftp.IsADirectoryError("rmfile called on a directory")
     return parent.delete(childname)
Beispiel #32
0
    def got_child(self, node_or_failure, ctx, name):
        DEBUG = False
        if DEBUG: print "GOT_CHILD", name, node_or_failure
        req = IRequest(ctx)
        method = req.method
        nonterminal = len(req.postpath) > 1
        t = get_arg(req, "t", "").strip()
        if isinstance(node_or_failure, Failure):
            f = node_or_failure
            f.trap(NoSuchChildError)
            # No child by this name. What should we do about it?
            if DEBUG: print "no child", name
            if DEBUG: print "postpath", req.postpath
            if nonterminal:
                if DEBUG: print " intermediate"
                if should_create_intermediate_directories(req):
                    # create intermediate directories
                    if DEBUG: print " making intermediate directory"
                    d = self.node.create_subdirectory(name)
                    d.addCallback(make_handler_for, self.client, self.node,
                                  name)
                    return d
            else:
                if DEBUG: print " terminal"
                # terminal node
                if (method, t) in [("POST", "mkdir"), ("PUT", "mkdir"),
                                   ("POST", "mkdir-with-children"),
                                   ("POST", "mkdir-immutable")]:
                    if DEBUG: print " making final directory"
                    # final directory
                    kids = {}
                    if t in ("mkdir-with-children", "mkdir-immutable"):
                        req.content.seek(0)
                        kids_json = req.content.read()
                        kids = convert_children_json(self.client.nodemaker,
                                                     kids_json)
                    file_format = get_format(req, None)
                    mutable = True
                    mt = get_mutable_type(file_format)
                    if t == "mkdir-immutable":
                        mutable = False

                    d = self.node.create_subdirectory(name,
                                                      kids,
                                                      mutable=mutable,
                                                      mutable_version=mt)
                    d.addCallback(make_handler_for, self.client, self.node,
                                  name)
                    return d
                if (method, t) in (
                    ("PUT", ""),
                    ("PUT", "uri"),
                ):
                    if DEBUG: print " PUT, making leaf placeholder"
                    # we were trying to find the leaf filenode (to put a new
                    # file in its place), and it didn't exist. That's ok,
                    # since that's the leaf node that we're about to create.
                    # We make a dummy one, which will respond to the PUT
                    # request by replacing itself.
                    return PlaceHolderNodeHandler(self.client, self.node, name)
            if DEBUG: print " 404"
            # otherwise, we just return a no-such-child error
            return f

        node = node_or_failure
        if nonterminal and should_create_intermediate_directories(req):
            if not IDirectoryNode.providedBy(node):
                # we would have put a new directory here, but there was a
                # file in the way.
                if DEBUG: print "blocking"
                raise WebError(
                    "Unable to create directory '%s': "
                    "a file was in the way" % name, http.CONFLICT)
        if DEBUG: print "good child"
        return make_handler_for(node, self.client, self.node, name)
Beispiel #33
0
    def render_row(self, ctx, data):
        name, (target, metadata) = data
        name = name.encode("utf-8")
        assert not isinstance(name, unicode)
        nameurl = urllib.quote(name, safe="") # encode any slashes too

        root = get_root(ctx)
        here = "%s/uri/%s/" % (root, urllib.quote(self.node.get_uri()))
        if self.node.is_unknown() or self.node.is_readonly():
            unlink = "-"
            rename = "-"
        else:
            # this creates a button which will cause our _POST_unlink method
            # to be invoked, which unlinks the file and then redirects the
            # browser back to this directory
            unlink = T.form(action=here, method="post")[
                T.input(type='hidden', name='t', value='unlink'),
                T.input(type='hidden', name='name', value=name),
                T.input(type='hidden', name='when_done', value="."),
                T.input(type='submit', _class='btn', value='unlink', name="unlink"),
                ]

            rename = T.form(action=here, method="get")[
                T.input(type='hidden', name='t', value='rename-form'),
                T.input(type='hidden', name='name', value=name),
                T.input(type='hidden', name='when_done', value="."),
                T.input(type='submit', _class='btn', value='rename/relink', name="rename"),
                ]

        ctx.fillSlots("unlink", unlink)
        ctx.fillSlots("rename", rename)

        times = []
        linkcrtime = metadata.get('tahoe', {}).get("linkcrtime")
        if linkcrtime is not None:
            times.append("lcr: " + render_time(linkcrtime))
        else:
            # For backwards-compatibility with links last modified by Tahoe < 1.4.0:
            if "ctime" in metadata:
                ctime = render_time(metadata["ctime"])
                times.append("c: " + ctime)
        linkmotime = metadata.get('tahoe', {}).get("linkmotime")
        if linkmotime is not None:
            if times:
                times.append(T.br())
            times.append("lmo: " + render_time(linkmotime))
        else:
            # For backwards-compatibility with links last modified by Tahoe < 1.4.0:
            if "mtime" in metadata:
                mtime = render_time(metadata["mtime"])
                if times:
                    times.append(T.br())
                times.append("m: " + mtime)
        ctx.fillSlots("times", times)

        assert IFilesystemNode.providedBy(target), target
        target_uri = target.get_uri() or ""
        quoted_uri = urllib.quote(target_uri, safe="") # escape slashes too

        if IMutableFileNode.providedBy(target):
            # to prevent javascript in displayed .html files from stealing a
            # secret directory URI from the URL, send the browser to a URI-based
            # page that doesn't know about the directory at all
            dlurl = "%s/file/%s/@@named=/%s" % (root, quoted_uri, nameurl)

            ctx.fillSlots("filename", T.a(href=dlurl, rel="noreferrer")[name])
            ctx.fillSlots("type", "SSK")

            ctx.fillSlots("size", "?")

            info_link = "%s/uri/%s?t=info" % (root, quoted_uri)

        elif IImmutableFileNode.providedBy(target):
            dlurl = "%s/file/%s/@@named=/%s" % (root, quoted_uri, nameurl)

            ctx.fillSlots("filename", T.a(href=dlurl, rel="noreferrer")[name])
            ctx.fillSlots("type", "FILE")

            ctx.fillSlots("size", target.get_size())

            info_link = "%s/uri/%s?t=info" % (root, quoted_uri)

        elif IDirectoryNode.providedBy(target):
            # directory
            uri_link = "%s/uri/%s/" % (root, urllib.quote(target_uri))
            ctx.fillSlots("filename", T.a(href=uri_link)[name])
            if not target.is_mutable():
                dirtype = "DIR-IMM"
            elif target.is_readonly():
                dirtype = "DIR-RO"
            else:
                dirtype = "DIR"
            ctx.fillSlots("type", dirtype)
            ctx.fillSlots("size", "-")
            info_link = "%s/uri/%s/?t=info" % (root, quoted_uri)

        elif isinstance(target, ProhibitedNode):
            ctx.fillSlots("filename", T.strike[name])
            if IDirectoryNode.providedBy(target.wrapped_node):
                blacklisted_type = "DIR-BLACKLISTED"
            else:
                blacklisted_type = "BLACKLISTED"
            ctx.fillSlots("type", blacklisted_type)
            ctx.fillSlots("size", "-")
            info_link = None
            ctx.fillSlots("info", ["Access Prohibited:", T.br, target.reason])

        else:
            # unknown
            ctx.fillSlots("filename", name)
            if target.get_write_uri() is not None:
                unknowntype = "?"
            elif not self.node.is_mutable() or target.is_alleged_immutable():
                unknowntype = "?-IMM"
            else:
                unknowntype = "?-RO"
            ctx.fillSlots("type", unknowntype)
            ctx.fillSlots("size", "-")
            # use a directory-relative info link, so we can extract both the
            # writecap and the readcap
            info_link = "%s?t=info" % urllib.quote(name)

        if info_link:
            ctx.fillSlots("info", T.a(href=info_link)["More Info"])

        return ctx.tag
Beispiel #34
0
 def render_is_directory(self, ctx, data):
     node = self.original
     if IDirectoryNode.providedBy(node):
         return ctx.tag
     return ""
Beispiel #35
0
    def test_maker(self):
        basedir = "client/NodeMaker/maker"
        fileutil.make_dirs(basedir)
        fileutil.write(os.path.join(basedir, "tahoe.cfg"), BASECONFIG)
        c = yield client.create_client(basedir)

        n = c.create_node_from_uri(
            "URI:CHK:6nmrpsubgbe57udnexlkiwzmlu:bjt7j6hshrlmadjyr7otq3dc24end5meo5xcr5xe5r663po6itmq:3:10:7277"
        )
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failUnless(IFileNode.providedBy(n))
        self.failUnless(IImmutableFileNode.providedBy(n))
        self.failIf(IMutableFileNode.providedBy(n))
        self.failIf(IDirectoryNode.providedBy(n))
        self.failUnless(n.is_readonly())
        self.failIf(n.is_mutable())

        # Testing #1679. There was a bug that would occur when downloader was
        # downloading the same readcap more than once concurrently, so the
        # filenode object was cached, and there was a failure from one of the
        # servers in one of the download attempts. No subsequent download
        # attempt would attempt to use that server again, which would lead to
        # the file being undownloadable until the gateway was restarted. The
        # current fix for this (hopefully to be superceded by a better fix
        # eventually) is to prevent re-use of filenodes, so the NodeMaker is
        # hereby required *not* to cache and re-use filenodes for CHKs.
        other_n = c.create_node_from_uri(
            "URI:CHK:6nmrpsubgbe57udnexlkiwzmlu:bjt7j6hshrlmadjyr7otq3dc24end5meo5xcr5xe5r663po6itmq:3:10:7277"
        )
        self.failIf(n is other_n, (n, other_n))

        n = c.create_node_from_uri("URI:LIT:n5xgk")
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failUnless(IFileNode.providedBy(n))
        self.failUnless(IImmutableFileNode.providedBy(n))
        self.failIf(IMutableFileNode.providedBy(n))
        self.failIf(IDirectoryNode.providedBy(n))
        self.failUnless(n.is_readonly())
        self.failIf(n.is_mutable())

        n = c.create_node_from_uri(
            "URI:SSK:n6x24zd3seu725yluj75q5boaa:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq"
        )
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failUnless(IFileNode.providedBy(n))
        self.failIf(IImmutableFileNode.providedBy(n))
        self.failUnless(IMutableFileNode.providedBy(n))
        self.failIf(IDirectoryNode.providedBy(n))
        self.failIf(n.is_readonly())
        self.failUnless(n.is_mutable())

        n = c.create_node_from_uri(
            "URI:SSK-RO:b7sr5qsifnicca7cbk3rhrhbvq:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq"
        )
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failUnless(IFileNode.providedBy(n))
        self.failIf(IImmutableFileNode.providedBy(n))
        self.failUnless(IMutableFileNode.providedBy(n))
        self.failIf(IDirectoryNode.providedBy(n))
        self.failUnless(n.is_readonly())
        self.failUnless(n.is_mutable())

        n = c.create_node_from_uri(
            "URI:DIR2:n6x24zd3seu725yluj75q5boaa:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq"
        )
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failIf(IFileNode.providedBy(n))
        self.failIf(IImmutableFileNode.providedBy(n))
        self.failIf(IMutableFileNode.providedBy(n))
        self.failUnless(IDirectoryNode.providedBy(n))
        self.failIf(n.is_readonly())
        self.failUnless(n.is_mutable())

        n = c.create_node_from_uri(
            "URI:DIR2-RO:b7sr5qsifnicca7cbk3rhrhbvq:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq"
        )
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failIf(IFileNode.providedBy(n))
        self.failIf(IImmutableFileNode.providedBy(n))
        self.failIf(IMutableFileNode.providedBy(n))
        self.failUnless(IDirectoryNode.providedBy(n))
        self.failUnless(n.is_readonly())
        self.failUnless(n.is_mutable())

        unknown_rw = "lafs://from_the_future"
        unknown_ro = "lafs://readonly_from_the_future"
        n = c.create_node_from_uri(unknown_rw, unknown_ro)
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failIf(IFileNode.providedBy(n))
        self.failIf(IImmutableFileNode.providedBy(n))
        self.failIf(IMutableFileNode.providedBy(n))
        self.failIf(IDirectoryNode.providedBy(n))
        self.failUnless(n.is_unknown())
        self.failUnlessReallyEqual(n.get_uri(), unknown_rw)
        self.failUnlessReallyEqual(n.get_write_uri(), unknown_rw)
        self.failUnlessReallyEqual(n.get_readonly_uri(), "ro." + unknown_ro)

        # Note: it isn't that we *intend* to deploy non-ASCII caps in
        # the future, it is that we want to make sure older Tahoe-LAFS
        # versions wouldn't choke on them if we were to do so. See
        # #1051 and wiki:NewCapDesign for details.
        unknown_rw = u"lafs://from_the_future_rw_\u263A".encode('utf-8')
        unknown_ro = u"lafs://readonly_from_the_future_ro_\u263A".encode(
            'utf-8')
        n = c.create_node_from_uri(unknown_rw, unknown_ro)
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failIf(IFileNode.providedBy(n))
        self.failIf(IImmutableFileNode.providedBy(n))
        self.failIf(IMutableFileNode.providedBy(n))
        self.failIf(IDirectoryNode.providedBy(n))
        self.failUnless(n.is_unknown())
        self.failUnlessReallyEqual(n.get_uri(), unknown_rw)
        self.failUnlessReallyEqual(n.get_write_uri(), unknown_rw)
        self.failUnlessReallyEqual(n.get_readonly_uri(), "ro." + unknown_ro)
Beispiel #36
0
 def render_directory_readcap(self, ctx, data):
     node = self.original
     if not IDirectoryNode.providedBy(node):
         return ""
     return ctx.tag[node.get_readonly_uri()]
Beispiel #37
0
 def directory_readcap(self, req, tag):
     node = self.original
     if not IDirectoryNode.providedBy(node):
         return ""
     return tag(node.get_readonly_uri())
Beispiel #38
0
 def is_directory(self, req, tag):
     node = self.original
     if IDirectoryNode.providedBy(node):
         return tag
     return ""
Beispiel #39
0
    def test_maker(self):
        basedir = "client/NodeMaker/maker"
        fileutil.make_dirs(basedir)
        f = open(os.path.join(basedir, "tahoe.cfg"), "w")
        f.write(BASECONFIG)
        f.close()
        c = client.Client(basedir)

        n = c.create_node_from_uri("URI:CHK:6nmrpsubgbe57udnexlkiwzmlu:bjt7j6hshrlmadjyr7otq3dc24end5meo5xcr5xe5r663po6itmq:3:10:7277")
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failUnless(IFileNode.providedBy(n))
        self.failUnless(IImmutableFileNode.providedBy(n))
        self.failIf(IMutableFileNode.providedBy(n))
        self.failIf(IDirectoryNode.providedBy(n))
        self.failUnless(n.is_readonly())
        self.failIf(n.is_mutable())

        n = c.create_node_from_uri("URI:LIT:n5xgk")
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failUnless(IFileNode.providedBy(n))
        self.failUnless(IImmutableFileNode.providedBy(n))
        self.failIf(IMutableFileNode.providedBy(n))
        self.failIf(IDirectoryNode.providedBy(n))
        self.failUnless(n.is_readonly())
        self.failIf(n.is_mutable())

        n = c.create_node_from_uri("URI:SSK:n6x24zd3seu725yluj75q5boaa:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq")
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failUnless(IFileNode.providedBy(n))
        self.failIf(IImmutableFileNode.providedBy(n))
        self.failUnless(IMutableFileNode.providedBy(n))
        self.failIf(IDirectoryNode.providedBy(n))
        self.failIf(n.is_readonly())
        self.failUnless(n.is_mutable())

        n = c.create_node_from_uri("URI:SSK-RO:b7sr5qsifnicca7cbk3rhrhbvq:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq")
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failUnless(IFileNode.providedBy(n))
        self.failIf(IImmutableFileNode.providedBy(n))
        self.failUnless(IMutableFileNode.providedBy(n))
        self.failIf(IDirectoryNode.providedBy(n))
        self.failUnless(n.is_readonly())
        self.failUnless(n.is_mutable())

        n = c.create_node_from_uri("URI:DIR2:n6x24zd3seu725yluj75q5boaa:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq")
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failIf(IFileNode.providedBy(n))
        self.failIf(IImmutableFileNode.providedBy(n))
        self.failIf(IMutableFileNode.providedBy(n))
        self.failUnless(IDirectoryNode.providedBy(n))
        self.failIf(n.is_readonly())
        self.failUnless(n.is_mutable())

        n = c.create_node_from_uri("URI:DIR2-RO:b7sr5qsifnicca7cbk3rhrhbvq:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq")
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failIf(IFileNode.providedBy(n))
        self.failIf(IImmutableFileNode.providedBy(n))
        self.failIf(IMutableFileNode.providedBy(n))
        self.failUnless(IDirectoryNode.providedBy(n))
        self.failUnless(n.is_readonly())
        self.failUnless(n.is_mutable())

        unknown_rw = "lafs://from_the_future"
        unknown_ro = "lafs://readonly_from_the_future"
        n = c.create_node_from_uri(unknown_rw, unknown_ro)
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failIf(IFileNode.providedBy(n))
        self.failIf(IImmutableFileNode.providedBy(n))
        self.failIf(IMutableFileNode.providedBy(n))
        self.failIf(IDirectoryNode.providedBy(n))
        self.failUnless(n.is_unknown())
        self.failUnlessReallyEqual(n.get_uri(), unknown_rw)
        self.failUnlessReallyEqual(n.get_write_uri(), unknown_rw)
        self.failUnlessReallyEqual(n.get_readonly_uri(), "ro." + unknown_ro)

        # Note: it isn't that we *intend* to deploy non-ASCII caps in
        # the future, it is that we want to make sure older Tahoe-LAFS
        # versions wouldn't choke on them if we were to do so. See
        # #1051 and wiki:NewCapDesign for details.
        unknown_rw = u"lafs://from_the_future_rw_\u263A".encode('utf-8')
        unknown_ro = u"lafs://readonly_from_the_future_ro_\u263A".encode('utf-8')
        n = c.create_node_from_uri(unknown_rw, unknown_ro)
        self.failUnless(IFilesystemNode.providedBy(n))
        self.failIf(IFileNode.providedBy(n))
        self.failIf(IImmutableFileNode.providedBy(n))
        self.failIf(IMutableFileNode.providedBy(n))
        self.failIf(IDirectoryNode.providedBy(n))
        self.failUnless(n.is_unknown())
        self.failUnlessReallyEqual(n.get_uri(), unknown_rw)
        self.failUnlessReallyEqual(n.get_write_uri(), unknown_rw)
        self.failUnlessReallyEqual(n.get_readonly_uri(), "ro." + unknown_ro)
Beispiel #40
0
    def render_row(self, ctx, data):
        name, (target, metadata) = data
        name = name.encode("utf-8")
        assert not isinstance(name, unicode)
        nameurl = urllib.quote(name, safe="")  # encode any slashes too

        root = get_root(ctx)
        here = "%s/uri/%s/" % (root, urllib.quote(self.node.get_uri()))
        if self.node.is_unknown() or self.node.is_readonly():
            unlink = "-"
            rename = "-"
        else:
            # this creates a button which will cause our _POST_unlink method
            # to be invoked, which unlinks the file and then redirects the
            # browser back to this directory
            unlink = T.form(action=here, method="post")[
                T.input(type='hidden', name='t', value='unlink'),
                T.input(type='hidden', name='name', value=name),
                T.input(type='hidden', name='when_done', value="."),
                T.input(type='submit', value='unlink', name="unlink"), ]

            rename = T.form(action=here, method="get")[
                T.input(type='hidden', name='t', value='rename-form'),
                T.input(type='hidden', name='name', value=name),
                T.input(type='hidden', name='when_done', value="."),
                T.input(type='submit', value='rename', name="rename"), ]

        ctx.fillSlots("unlink", unlink)
        ctx.fillSlots("rename", rename)

        times = []
        linkcrtime = metadata.get('tahoe', {}).get("linkcrtime")
        if linkcrtime is not None:
            times.append("lcr: " + time_format.iso_local(linkcrtime))
        else:
            # For backwards-compatibility with links last modified by Tahoe < 1.4.0:
            if "ctime" in metadata:
                ctime = time_format.iso_local(metadata["ctime"])
                times.append("c: " + ctime)
        linkmotime = metadata.get('tahoe', {}).get("linkmotime")
        if linkmotime is not None:
            if times:
                times.append(T.br())
            times.append("lmo: " + time_format.iso_local(linkmotime))
        else:
            # For backwards-compatibility with links last modified by Tahoe < 1.4.0:
            if "mtime" in metadata:
                mtime = time_format.iso_local(metadata["mtime"])
                if times:
                    times.append(T.br())
                times.append("m: " + mtime)
        ctx.fillSlots("times", times)

        assert IFilesystemNode.providedBy(target), target
        target_uri = target.get_uri() or ""
        quoted_uri = urllib.quote(target_uri, safe="")  # escape slashes too

        if IMutableFileNode.providedBy(target):
            # to prevent javascript in displayed .html files from stealing a
            # secret directory URI from the URL, send the browser to a URI-based
            # page that doesn't know about the directory at all
            dlurl = "%s/file/%s/@@named=/%s" % (root, quoted_uri, nameurl)

            ctx.fillSlots("filename", T.a(href=dlurl)[html.escape(name)])
            ctx.fillSlots("type", "SSK")

            ctx.fillSlots("size", "?")

            info_link = "%s/uri/%s?t=info" % (root, quoted_uri)

        elif IImmutableFileNode.providedBy(target):
            dlurl = "%s/file/%s/@@named=/%s" % (root, quoted_uri, nameurl)

            ctx.fillSlots("filename", T.a(href=dlurl)[html.escape(name)])
            ctx.fillSlots("type", "FILE")

            ctx.fillSlots("size", target.get_size())

            info_link = "%s/uri/%s?t=info" % (root, quoted_uri)

        elif IDirectoryNode.providedBy(target):
            # directory
            uri_link = "%s/uri/%s/" % (root, urllib.quote(target_uri))
            ctx.fillSlots("filename", T.a(href=uri_link)[html.escape(name)])
            if not target.is_mutable():
                dirtype = "DIR-IMM"
            elif target.is_readonly():
                dirtype = "DIR-RO"
            else:
                dirtype = "DIR"
            ctx.fillSlots("type", dirtype)
            ctx.fillSlots("size", "-")
            info_link = "%s/uri/%s/?t=info" % (root, quoted_uri)

        elif isinstance(target, ProhibitedNode):
            ctx.fillSlots("filename", T.strike[name])
            if IDirectoryNode.providedBy(target.wrapped_node):
                blacklisted_type = "DIR-BLACKLISTED"
            else:
                blacklisted_type = "BLACKLISTED"
            ctx.fillSlots("type", blacklisted_type)
            ctx.fillSlots("size", "-")
            info_link = None
            ctx.fillSlots("info", ["Access Prohibited:", T.br, target.reason])

        else:
            # unknown
            ctx.fillSlots("filename", html.escape(name))
            if target.get_write_uri() is not None:
                unknowntype = "?"
            elif not self.node.is_mutable() or target.is_alleged_immutable():
                unknowntype = "?-IMM"
            else:
                unknowntype = "?-RO"
            ctx.fillSlots("type", unknowntype)
            ctx.fillSlots("size", "-")
            # use a directory-relative info link, so we can extract both the
            # writecap and the readcap
            info_link = "%s?t=info" % urllib.quote(name)

        if info_link:
            ctx.fillSlots("info", T.a(href=info_link)["More Info"])

        return ctx.tag