Subject: Re: bug#78509: Coreutils' mv and cp 9.5 do not work properly on old PPC Mac OS X 10.4.11, Tiger

From: Peter Dyballa <[email-masked]>
Date: Sat, 24 May 2025 20:45:20 +0200

> Am 24.05.2025 um 10:36 schrieb Paul Eggert <[email-masked]>:
> 
> On 2025-05-24 00:51, Peter Dyballa wrote:
>> You are saying that the GNUlib function target_directory_operand() is known to fail when the target of cp or mv is a file? So I do not need to debug further?
> 
> No, I'm saying that target_directory_operand is supposed to fail if it's given the name of a regular file. (It should succeed only on directories.) You're reporting that it succeeded, which means there's something wrong here.

This function in lib/targetdir.c, target_directory_operand(), seems to work OK:

    63   if (must_be_working_directory (file))

evaluates to false

    72   if (!O_DIRECTORY)

evaluates to false – and we come to:

    88   if (try_to_open)
    89     {
    90       fd = open (file, O_PATHSEARCH | O_DIRECTORY);
    91 
    92       /* On platforms lacking O_PATH, using O_SEARCH | O_DIRECTORY to
    93          open an overly-protected non-directory can fail with either
    94          EACCES or ENOTDIR.  Prefer ENOTDIR as it makes for better
    95          diagnostics.  */
    96       if (O_PATHSEARCH == O_SEARCH && fd < 0 && errno == EACCES)
    97         errno = (((O_DIRECTORY ? stat (file, st) : stat_result) == 0
    98                   && !S_ISDIR (st->st_mode))
    99                  ? ENOTDIR : EACCES);
   100     }

open() on line #90 returns 7, so the if on line #96 evaluates to false and we're on line

   117   return fd - (AT_FDCWD == -1 && fd < 0);

AT_FDCWD is replaced by a large negative value (found by pre-processing the source) and fd is larger than 0: how can an integer minus a logical expression, that evaluates to FALSE, be evaluated and returned? (Because TRUE and FALSE are some integer values…)

Anyway, back in mv.c fd is still 7. And we come to function target_dirfd_valid() in lib/targetdir.h (no openat in Tiger):

    29 /* Return a file descriptor open to FILE, for use in openat.
    30    As an optimization, return AT_FDCWD if FILE must be the working directory.
    31    As a side effect, possibly set *ST to the file's status.
    32    Fail and set errno if FILE is not a directory.
    33    On failure return -2 if AT_FDCWD is -1, -1 otherwise.  */
    34 extern int target_directory_operand (char const *file, struct stat *st);
    35 
    36 /* Return true if FD represents success for target_directory_operand.  */
    37 TARGETDIR_INLINE _GL_ATTRIBUTE_PURE bool
    38 target_dirfd_valid (int fd)
    39 {
    40   return fd != -1 - (AT_FDCWD == -1);
    41 }
 
In pre-processed output of src/mv.c is this line:

 14590   return fd != -1 - ((-3041965) == -1);

This is TRUE minus FALSE, isn't it? It must be true – because Gdb executes this block in src/mv.c:

   461             {
   462               x.rename_errno = -1;
   463               target_dirfd = fd;
   464               target_directory = lastfile;
   465               n_files--;
   466             }

And the target file has turned into a target directory…

Is the old way of checking with S_IFDIR and stat() or fstat() too out-fashioned? Why is the value of O_DIRECTORY used for complicated different things when it can be used directly? Besides, the decisive function, target_dirfd_valid(), seems to check only if the file descriptor is valid. I cannot see that it makes a distinction whether fd is that of a(n ordinary) file or a directory.

--

Greetings

  Pete

No one is patriotic about taxes.
				– George Orwell, Orwell Diaries 1938-1942, (1940-08-09)