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.
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.
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
@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
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
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;
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[] =
{"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'},
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\
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;
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 \
--- /dev/null
+#!/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