Bug #10104


Fileutils cp_r fails on sockets or fifes even if File.mknod and File.mkfifo are defined

Added by wpaulson (Bill Paulson) over 8 years ago. Updated over 3 years ago.

Target version:
ruby -v:
ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-darwin12.0]


Briefly, our code uses cp_r to copy a directory structure whose contents seem to occasionally have a Unix domain socket. This raises an exception, which seems reasonable enough. We consulted the code to see if there was a workaround, and found that if the methods File.mknod and File.mkfifo where defined, it would avoid the exception, apparently by design. The problem being reported is that when File.mknod and File.mkfifo are defined, the copy method fails because it tries to call FileUtils::Entry_.mknod or FileUtils::Entry_.mkfifo, which do not exist. It appears that the "unless File.respond_to?(:mknod)" clause is irrelevant to the intended behavior.
In fileutils.rb, the copy method has checks that raises an exception when it tries to copy a Unix domain socket, fifo, or other special file. It will not raise if the File class responds to :mknod or :mkfifo.
In class Entry_, method copy() -

      when chardev?
        raise "cannot handle device file" unless File.respond_to?(:mknod)
        mknod dest, ?c, 0666, lstat().rdev
      when blockdev?
        raise "cannot handle device file" unless File.respond_to?(:mknod)
        mknod dest, ?b, 0666, lstat().rdev
      when socket?
        raise "cannot handle socket" unless File.respond_to?(:mknod)
        mknod dest, nil, lstat().mode, 0
      when pipe?
        raise "cannot handle FIFO" unless File.respond_to?(:mkfifo)
        mkfifo dest, 0666

Some previous suggested that that if desired that creating File.mkfifo and File.mknod would allow the copy to continue. If the code called File.mkfifo this would be true, but since it calls FileUtils::Entry_.mkfifo, working around this exception needs more than just creating File methods.

The following (ugly, monkey patching) workaround allows copy to work correctly, while skipping special files:

    File.singleton_class.module_eval("def mknod; end")
    File.singleton_class.module_eval("def mkfifo; end")
    FileUtils::Entry_.module_eval("def mknod(*args); end")
    FileUtils::Entry_.module_eval("def mkfifo(*args); end")

    FileUtils.cp_r(old, new)

     File.singleton_class.module_eval("undef_method :mknod")
     File.singleton_class.module_eval("undef_method :mkfifo")

It appears that the intent of the existing code is to allow Unix/Linux programs to work (if necessary by creating File.mknod and File.mkfifo routines), while acknowledging that mkfifo and mknod aren't ubiquitous. In that case, the lines following each exception should call File.mknod or File.mkfifo, rather than*.

Actions #1

Updated by hsbt (Hiroshi SHIBATA) about 5 years ago

  • Description updated (diff)

Updated by jeremyevans0 (Jeremy Evans) over 3 years ago

I agree this is a bug. This has been broken since the code was originally added in 18c0a86cc680929bbd8c48f4f45a197e3e68c70a. I added a pull request upstream to fix it:

Updated by ko1 (Koichi Sasada) over 3 years ago

  • Status changed from Open to Rejected

continue on github issue?


Also available in: Atom PDF