On 2023-01-24 17:20, Mike Frysinger wrote:
i'd like to require that the mv be
atomic when relocating a directory, and if it isn't, fallback to other
logic
Calling the new option "--one-file-system" sounds a bit wrong, as
--one-file-system is for programs like 'du' and 'cp' and causes them to
not cross a file system boundary when recursing - something that 'mv'
could also do (when it falls back on copying), but which is a different
thing than what you're asking for.
How about if we call the new option --no-copy instead? Something like
the attached incomplete patch, say. (It needs more documentation and
ideally a test case.)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..15dcfa8af 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 attempt to use rename.
+ If that fails and !NO_COPY, resort to 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;