]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
mv: new option --no-copy
authorPaul Eggert <eggert@cs.ucla.edu>
Fri, 27 Jan 2023 18:59:13 +0000 (10:59 -0800)
committerPaul Eggert <eggert@cs.ucla.edu>
Fri, 27 Jan 2023 19:03:38 +0000 (11:03 -0800)
Wishlist item from Mike Frysinger (Bug#61050).
* src/copy.c (copy_internal):
Do not fall back on copying if x->no_copy.
* src/copy.h (struct cp_options): New member no_copy.
* src/mv.c (NO_COPY_OPTION): New constant.
(long_options, usage, main): Support --no-copy.
* tests/mv/no-copy.sh: New test.
* tests/local.mk (all_tests): Add it.

NEWS
doc/coreutils.texi
src/copy.c
src/copy.h
src/mv.c
tests/local.mk
tests/mv/no-copy.sh [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index 9594179b48b08b263ce06de05af410b491b04bd6..d714b8f3b674d6a3626d913dcbd2a5c629cb0b98 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -95,6 +95,9 @@ GNU coreutils NEWS                                    -*- outline -*-
   ls now supports the --time=modification option, to explicitly
   select the default mtime timestamp for display and sorting.
 
+  mv now supports the --no-copy option, which causes it to fail when
+  asked to move a file to a different file system.
+
   wc now accepts the --total={auto,never,always,only} option
   to give explicit control over when the total is output.
 
index f5e531d65cf3a524ec87b7918c0b915ba43bf19c..c7494ba4771e4a6f5f26f8ac208854993ff8f227 100644 (file)
@@ -10038,19 +10038,16 @@ failing that if the last file is a directory and the
 directory, using the @var{source}s' names.
 @end itemize
 
-@command{mv} can move any type of file from one file system to another.
-Prior to version @code{4.0} of the fileutils,
-@command{mv} could move only regular files between file systems.
-For example, now @command{mv} can move an entire directory hierarchy
-including special device files from one partition to another.  It first
-uses some of the same code that's used by @code{cp -a} to copy the
-requested directories and files, then (assuming the copy succeeded)
-it removes the originals.  If the copy fails, then the part that was
-copied to the destination partition is removed.  If you were to copy
-three directories from one partition to another and the copy of the first
+To move a file, @command{mv} ordinarily simply renames it.
+However, if renaming does not work because the destination's file
+system differs, @command{mv} falls back on copying as if by @code{cp -a},
+then (assuming the copy succeeded) it removes the original.
+If the copy fails, then @command{mv} removes any partially created
+copy in the destination.  If you were to copy three directories from
+one file system to another and the copy of the first
 directory succeeded, but the second didn't, the first would be left on
-the destination partition and the second and third would be left on the
-original partition.
+the destination file system and the second and third would be left on the
+original file system.
 
 @cindex extended attributes, xattr
 @command{mv} always tries to copy extended attributes (xattr), which may
@@ -10114,6 +10111,12 @@ Do not overwrite an existing file; silently do nothing instead.
 @mvOptsIfn
 This option is mutually exclusive with @option{-b} or @option{--backup} option.
 
+@item --no-copy
+@opindex --no-copy
+@cindex renaming files without copying them
+If a file cannot be renamed because the destination file system differs,
+fail with a diagnostic instead of copying and then removing the file.
+
 @item -u
 @itemx --update
 @opindex -u
index 98f2ba45a2723576bd8c81f5eb2b5b6e801c15a1..99834434f125b7bce17ee92eb0fec5cec620f211 100644 (file)
@@ -2617,7 +2617,7 @@ copy_internal (char const *src_name, char const *dst_name,
          where you'd replace '18' with the integer in parentheses that
          was output from the perl one-liner above.
          If necessary, of course, change '/tmp' to some other directory.  */
-      if (rename_errno != EXDEV)
+      if (rename_errno != EXDEV || x->no_copy)
         {
           /* There are many ways this can happen due to a race condition.
              When something happens between the initial follow_fstatat and the
index d775db047dc37d3e2d2e8703609860d5b1692ee9..9d3884403ec4043bbe3093a4430d10bc2d01c6ff 100644 (file)
@@ -134,9 +134,9 @@ struct cp_options
      Create destination directories as usual. */
   bool hard_link;
 
-  /* If true, rather than copying, first attempt to use rename.
-     If that fails, then resort to copying.  */
-  bool move_mode;
+  /* If MOVE_MODE, first try to rename.
+     If that fails and NO_COPY, fail instead of copying.  */
+  bool move_mode, no_copy;
 
   /* If true, install(1) is the caller.  */
   bool install_mode;
index 72bbe8e46f001872d04e1b96a706612f0fd42913..f27a07a1c0217b2fe2e6bfc1dd0ac7155f0b7245 100644 (file)
--- a/src/mv.c
+++ b/src/mv.c
@@ -48,7 +48,8 @@
    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
 enum
 {
-  STRIP_TRAILING_SLASHES_OPTION = CHAR_MAX + 1
+  NO_COPY_OPTION = CHAR_MAX + 1,
+  STRIP_TRAILING_SLASHES_OPTION
 };
 
 static struct option const long_options[] =
@@ -58,6 +59,7 @@ static struct option const long_options[] =
   {"force", no_argument, NULL, 'f'},
   {"interactive", no_argument, NULL, 'i'},
   {"no-clobber", no_argument, NULL, 'n'},
+  {"no-copy", no_argument, NULL, NO_COPY_OPTION},
   {"no-target-directory", no_argument, NULL, 'T'},
   {"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION},
   {"suffix", required_argument, NULL, 'S'},
@@ -260,6 +262,7 @@ Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\
 If you specify more than one of -i, -f, -n, only the final one takes effect.\n\
 "), stdout);
       fputs (_("\
+      --no-copy                do not copy if renaming fails\n\
       --strip-trailing-slashes  remove any trailing slashes from each SOURCE\n\
                                  argument\n\
   -S, --suffix=SUFFIX          override the usual backup suffix\n\
@@ -330,6 +333,9 @@ main (int argc, char **argv)
         case 'n':
           x.interactive = I_ALWAYS_NO;
           break;
+        case NO_COPY_OPTION:
+          x.no_copy = true;
+          break;
         case STRIP_TRAILING_SLASHES_OPTION:
           remove_trailing_slashes = true;
           break;
index 86908ef4f8ed2c4ac8f754d735c391f326dfc7e4..f6e3746b682eb95f33ea9c5be0db7395f571bc02 100644 (file)
@@ -686,6 +686,7 @@ all_tests =                                 \
   tests/mv/leak-fd.sh                          \
   tests/mv/mv-n.sh                             \
   tests/mv/mv-special-1.sh                     \
+  tests/mv/no-copy.sh                          \
   tests/mv/no-target-dir.sh                    \
   tests/mv/part-fail.sh                                \
   tests/mv/part-hardlink.sh                    \
diff --git a/tests/mv/no-copy.sh b/tests/mv/no-copy.sh
new file mode 100755 (executable)
index 0000000..fba475c
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/sh
+# Test mv --no-copy.
+
+# Copyright (C) 2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ mv
+cleanup_() { rm -rf "$other_partition_tmpdir"; }
+. "$abs_srcdir/tests/other-fs-tmpdir"
+
+mkdir dir || framework_failure_
+> dir/a || framework_failure_
+> file || framework_failure_
+
+mv --no-copy dir "$other_partition_tmpdir" && fail=1
+mv --no-copy file "$other_partition_tmpdir" && fail=1
+mv dir "$other_partition_tmpdir" || fail=1
+mv file "$other_partition_tmpdir" || fail=1
+
+Exit $fail