When using mv --exchange and the destination path does not exist, mv
fails before issuing the renameat2 system call and prints
"Unknown error -1" instead of the expected "No such file or directory".
The exit status (1) is correct; only the error message is wrong.


*Reproduction:*
  $ touch a
  $ LC_ALL=C mv --exchange a b
  mv: cannot exchange 'a' and 'b': Unknown error -1

*Expected:*

  mv: cannot exchange 'a' and 'b': No such file or directory

Strace shows that the stat of the destination returns ENOENT, and no
renameat2 call is made:

  openat(AT_FDCWD, "b", O_RDONLY|O_PATH|O_DIRECTORY) = -1 ENOENT (No such
file or directory)
  newfstatat(AT_FDCWD, "a", {st_mode=S_IFREG|0600, st_size=0, ...},
AT_SYMLINK_NOFOLLOW) = 0
  newfstatat(AT_FDCWD, "b", 0x7fffdf0654e0, AT_SYMLINK_NOFOLLOW) = -1
ENOENT (No such file or directory)
  mv: cannot exchange 'a' and 'b': Unknown error -1

So the -1 is not returned by the kernel. Refusing the exchange is fine
since the destination doesn't exist; only the message is wrong. My
guess, from the strace only, is that the ENOENT is not passed to the
error message.

The same happens when the destination is an existing directory and -T
is not used: mv tries dest/source, that path does not exist, and the
same "Unknown error -1" appears.

When both files exist but are on different file systems, renameat2 is
called, returns EXDEV, and the message is correct ("Invalid cross-device
link").

*Reproduced on:*

  coreutils 9.7 (Debian 13, container)
  coreutils 9.11 (NixOS, kernel 6.18.56, glibc 2.42)

I checked the git log on master and found no related commit after 9.11.

Reply via email to