Cannot resolve or detect symlinks on Windows #3287

Closed
nlowe opened this Issue Aug 26, 2015 · 7 comments

Comments

Projects
None yet
5 participants
@nlowe

nlowe commented Aug 26, 2015

jruby 1.7.21 (1.9.3p551) 2015-07-07 a741a82 on Java HotSpot(TM) 64-Bit Server VM 1.8.0_51-b16 +jit [Windows 8.1-amd64]

While doing some testing for running gollum under windows I discovered that JRuby under windows appears to lack support for resolving symlinks. From gollum/gollum#1044:


It doesn't seem that Pathname can detect windows symlinks. For this test, I have created the following:

  • C:\SymlinkTest\a.txt
  • C:\SymlinkTest\b.txt => softlink to C:\SymlinkTest\a.txt (mklink C:\SymlinkTest\b.txt C:\SymlinkTest\a.txt)
  • C:\SymlinkTest\c.txt => hardlink to C:\SymlinkTest\a.txt (fsutil hardlink create C:\SymlinkTest\b.txt C:\SymlinkTest\a.txt)
irb(main):001:0> b = Pathname.new('C:\\SymlinkTest\\b.txt')
=> <Pathname:C:\SymlinkTest\b.txt>
irb(main):002:0> p.symlink?
=> false
irb(main):003:0> c = Pathname.new('C:\\SymlinkTest\\c.txt')
=> <Pathname:C:\SymlinkTest\c.txt>
irb(main):004:0> p.symlink?
=> false
irb(main):005:0> b.realpath
=> <Pathname:C:\SymlinkTest\b.txt>
irb(main):006:0> c.realpath
=> <Pathname:C:\SymlinkTest\c.txt>
irb(main):007:0> File.readlink('C:\\SymlinkTests\\b.txt')
NotImplementedError: readlink unsupported or native support failed to load
        from org/jruby/RubyFile.java:1049:in `readlink'
        from (irb):1:in `evaluate'
        from org/jruby/RubyKernel.java:1079:in `eval'
        from org/jruby/RubyKernel.java:1479:in `loop'
        from org/jruby/RubyKernel.java:1242:in `catch'
        from org/jruby/RubyKernel.java:1242:in `catch'
        from C:/jruby-1.7.21/bin/jirb:13:in `(root)'
irb(main):008:0> File.readlink('C:\\SymlinkTests\\c.txt')
NotImplementedError: readlink unsupported or native support failed to load
        from org/jruby/RubyFile.java:1049:in `readlink'
        from (irb):2:in `evaluate'
        from org/jruby/RubyKernel.java:1079:in `eval'
        from org/jruby/RubyKernel.java:1479:in `loop'
        from org/jruby/RubyKernel.java:1242:in `catch'
        from org/jruby/RubyKernel.java:1242:in `catch'
        from C:/jruby-1.7.21/bin/jirb:13:in `(root)'

@nlowe nlowe changed the title from Cannot resolve symlinks on Windows to Cannot resolve or detect symlinks on Windows Aug 26, 2015

@enebo

This comment has been minimized.

Show comment
Hide comment
@enebo

enebo Aug 27, 2015

Member

Article on links for windows as a note when we try and figure this out: http://www.sevenforums.com/tutorials/278262-mklink-create-use-links-windows.html. This will get fixed upstream in jnr/jnr-posix to get fixed in jruby proper.

Member

enebo commented Aug 27, 2015

Article on links for windows as a note when we try and figure this out: http://www.sevenforums.com/tutorials/278262-mklink-create-use-links-windows.html. This will get fixed upstream in jnr/jnr-posix to get fixed in jruby proper.

@headius

This comment has been minimized.

Show comment
Hide comment
@headius

headius Sep 3, 2015

Member

Does this work in MRI? I thought I might find that they had shimmed out a win32 version of readlink, but it appears they simply raise NotImplementedError when readlink is not present on the given system. We could do that too; perhaps gollum checks if it's supported before trying to use it?

Member

headius commented Sep 3, 2015

Does this work in MRI? I thought I might find that they had shimmed out a win32 version of readlink, but it appears they simply raise NotImplementedError when readlink is not present on the given system. We could do that too; perhaps gollum checks if it's supported before trying to use it?

@headius

This comment has been minimized.

Show comment
Hide comment
@headius

headius Sep 3, 2015

Member

I went ahead and "fixed" this the same way MRI does, by marking File.readlink as not implemented on Windows. I do feel like we could both provide readlink using Win32 API calls, but our behavior now aligns with MRI.

@techwiz24 Tonight's nightly builds of 1.7.23 and 9.0.2.0 will have this fix...perhaps you can check whether it's working? I suspect gollum checks for readlink or perhaps it should. If you believe we should actually support readlink, we'll need to have that conversation with ruby-core too.

Member

headius commented Sep 3, 2015

