Bug #3556

FileUtils.mkdir_p fails trying to create C: under Windows

Added by Luis Lavena almost 5 years ago. Updated almost 4 years ago.

[ruby-core:31177]
Status:Closed
Priority:Normal
Assignee:-
ruby -v:1.8 and 1.9, including trunk Backport:

Description

=begin
Hello,

I've been experiencing weird problems with FileUtils.mkdir_p in RubyGems, as tried to document in

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/365540

The root of this investigation is a report about gem installation under Windows:

http://groups.google.com/group/rubyinstaller/browse_thread/thread/df7b7c217ad7d882

We have been trying to reproduce this without avail.

Now, I was able to recreate a scenario:

ruby -v -rfileutils -e "system('rd C:\Foo /s/q'); puts FileUtils.mkdir_p('C:/Foo/Bar')"

C:/Users/Luis/Tools/Ruby/ruby-1.8.6-p398-i386-mingw32/lib/ruby/1.8/fileutils.rb:243:in mkdir': File exists - C: (Errno::EEXIST)
from C:/Users/Luis/Tools/Ruby/ruby-1.8.6-p398-i386-mingw32/lib/ruby/1.8/fileutils.rb:243:in
fu_mkdir'
from C:/Users/Luis/Tools/Ruby/ruby-1.8.6-p398-i386-mingw32/lib/ruby/1.8/fileutils.rb:217:in mkdir_p'
from C:/Users/Luis/Tools/Ruby/ruby-1.8.6-p398-i386-mingw32/lib/ruby/1.8/fileutils.rb:215:in
reverse_each'
from C:/Users/Luis/Tools/Ruby/ruby-1.8.6-p398-i386-mingw32/lib/ruby/1.8/fileutils.rb:215:in mkdir_p'
from C:/Users/Luis/Tools/Ruby/ruby-1.8.6-p398-i386-mingw32/lib/ruby/1.8/fileutils.rb:201:in
each'
from C:/Users/Luis/Tools/Ruby/ruby-1.8.6-p398-i386-mingw32/lib/ruby/1.8/fileutils.rb:201:in `mkdir_p'
from -e:1

C:/Users/Luis/Tools/Ruby/ruby-1.9.1-p378-i386-mingw32/lib/ruby/1.9.1/fileutils.rb:243:in mkdir': File exists - C: (Errno::EEXIST)
from C:/Users/Luis/Tools/Ruby/ruby-1.9.1-p378-i386-mingw32/lib/ruby/1.9.1/fileutils.rb:243:in
fu_mkdir'
from C:/Users/Luis/Tools/Ruby/ruby-1.9.1-p378-i386-mingw32/lib/ruby/1.9.1/fileutils.rb:217:in block (2 levels)
in mkdir_p'
from C:/Users/Luis/Tools/Ruby/ruby-1.9.1-p378-i386-mingw32/lib/ruby/1.9.1/fileutils.rb:215:in
reverse_each'
from C:/Users/Luis/Tools/Ruby/ruby-1.9.1-p378-i386-mingw32/lib/ruby/1.9.1/fileutils.rb:215:in block in mkdir_p'
from C:/Users/Luis/Tools/Ruby/ruby-1.9.1-p378-i386-mingw32/lib/ruby/1.9.1/fileutils.rb:201:in
each'
from C:/Users/Luis/Tools/Ruby/ruby-1.9.1-p378-i386-mingw32/lib/ruby/1.9.1/fileutils.rb:201:in mkdir_p'
from -e:1:in
'

C:/Users/Luis/Tools/Ruby/ruby-1.9.2-rc1-i386-mingw32/lib/ruby/1.9.1/fileutils.rb:243:in mkdir': File exists - C: (Errno::EEXIST)
from C:/Users/Luis/Tools/Ruby/ruby-1.9.2-rc1-i386-mingw32/lib/ruby/1.9.1/fileutils.rb:243:in
fu_mkdir'
from C:/Users/Luis/Tools/Ruby/ruby-1.9.2-rc1-i386-mingw32/lib/ruby/1.9.1/fileutils.rb:217:in block (2 levels) in mkdir_p'
from C:/Users/Luis/Tools/Ruby/ruby-1.9.2-rc1-i386-mingw32/lib/ruby/1.9.1/fileutils.rb:215:in
reverse_each'
from C:/Users/Luis/Tools/Ruby/ruby-1.9.2-rc1-i386-mingw32/lib/ruby/1.9.1/fileutils.rb:215:in block in mkdir_p'
from C:/Users/Luis/Tools/Ruby/ruby-1.9.2-rc1-i386-mingw32/lib/ruby/1.9.1/fileutils.rb:201:in
each'
from C:/Users/Luis/Tools/Ruby/ruby-1.9.2-rc1-i386-mingw32/lib/ruby/1.9.1/fileutils.rb:201:in mkdir_p'
from -e:1:in
'

And same happens with trunk.

After digging a little bit on test_fileutils.rb found that even running the tests fails:

1) Error:
test_mkdir_p(TestFileUtils):
Errno::EEXIST: File exists - C:
test/fileutils/test_fileutils.rb:767:in block in test_mkdir_p'
test/fileutils/test_fileutils.rb:766:in
each'
test/fileutils/test_fileutils.rb:766:in `test_mkdir_p'

53 tests, 278 assertions, 0 failures, 1 errors, 0 skips

Further investigation pointed that the rescue of SystemCallError in mkdir_p is evaluating for File.directory? of dir, when dir has actually been altered inside fu_mkdir:

path = path.sub(%r</\z>, '')

But this change, been made in a local variable, is not seen by mkdir_p.

So:

1) mkdir_p sends "C:/" to fu_mkdir
2) fu_mkdir trims it to "C:"
3) fu_mkdir invokes Dir.mkdir "C:" and Errno::EEXIST is raised
4) mkdir_p catches it but evaluates File.directory? for "C:/" instead of "C:"
5) mkdir_p re-raises the exception since File.directory?("C:/") == false

If I'm not missing something, that is wrong.

Attached is a simple patch that correct the issue, however, I think the real problem is File.stat that fails:

C:\Users\Luis>ruby -v -e "puts File.stat('C:').inspect; puts File.stat('C:/').inspect"
ruby 1.9.2dev (2010-07-02) [i386-mingw32]
#
-e:1:in stat': Permission denied - C:/ (Errno::EACCES)
from -e:1:in
'

You receive Errno::EACCES for C:/, but that doesn't happen on JRuby or IronRuby:

IronRuby 1.0.0.0 on .NET 4.0.30319.1

#<File::Stat dev=3, ino=0, mode=16768, nlink=1, uid=0, gid=0, rdev=3, size=0, blksize=nil, blocks=nil, atime=Sat Jul 10
17:12:25 -0300 2010, mtime=Sat Jul 10 17:12:25 -0300 2010, ctime=Sun Feb 28 11:29:05 -0300 2010
#<File::Stat dev=3, ino=0, mode=16768, nlink=1, uid=0, gid=0, rdev=3, size=0, blksize=nil, blocks=nil, atime=Sat Jul 10
16:48:38 -0300 2010, mtime=Sat Jul 10 16:48:38 -0300 2010, ctime=Mon Jul 13 23:38:56 -0300 2009

jruby 1.4.1 (ruby 1.8.7 patchlevel 174) (2010-04-26 ea6db6a) (Java HotSpot(TM) Client VM 1.6.0_18) [x86-java]

#
#

jruby 1.5.1 (ruby 1.8.7 patchlevel 249) (2010-06-06 f3a3480) (Java HotSpot(TM) Client VM 1.6.0_18) [x86-java]

#
#
=end

0001-Workaround-File.stat-Errno-EACCES-bug-for-mkdir_p.patch Magnifier (710 Bytes) Luis Lavena, 07/11/2010 05:19 AM

History

#1 Updated by Luis Lavena almost 5 years ago

=begin
Please disregard this bug report.

The root of the issue is a broken VirtualStore folder, used by File redirection service under x64 OS.

Either E-TextEditor or Cygwin installation broke the permissions of it.

Apologizes for the noise.

Thank you.
=end

#2 Updated by Marc-Andre Lafortune almost 5 years ago

  • Status changed from Open to Closed

=begin

=end

#3 Updated by Nobuyoshi Nakada almost 5 years ago

=begin
Hi,

At Sun, 11 Jul 2010 05:20:01 +0900,
Luis Lavena wrote in :

Further investigation pointed that the rescue of
SystemCallError in mkdir_p is evaluating for File.directory?
of dir, when dir has actually been altered inside fu_mkdir:

path = path.sub(%r</\z>, '')

But this change, been made in a local variable, is not seen by mkdir_p.

This substitution is wrong on Windows.

So:

1) mkdir_p sends "C:/" to fu_mkdir
2) fu_mkdir trims it to "C:"
3) fu_mkdir invokes Dir.mkdir "C:" and Errno::EEXIST is raised
4) mkdir_p catches it but evaluates File.directory? for "C:/" instead of "C:"
5) mkdir_p re-raises the exception since File.directory?("C:/") == false

If I'm not missing something, that is wrong.

Note that C:/ and C: don't refere same directory. The latter
is C:., the cwd of C: drive.

At Sun, 11 Jul 2010 08:05:18 +0900,
Luis Lavena wrote in :

The root of the issue is a broken VirtualStore folder, used
by File redirection service under x64 OS.

That means that File.stat on VirtualStore folders isn't
working. It seems like a bug.

--
Nobu Nakada

=end

#4 Updated by Luis Lavena almost 5 years ago

=begin
On Sat, Jul 10, 2010 at 9:39 PM, Nobuyoshi Nakada nobu@ruby-lang.org wrote:

Note that C:/ and C: don't refere same directory.  The latter
is C:., the cwd of C: drive.

Indeed, after tracing it over debug-land and reading forgotten MSDN
documentation found that I was wrong.

That means that File.stat on VirtualStore folders isn't
working.  It seems like a bug.

Is not a Ruby bug, it was actually a permission issue caused by a bug
or something by the installer process of E-TextEditor or the cygwin
bundled by it.

I was not able to cd ino %LOCALAPPDATA%\VirtualStore after that.

WOW64 redirection is automatically enabled once a 32bits application
start working on a x64 version of Windows, due the permission issues,
it was not possible FindFirstFileW or GetFileAttributeW to it.

Thank you for the time you took answering this and again apologize the
noise I have caused.
--
Luis Lavena
AREA 17
-
Perfection in design is achieved not when there is nothing more to add,
but rather when there is nothing more to take away.
Antoine de Saint-Exupéry

=end

Also available in: Atom PDF