Bug #3556
closedFileUtils.mkdir_p fails trying to create C: under Windows
Description
=begin
Hello,
I've been experiencing weird problems with FileUtils.mkdir_p in RubyGems, as tried to document in [ruby-talk:365540]
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:
- Error:
test_mkdir_p(TestFileUtils):
Errno::EEXIST: File exists - C:
test/fileutils/test_fileutils.rb:767:inblock 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:
- mkdir_p sends "C:/" to fu_mkdir
- fu_mkdir trims it to "C:"
- fu_mkdir invokes Dir.mkdir "C:" and Errno::EEXIST is raised
- mkdir_p catches it but evaluates File.directory? for "C:/" instead of "C:"
- 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]
#<File::Stat dev=0x2, ino=0, mode=040755, nlink=1, uid=0, gid=0, rdev=0x2, size=0, blksize=nil, blocks=nil, atime=2010-07-10 17:12:25 -0300, mtime=2010-07-10 17:12:25 -0300, ctime=2010-02-28 11:29:05 -0300>
-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]
#<File::Stat dev=0x2, ino=0, mode=040777, nlink=1, uid=0, gid=0, rdev=0x2, size=24576, blksize=0, blocks=0, atime=Tue Jan 01 00:00:00 -0300 1980, mtime=Tue Jan 01 00:00:00 -0300 1980, ctime=Tue Jan 01 00:00:00 -0300 1980>
#<File::Stat dev=0x2, ino=0, mode=040777, nlink=1, uid=0, gid=0, rdev=0x2, size=24576, blksize=0, blocks=0, atime=Tue Jan 01 00:00:00 -0300 1980, mtime=Tue Jan 01 00:00:00 -0300 1980, ctime=Tue Jan 01 00:00:00 -0300 1980>
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]
#<File::Stat dev=0x2, ino=0, mode=040755, nlink=1, uid=0, gid=0, rdev=0x2, size=24576, blksize=512, blocks=0, atime=Tue
Jan 01 00:00:00 -0300 1980, mtime=Tue Jan 01 00:00:00 -0300 1980, ctime=Tue Jan 01 00:00:00 -0300 1980>
#<File::Stat dev=0x2, ino=0, mode=040755, nlink=1, uid=0, gid=0, rdev=0x2, size=24576, blksize=512, blocks=0, atime=Tue
Jan 01 00:00:00 -0300 1980, mtime=Tue Jan 01 00:00:00 -0300 1980, ctime=Tue Jan 01 00:00:00 -0300 1980>
=end
Files
Updated by luislavena (Luis Lavena) over 14 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
Updated by marcandre (Marc-Andre Lafortune) over 14 years ago
- Status changed from Open to Closed
=begin
=end
Updated by nobu (Nobuyoshi Nakada) over 14 years ago
=begin
Hi,
At Sun, 11 Jul 2010 05:20:01 +0900,
Luis Lavena wrote in [ruby-core:31177]:
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:
- mkdir_p sends "C:/" to fu_mkdir
- fu_mkdir trims it to "C:"
- fu_mkdir invokes Dir.mkdir "C:" and Errno::EEXIST is raised
- mkdir_p catches it but evaluates File.directory? for "C:/" instead of "C:"
- 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 [ruby-core:31179]:
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
Updated by luislavena (Luis Lavena) over 14 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