Package: coreutils;
Reported by: Peter Dyballa <Peter_Dyballa <at> Web.DE>
Date: Tue, 20 May 2025 16:13:04 UTC
Severity: normal
Done: Paul Eggert <eggert <at> cs.ucla.edu>
View this message in rfc822 format
From: Peter Dyballa <Peter_Dyballa <at> Web.DE> To: Paul Eggert <eggert <at> cs.ucla.edu> Cc: 78509 <at> debbugs.gnu.org Subject: bug#78509: Coreutils' mv and cp 9.5 do not work properly on old PPC Mac OS X 10.4.11, Tiger Date: Fri, 23 May 2025 13:27:44 +0200
When using Gdb I skip in mv.c's main() function over the first few calls and come to the while loop, line #344. It is entered and 378 target_directory = optarg;. is – visited? The name "out" contains a "t" which is also listed in the list of command line options. After the while loop target_directory is still 0x0. It also "visits" line #386 because of "u". But this too does not seem to change anything… But then the if at line 431 if (no_target_directory) is reached. no_target_directory is false, so 443 else if (target_directory) is reached. target_directory is still 0x0, the "nullptr", so we come to line 450 else and then to an if on line 457 if (x.rename_errno != 0) x.rename_errno is 17 and therefore != 0, so the if expression is true and its block entered: 458 { 459 int fd = target_directory_operand (lastfile, &sb); 460 if (target_dirfd_valid (fd)) 461 { 462 x.rename_errno = -1; 463 target_dirfd = fd; 464 target_directory = lastfile; 465 n_files--; 466 } 467 else 468 { ... 484 } 485 } Here x.rename_errno is (re)set to -1 although already != 0, and most importantly target_directory receives value "out". So from now on mv might be sure that something needs to be moved into a *directory* "out", and we come to 519 if (target_directory) 520 { 521 /* Initialize the hash table only if we'll need it. 522 The problem it is used to detect can arise only if there are 523 two or more files to move. */ 524 if (2 <= n_files) 525 dest_info_init (&x); 526 527 ok = true; 528 for (int i = 0; i < n_files; ++i) 529 { 530 x.last_file = i + 1 == n_files; 531 char const *source = file[i]; 532 char const *source_basename = last_component (source); 533 char *dest_relname; 534 char *dest = file_name_concat (target_directory, source_basename, 535 &dest_relname); 536 strip_trailing_slashes (dest_relname); 537 ok &= do_move (source, dest, target_dirfd, dest_relname, &x); 538 free (dest); 539 } 540 } target_directory is "out" and therefore we enter the true block. n_files has been decremented on line #365, so dest_info_init() is not called and we enter the for loop to do our job… The cause for faulty behaviour is the value of x.rename_errno. It receives its value from 450 else 451 { 452 char const *lastfile = file[n_files - 1]; 453 if (n_files == 2 && !x.exchange) 454 x.rename_errno = (renameatu (AT_FDCWD, file[0], AT_FDCWD, lastfile, 455 RENAME_NOREPLACE) 456 ? errno : 0); 457 if (x.rename_errno != 0) On line # 453 n_files is 2, and x.exchange is false, so we have if( true && true) and renameatu() is called, with: renameatu (fd1=-3041965, src=0xbfffd607 "k", fd2=-3041965, dst=0xbfffd609 "out", flags=1) at lib/renameatu.c:257 254 #else /* !HAVE_RENAMEAT */ 255 256 /* RENAME_NOREPLACE is the only flag currently supported. */ 257 if (flags & ~RENAME_NOREPLACE) 258 return errno_fail (ENOTSUP); 259 return at_func2 (fd1, src, fd2, dst, flags ? rename_noreplace : rename); 260 261 #endif /* !HAVE_RENAMEAT */ Mac OS X 10.4, Tiger, does not have the renameat() system call, so not much is used from the GNUlib function. The if clause on line #257 is false, so renameatu() returns, calling at_func2(): at_func2 (fd1=-3041965, file1=0xbfffd607 "k", fd2=-3041965, file2=0xbfffd609 "out", func=0x25270 <rename_noreplace>) at lib/at-func2.c:77 77 if ((fd1 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file1)) 78 && (fd2 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file2))) 79 return func (file1, file2); /* Case 0-2, 4-6, 8-10. */ Here the if clause on lines #77/78 is true and func() is called: rename_noreplace (src=0xbfffd607 "k", dst=0xbfffd609 "out") at lib/renameatu.c:55 39 #if HAVE_RENAMEAT 40 41 # include <stdlib.h> 42 # include <string.h> 43 44 # include "dirname.h" 45 # include "openat.h" 46 47 #else 48 # include "openat-priv.h" 49 50 static int 51 rename_noreplace (char const *src, char const *dst) 52 { 53 /* This has a race between the call to lstat and the call to rename. */ 54 struct stat st; 55 return (lstat (dst, &st) == 0 || errno == EOVERFLOW ? errno_fail (EEXIST) 56 : errno == ENOENT ? rename (src, dst) 57 : -1); 58 } 59 #endif Isn't calling rename_noreplace() already faulty? Shouldn't it be rename_and_do_replace_and/or_remove()? The faulty behaviour starts here in mv.c: 454 x.rename_errno = (renameatu (AT_FDCWD, file[0], AT_FDCWD, lastfile, 455 RENAME_NOREPLACE) RENAME_NOREPLACE should not be passed to the function here. Anyway, lib/renameatu.c is divided into two parts, one for systems with renameat() family of library functions and another one without them. And this part might behave differently and take the last parameter as a target directory. I checked on my up-to-date Mac coreutils 9.7: mv -v k out just works! So I should start to debug here too to understand what produces the different behaviour – but first I need a working version of Gdb! -- Greetings Pete Clovis' Consideration of an Atmospheric Anomaly: The perversity of nature is nowhere better demonstrated than by the fact that, when exposed to the same atmosphere, bread becomes hard while crackers become soft
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.