mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2024-12-19 13:07:56 +00:00
This forbids operations that would implicitly create a directory with a zero-length (empty string) name, like what you'd get if you did "tahoe put local /oops/blah" (#358) or "POST /uri/CAP//?t=mkdir" (#676). The error message is fairly friendly too. Also added code to "tahoe put" to catch this error beforehand and suggest the correct syntax (i.e. without the leading slash).
This commit is contained in:
parent
2e0a61a953
commit
499add09e6
@ -2374,3 +2374,6 @@ class InsufficientVersionError(Exception):
|
||||
def __repr__(self):
|
||||
return "InsufficientVersionError(need '%s', got %s)" % (self.needed,
|
||||
self.got)
|
||||
|
||||
class EmptyPathnameComponentError(Exception):
|
||||
"""The webapi disallows empty pathname components."""
|
||||
|
@ -31,8 +31,10 @@ def put(options):
|
||||
# <none> : unlinked upload
|
||||
# foo : TAHOE_ALIAS/foo
|
||||
# subdir/foo : TAHOE_ALIAS/subdir/foo
|
||||
# /oops/subdir/foo : DISALLOWED
|
||||
# ALIAS:foo : aliases[ALIAS]/foo
|
||||
# ALIAS:subdir/foo : aliases[ALIAS]/subdir/foo
|
||||
# ALIAS:/oops/subdir/foo : DISALLOWED
|
||||
# DIRCAP:./foo : DIRCAP/foo
|
||||
# DIRCAP:./subdir/foo : DIRCAP/subdir/foo
|
||||
# MUTABLE-FILE-WRITECAP : filecap
|
||||
@ -41,6 +43,11 @@ def put(options):
|
||||
url = nodeurl + "uri/%s" % urllib.quote(to_file)
|
||||
else:
|
||||
rootcap, path = get_alias(aliases, to_file, DEFAULT_ALIAS)
|
||||
if path.startswith("/"):
|
||||
suggestion = to_file.replace("/", "", 1)
|
||||
print >>stderr, "ERROR: The VDRIVE filename must not start with a slash"
|
||||
print >>stderr, "Please try again, perhaps with:", suggestion
|
||||
return 1
|
||||
url = nodeurl + "uri/%s/" % urllib.quote(rootcap)
|
||||
if path:
|
||||
url += escape_path(path)
|
||||
|
@ -836,6 +836,14 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, unittest.TestCase):
|
||||
"Unable to create directory 'blockingfile': a file was in the way")
|
||||
return d
|
||||
|
||||
def test_PUT_NEWFILEURL_emptyname(self):
|
||||
# an empty pathname component (i.e. a double-slash) is disallowed
|
||||
d = self.shouldFail2(error.Error, "test_PUT_NEWFILEURL_emptyname",
|
||||
"400 Bad Request",
|
||||
"The webapi does not allow empty pathname components",
|
||||
self.PUT, self.public_url + "/foo//new.txt", "")
|
||||
return d
|
||||
|
||||
def test_DELETE_FILEURL(self):
|
||||
d = self.DELETE(self.public_url + "/foo/bar.txt")
|
||||
d.addCallback(lambda res:
|
||||
@ -1128,6 +1136,22 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, unittest.TestCase):
|
||||
d.addCallback(self.failUnlessNodeKeysAre, [])
|
||||
return d
|
||||
|
||||
def test_POST_NEWDIRURL(self):
|
||||
d = self.POST2(self.public_url + "/foo/newdir?t=mkdir", "")
|
||||
d.addCallback(lambda res:
|
||||
self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
|
||||
d.addCallback(lambda res: self._foo_node.get(u"newdir"))
|
||||
d.addCallback(self.failUnlessNodeKeysAre, [])
|
||||
return d
|
||||
|
||||
def test_POST_NEWDIRURL_emptyname(self):
|
||||
# an empty pathname component (i.e. a double-slash) is disallowed
|
||||
d = self.shouldFail2(error.Error, "test_POST_NEWDIRURL_emptyname",
|
||||
"400 Bad Request",
|
||||
"The webapi does not allow empty pathname components, i.e. a double slash",
|
||||
self.POST, self.public_url + "//?t=mkdir")
|
||||
return d
|
||||
|
||||
def test_POST_NEWDIRURL_initial_children(self):
|
||||
(newkids, filecap1, filecap2, filecap3,
|
||||
dircap) = self._create_initial_children()
|
||||
|
@ -8,7 +8,7 @@ from nevow.inevow import IRequest
|
||||
from nevow.util import resource_filename
|
||||
from allmydata.interfaces import ExistingChildError, NoSuchChildError, \
|
||||
FileTooLargeError, NotEnoughSharesError, NoSharesError, \
|
||||
NotDeepImmutableError
|
||||
NotDeepImmutableError, EmptyPathnameComponentError
|
||||
from allmydata.mutable.common import UnrecoverableFileError
|
||||
from allmydata.util import abbreviate # TODO: consolidate
|
||||
|
||||
@ -147,6 +147,9 @@ def should_create_intermediate_directories(req):
|
||||
|
||||
def humanize_failure(f):
|
||||
# return text, responsecode
|
||||
if f.check(EmptyPathnameComponentError):
|
||||
return ("The webapi does not allow empty pathname components, "
|
||||
"i.e. a double slash", http.BAD_REQUEST)
|
||||
if f.check(ExistingChildError):
|
||||
return ("There was already a child by that name, and you asked me "
|
||||
"to not replace it.", http.CONFLICT)
|
||||
|
@ -15,7 +15,8 @@ from foolscap.api import fireEventually
|
||||
from allmydata.util import base32, time_format
|
||||
from allmydata.uri import from_string_dirnode
|
||||
from allmydata.interfaces import IDirectoryNode, IFileNode, IFilesystemNode, \
|
||||
IImmutableFileNode, IMutableFileNode, ExistingChildError, NoSuchChildError
|
||||
IImmutableFileNode, IMutableFileNode, ExistingChildError, \
|
||||
NoSuchChildError, EmptyPathnameComponentError
|
||||
from allmydata.monitor import Monitor, OperationCancelledError
|
||||
from allmydata import dirnode
|
||||
from allmydata.web.common import text_plain, WebError, \
|
||||
@ -61,6 +62,8 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
|
||||
def childFactory(self, ctx, name):
|
||||
req = IRequest(ctx)
|
||||
name = name.decode("utf-8")
|
||||
if not name:
|
||||
raise EmptyPathnameComponentError()
|
||||
d = self.node.get(name)
|
||||
d.addBoth(self.got_child, ctx, name)
|
||||
# got_child returns a handler resource: FileNodeHandler or
|
||||
|
Loading…
Reference in New Issue
Block a user