From: Brian Warner Date: Sat, 2 May 2015 00:37:07 +0000 (-0700) Subject: cp: trailing slash on source filename is an error, just like on targets X-Git-Tag: allmydata-tahoe-1.10.1a1~17^2~1 X-Git-Url: https://git.rkrishnan.org/pf/content/en/service/index.php?a=commitdiff_plain;h=275ddb93cc81862d9bb9d3a993e742cdfa069b98;p=tahoe-lafs%2Ftahoe-lafs.git cp: trailing slash on source filename is an error, just like on targets --- diff --git a/src/allmydata/scripts/tahoe_cp.py b/src/allmydata/scripts/tahoe_cp.py index a5cfe16a..7c302621 100644 --- a/src/allmydata/scripts/tahoe_cp.py +++ b/src/allmydata/scripts/tahoe_cp.py @@ -20,6 +20,10 @@ class MissingSourceError(TahoeError): def __init__(self, name, quotefn=quote_output): TahoeError.__init__(self, "No such file or directory %s" % quotefn(name)) +class FilenameWithTrailingSlashError(TahoeError): + def __init__(self, name, quotefn=quote_output): + TahoeError.__init__(self, "source '%s' is not a directory, but ends with a slash" % quotefn(name)) + def GET_to_file(url): resp = do_http("GET", url) @@ -501,7 +505,11 @@ class Copier: sources = [] # list of source objects for ss in source_specs: - si = self.get_source_info(ss) + try: + si = self.get_source_info(ss) + except FilenameWithTrailingSlashError: + self.to_stderr("source is not a directory, but ends with a slash") + return 1 precondition(isinstance(si, FileSources + DirectorySources), si) sources.append(si) @@ -622,6 +630,9 @@ class Copier: precondition(isinstance(source_spec, unicode), source_spec) rootcap, path_utf8 = get_alias(self.aliases, source_spec, None) path = path_utf8.decode("utf-8") + # any trailing slash is removed in abspath_expanduser_unicode(), so + # make a note of it here, to throw an error later + had_trailing_slash = path.endswith("/") if rootcap == DefaultAliasMarker: # no alias, so this is a local file pathname = abspath_expanduser_unicode(path) @@ -631,6 +642,8 @@ class Copier: if os.path.isdir(pathname): t = LocalDirectorySource(self.progress, pathname, name) else: + if had_trailing_slash: + raise FilenameWithTrailingSlashError(source_spec) assert os.path.isfile(pathname) t = LocalFileSource(pathname, name) # non-empty else: @@ -659,6 +672,8 @@ class Copier: self.progress, name) t.init_from_parsed(parsed) else: + if had_trailing_slash: + raise FilenameWithTrailingSlashError(source_spec) writecap = to_str(d.get("rw_uri")) readcap = to_str(d.get("ro_uri")) mutable = d.get("mutable", False) # older nodes don't provide it diff --git a/src/allmydata/test/test_cli_cp.py b/src/allmydata/test/test_cli_cp.py index 24087a4f..03abcc45 100644 --- a/src/allmydata/test/test_cli_cp.py +++ b/src/allmydata/test/test_cli_cp.py @@ -660,7 +660,7 @@ starting copy, 2 files, 1 directories # trailing slash on target *directory* should not matter, test both # trailing slash on target files should cause error # trailing slash on source directory should not matter, test a few -# ignore trailing slash on source file, that's easiest +# trailing slash on source files should cause error COPYOUT_TESTCASES = """ cp $FILECAP to/existing-file : to/existing-file @@ -686,12 +686,12 @@ cp $FILECAP to : E2-DESTNAME cp -r $FILECAP to : E2-DESTNAME cp $DIRCAP/file to : to/file cp -r $DIRCAP/file to : to/file -# these two should behave like the two above: ignore trailing slash -cp $DIRCAP/file/ to : to/file -cp -r $DIRCAP/file/ to : to/file +# these two are errors +cp $DIRCAP/file/ to : E8-BADSLASH +cp -r $DIRCAP/file/ to : E8-BADSLASH cp $PARENTCAP/dir to : E4-NEED-R cp -r $PARENTCAP/dir to : to/dir/file -# these two should ignore the trailing source slash too +# but these two should ignore the trailing source slash cp $PARENTCAP/dir/ to : E4-NEED-R cp -r $PARENTCAP/dir/ to : to/dir/file cp $DIRCAP to : E4-NEED-R @@ -949,6 +949,8 @@ class CopyOut(GridTestMixin, CLITestMixin, unittest.TestCase): return set(["E6-MANYONE"]) if err == "target is not a directory, but ends with a slash": return set(["E7-BADSLASH"]) + if err == "source is not a directory, but ends with a slash": + return set(["E8-BADSLASH"]) self.fail("unrecognized error ('%s') %s" % (case, res)) d.addCallback(_check) return d