diff options
author | Ken Booth <ken@booths.org.uk> | 2013-07-02 01:06:32 +0100 |
---|---|---|
committer | Pádraig Brady <P@draigBrady.com> | 2013-07-25 16:45:02 +0100 |
commit | 2bdb74ec1a453f6c6084d042e573de436ec205f3 (patch) | |
tree | 08f8c9fdc929321f414b8e1355643e13a78271ea | |
parent | 68e5e8886170a3c2af88f830f2ac36100a9a610b (diff) | |
download | coreutils-2bdb74ec1a453f6c6084d042e573de436ec205f3.tar.xz |
mv: replace empty directories in cross file system move
src/copy.c (copy_internal): Use rmdir() rather than unlink()
when the source is a directory, so that empty directories
are replaced in the destination as per POSIX.
* tests/mv/part-rename.sh: Augment with various combinations.
* NEWS: Mention the bug fix.
Fixes http://bugs.gnu.org/14763
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | src/copy.c | 8 | ||||
-rwxr-xr-x | tests/mv/part-rename.sh | 34 |
3 files changed, 40 insertions, 6 deletions
@@ -22,6 +22,10 @@ GNU coreutils NEWS -*- outline -*- system such as GNU/Linux where directory ACL umasks override process umasks. [bug introduced in coreutils-6.0] + mv will now replace empty directories in the destination with directories + from the source, when copying across file systems. + [This bug was present in "the beginning".] + od -wN with N larger than 64K on a system with 32-bit size_t would print approximately 2*N bytes of extraneous padding. [Bug introduced in coreutils-7.0] diff --git a/src/copy.c b/src/copy.c index c1c82730d..f0cebb1b1 100644 --- a/src/copy.c +++ b/src/copy.c @@ -2172,8 +2172,12 @@ copy_internal (char const *src_name, char const *dst_name, /* The rename attempt has failed. Remove any existing destination file so that a cross-device 'mv' acts as if it were really using - the rename syscall. */ - if (unlink (dst_name) != 0 && errno != ENOENT) + the rename syscall. Note both src and dst must both be directories + or not, and this is enforced above. Therefore we check the src_mode + and operate on dst_name here as a tighter constraint and also because + src_mode is readily available here. */ + if ((S_ISDIR (src_mode) ? rmdir (dst_name) : unlink (dst_name)) != 0 + && errno != ENOENT) { error (0, errno, _("inter-device move failed: %s to %s; unable to remove target"), diff --git a/tests/mv/part-rename.sh b/tests/mv/part-rename.sh index aa2265b1b..b45fc3ac6 100755 --- a/tests/mv/part-rename.sh +++ b/tests/mv/part-rename.sh @@ -1,7 +1,5 @@ #!/bin/sh -# Moving a directory specified with a trailing slash from one partition to -# another, and giving it a different name at the destination would cause mv -# to get a failed assertion. +# Test various cases for moving directories across file systems # Copyright (C) 2000-2013 Free Software Foundation, Inc. @@ -23,9 +21,37 @@ print_ver_ mv cleanup_() { rm -rf "$other_partition_tmpdir"; } . "$abs_srcdir/tests/other-fs-tmpdir" + +# Moving a directory specified with a trailing slash from one partition to +# another, and giving it a different name at the destination would cause mv +# to get a failed assertion. mkdir foo || framework_failure_ +mv foo/ "$other_partition_tmpdir/bar" || fail=1 -mv foo/ "$other_partition_tmpdir/bar" || fail=1 +# Moving a non directory from source shouldn't replace empty dir in dest +touch bar || framework_failure_ +mv bar "$other_partition_tmpdir/" && fail=1 + + +# Moving a directory from source shouldn't replace non directory in dest +mkdir bar2 +touch "$other_partition_tmpdir/bar2" +mv bar2 "$other_partition_tmpdir/" && fail=1 + + +# As per POSIX moving directory from source should replace empty dir in dest +mkdir bar3 +touch bar3/file +mkdir "$other_partition_tmpdir/bar3" +mv bar3 "$other_partition_tmpdir/" || fail=1 +test -e "$other_partition_tmpdir/bar3/file" || fail=1 + + +# As per POSIX moving directory from source shouldn't update dir in dest +mkdir bar3 +touch bar3/file2 +mv bar3 "$other_partition_tmpdir/" && fail=1 +test -e "$other_partition_tmpdir/bar3/file2" && fail=1 Exit $fail |