On 2023-01-25 05:34, Mike Frysinger wrote:
should there be a `--copy` flag to be able to undo `--no-copy` ?
We can add one if there's a need, but mv doesn't have a --clobber option
to match its --no-clobber and I'm hoping --no-copy is similar.
personally i always argue against using negative variable names
Yes, I considered calling it "rename_only" instead of "no_copy", but the
pull of the --no-copy option's name was too strong. Besides,
"rename_only" wasn't quite right either as "mv --no-copy" does more than
just renaming (e.g., if the destination is not writeable).
If we can come up with a better name now's a good time to switch. In the
meantime I installed the attached coreutils patch.From 20005ca733e111a61212ee8a6890a54583c55562 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Fri, 27 Jan 2023 10:59:13 -0800
Subject: [PATCH] mv: new option --no-copy
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 | 3 +++
doc/coreutils.texi | 27 +++++++++++++++------------
src/copy.c | 2 +-
src/copy.h | 6 +++---
src/mv.c | 8 +++++++-
tests/local.mk | 1 +
tests/mv/no-copy.sh | 33 +++++++++++++++++++++++++++++++++
7 files changed, 63 insertions(+), 17 deletions(-)
create mode 100755 tests/mv/no-copy.sh
diff --git a/NEWS b/NEWS
index 9594179b4..d714b8f3b 100644
--- 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.
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index f5e531d65..c7494ba47 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -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
diff --git a/src/copy.c b/src/copy.c
index 98f2ba45a..99834434f 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -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
diff --git a/src/copy.h b/src/copy.h
index d775db047..9d3884403 100644
--- a/src/copy.h
+++ b/src/copy.h
@@ -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;
diff --git a/src/mv.c b/src/mv.c
index 72bbe8e46..f27a07a1c 100644
--- 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;
diff --git a/tests/local.mk b/tests/local.mk
index 86908ef4f..f6e3746b6 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -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
index 000000000..fba475c03
--- /dev/null
+++ b/tests/mv/no-copy.sh
@@ -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
--
2.37.2