[coreutils] cp -fR fifo existing_file

Version:

coreutils-6.10 (fixed in 6.11)

How to reproduce?

mkfifo ./my_fifo

touch ./my_fifo_dest

coreutils-6.10/src/cp -fR ./my_fifo ./my_fifo_dest

cp: cannot create fifo `./my_fifo_dest': File exists

Symptom:

Early termination by rejecting valid input. ‘cp’ failed to replace the destination fifo file when there exists a destination file with the same name. It also printed the above error message before quit!

Root cause:

The destination fifo file is supposed to be created by system call ‘mkfifo’, however, ‘mkfifo’ requires there is no existing file with the same name. ‘cp’ is supposed to remove (unlink) the existing destination file ‘my_fifo_dest’ before calling ‘mkfifo’, however it failed to do so.

Comments written by us are in blue color.

....

-> copy_internal (src_name = “./my_fifo”, dst_name = “./my_fifo_dest” ...)

----------------------------------------------- copy.c ----------------------------------------------------------------

bool copy_internal (char const *src_name, char const *dst_name,  

         bool new_dst ... )  {

    ... ...

    if (x->backup_type != no_backups

           && ! dot_or_dotdot (last_component (src_name))

              && (x->move_mode || ! S_ISDIR (dst_sb.st_mode))) {

                  ...

    }

   /* The code below checks if we need to first remove the destination

    * file. The original logic was to only to remove the destination

    * if the source file is a link type.

    * In this case, the source type is not a link, but a fifo,

    * so the original logic will not remove the destination below.

    *

    * The fix is that as long as source is not a regular file,

    * remove the destination! */

    else if (! S_ISDIR (dst_sb.st_mode)

         && (x->unlink_dest_before_opening

            || (x->preserve_links && 1 < dst_sb.st_nlink)

         || (!x->move_mode && x->dereference == DEREF_NEVER  

-                && S_ISLNK (src_sb.st_mode))))

+                && !S_ISREG (src_sb.st_mode))))

            {                

           /* remove the destination file using unlink system call. */          

              if (unlink (dst_name) != 0 && errno != ENOENT)

                {

                  error (0, errno, _("cannot remove %s"), quote (dst_name));

                  return false;

                }

                   … ...

    } // else if (! S_ISDIR) …

    /* Here we are about to create the destination file (which is a fifo). */

    else if (S_ISFIFO (src_mode))  {

      if (mknod (dst_name, src_mode & ~omitted_permissions, 0) != 0)

      /* When the source file is fifo, it uses ‘mkfifo’ system call to

       * create the destination fifo. However, mkfifo failed to

       * create the destination because there is an existing file with the

       * same name. */

        if (mkfifo (dst_name, src_mode & ~S_IFIFO & ~omitted_permissions) != 0)

          {

            error (0, errno, _("cannot create fifo %s"), quote (dst_name));

            goto un_backup;

          }

   }

}

----------------------------------------------- copy.c ----------------------------------------------------------------

Is there Error Message?

Yes.

Can Errlog anticipate the error message?

Yes. By “system call error return” pattern.

The error message was printed because both the ‘mknode’ and ‘mkfifo’ system calls failed.

The existing error message is very helpful. If we further use LogEnhancer to record additional causally-related information, it would be sufficient to diagnose the failure.