I went ahead and "fixed" this the same way MRI does, by marking File.readlink as not implemented on Windows. I do feel like we could both provide readlink using Win32 API calls, but our behavior now aligns with MRI.

@techwiz24 Tonight's nightly builds of 1.7.23 and 9.0.2.0 will have this fix...perhaps you can check whether it's working? I suspect gollum checks for readlink or perhaps it should. If you believe we should actually support readlink, we'll need to have that conversation with ruby-core too.

@nlowe

This comment has been minimized.

Show comment
Hide comment
@nlowe

nlowe Sep 3, 2015

@headius Thank you for looking into this. For the specific test that was failing, this is what gollum looks for:

# Return the file path to this file on disk, if available.
#
# Returns nil if the file isn't available on disk. This can occur if the
# repo is bare, if the commit isn't the HEAD, or if there are problems
# resolving symbolic links.
def get_disk_reference(name, commit)
  return false if @wiki.repo.bare
  return false if commit.sha != @wiki.repo.head.commit.sha

  # This will try to resolve symbolic links, as well
  pathname = Pathname.new(::File.expand_path(::File.join(@wiki.repo.path, '..', name)))
  if pathname.symlink?
    source   = ::File.readlink(pathname.to_path)
    realpath = ::File.join(::File.dirname(pathname.to_path), source)
    return false unless realpath && ::File.exist?(realpath)
    @on_disk_path = realpath.to_s
  else
    @on_disk_path = pathname.to_path
  end
  true
end

I believe what you're saying is that we'll have to wrap pathname.symlink? to catch a NotImplemented under windows, correct?

nlowe commented Sep 3, 2015

@headius Thank you for looking into this. For the specific test that was failing, this is what gollum looks for:

# Return the file path to this file on disk, if available.
#
# Returns nil if the file isn't available on disk. This can occur if the
# repo is bare, if the commit isn't the HEAD, or if there are problems
# resolving symbolic links.
def get_disk_reference(name, commit)
  return false if @wiki.repo.bare
  return false if commit.sha != @wiki.repo.head.commit.sha

  # This will try to resolve symbolic links, as well
  pathname = Pathname.new(::File.expand_path(::File.join(@wiki.repo.path, '..', name)))
  if pathname.symlink?
    source   = ::File.readlink(pathname.to_path)
    realpath = ::File.join(::File.dirname(pathname.to_path), source)
    return false unless realpath && ::File.exist?(realpath)
    @on_disk_path = realpath.to_s
  else
    @on_disk_path = pathname.to_path
  end
  true
end

I believe what you're saying is that we'll have to wrap pathname.symlink? to catch a NotImplemented under windows, correct?

@nlowe nlowe referenced this issue in gollum/gollum Sep 3, 2015

Open

Support Windows via JRuby - Meta Issue #1044

0 of 7 tasks complete
@bartkamphorst

This comment has been minimized.

Show comment
Hide comment
@bartkamphorst

bartkamphorst Sep 3, 2015

I do feel like we could both provide readlink using Win32 API calls, but our behavior now aligns with MRI.

Thank you @headius , appreciate your time and effort! Another way to go might be to rely on java.nio.file, which, as @techwiz24 confirmed, can be used to read symlinks on Windows. Unsure if that would be a viable option though. And yes, there is of course the alignment with MRI issue.

I do feel like we could both provide readlink using Win32 API calls, but our behavior now aligns with MRI.

Thank you @headius , appreciate your time and effort! Another way to go might be to rely on java.nio.file, which, as @techwiz24 confirmed, can be used to read symlinks on Windows. Unsure if that would be a viable option though. And yes, there is of course the alignment with MRI issue.

@Wadeck

This comment has been minimized.

Show comment
Hide comment
@Wadeck

Wadeck Apr 20, 2018

Necroposting apart, it could good for reader coming from Google search.

rely on java.nio.file

I imagine you meant: File#toPath().toRealPath()
Just a short notice on that workaround, it works iff you check a file/folder that exist. If you have a symlink inside the hierarchy of a file that is not yet written, the method will throw an exception.

Concerning the support in "native" getCanonicalPath, it was requested in OpenJDK in 2012 and still not done.

Wadeck commented Apr 20, 2018

Necroposting apart, it could good for reader coming from Google search.

rely on java.nio.file

I imagine you meant: File#toPath().toRealPath()
Just a short notice on that workaround, it works iff you check a file/folder that exist. If you have a symlink inside the hierarchy of a file that is not yet written, the method will throw an exception.

Concerning the support in "native" getCanonicalPath, it was requested in OpenJDK in 2012 and still not done.

@bartkamphorst

This comment has been minimized.

Show comment
Hide comment
@bartkamphorst

bartkamphorst Apr 20, 2018

Thanks @Wadeck for sharing this info!

Thanks @Wadeck for sharing this info!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment