cp: error on target-filename collisions, rather than overwrite
authorBrian Warner <warner@lothar.com>
Wed, 29 Jul 2015 00:19:42 +0000 (17:19 -0700)
committerBrian Warner <warner@lothar.com>
Wed, 29 Jul 2015 00:39:26 +0000 (17:39 -0700)
Closes ticket:2447

src/allmydata/scripts/tahoe_cp.py
src/allmydata/test/test_cli_cp.py

index b603d6ba01193c67be16fb74da19ed9b5d61a162..351b6a331717db90bda955fed187998cd8f2a4e1 100644 (file)
@@ -496,8 +496,9 @@ class Copier:
 
     def try_copy(self):
         """
-        All usage errors are caught here, not in a subroutine. This bottoms
-        out in copy_file_to_file() or copy_things_to_directory().
+        All usage errors (except for target filename collisions) are caught
+        here, not in a subroutine. This bottoms out in copy_file_to_file() or
+        copy_things_to_directory().
         """
         source_specs = self.options.sources
         destination_spec = self.options.destination
@@ -735,6 +736,23 @@ class Copier:
         # sourceobject) dicts for all the files that need to wind up there.
         targetmap = self.build_targetmap(sources, target)
 
+        # target name collisions are an error
+        collisions = []
+        for target, sources in targetmap.items():
+            target_names = {}
+            for source in sources:
+                name = source.basename()
+                if name in target_names:
+                    collisions.append((target, source, target_names[name]))
+                else:
+                    target_names[name] = source
+        if collisions:
+            self.to_stderr("cannot copy multiple files with the same name into the same target directory")
+            # I'm not sure how to show where the collisions are coming from
+            #for (target, source1, source2) in collisions:
+            #    self.to_stderr(source1.basename())
+            return 1
+
         # step four: walk through the list of targets. For each one, copy all
         # the files. If the target is a TahoeDirectory, upload and create
         # read-caps, then do a set_children to the target directory.
index 82648307ddff861d57c44ab35e646e2032b94eb7..3c8bc80a931232ec3205554cf172cb7bd335d3a0 100644 (file)
@@ -787,7 +787,7 @@ cp -r $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 $FILECAP to/missing/ : E2-DE
 cp -r $PARENTCAP/dir4 to  : to/dir4/emptydir/
 cp -r $PARENTCAP/dir4 to/ : to/dir4/emptydir/
 
-# name collisions: ensure files are copied in order
+# name collisions should cause errors, not overwrites
 cp -r $PARENTCAP/dir6/dir $PARENTCAP/dir5/dir to : E9-COLLIDING-TARGETS
 cp -r $PARENTCAP/dir5/dir $PARENTCAP/dir6/dir to : E9-COLLIDING-TARGETS
 cp -r $DIRCAP6 $DIRCAP5 to : E9-COLLIDING-TARGETS
@@ -949,7 +949,7 @@ 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 == "cannot copy multiple files with the same name from different source directories into the same target directory":
+                if err == "cannot copy multiple files with the same name into the same target directory":
                     return set(["E9-COLLIDING-TARGETS"])
                 if (err.startswith("source ") and
                     "is not a directory, but ends with a slash" in err):