Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2c63fa529ddb
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 07313c1446c6
Choose a head ref
  • 2 commits
  • 2 files changed
  • 1 contributor

Commits on May 25, 2016

  1. Borrow code from djberg96/win32-file for symlink support.

    Permission given to relicense in JRuby by @djberg96 at
    #3926 (comment)
    headius committed May 25, 2016
    Copy the full SHA
    3d1ada0 View commit details
  2. Merge pull request #3926 from headius/windows_symlink

    Windows symlink support for #3905
    headius committed May 25, 2016
    2
    Copy the full SHA
    07313c1 View commit details
Showing with 161 additions and 1 deletion.
  1. +2 −1 core/src/main/ruby/jruby/kernel.rb
  2. +159 −0 core/src/main/ruby/jruby/kernel/file.rb
3 changes: 2 additions & 1 deletion core/src/main/ruby/jruby/kernel.rb
Original file line number Diff line number Diff line change
@@ -32,4 +32,5 @@
load 'jruby/kernel/time.rb'
load 'jruby/kernel/gc.rb'
load 'jruby/kernel/range.rb'
load 'jruby/kernel/load_error.rb'
load 'jruby/kernel/load_error.rb'
load 'jruby/kernel/file.rb'
159 changes: 159 additions & 0 deletions core/src/main/ruby/jruby/kernel/file.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Windows symlink support borrowed from djberg96/win32-file and ffi-win32-extensions

if org.jruby.platform.Platform::IS_WINDOWS

module JRuby
module Windows
module File
module Constants
FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400
INVALID_HANDLE_VALUE = (1 << FFI::Platform::ADDRESS_SIZE) - 1
INVALID_FILE_ATTRIBUTES = (1 << FFI::Platform::ADDRESS_SIZE) - 1
IO_REPARSE_TAG_SYMLINK = 0xA000000C
end

module Functions
extend FFI::Library
ffi_lib :kernel32

typedef :ulong, :dword
typedef :uintptr_t, :handle
typedef :pointer, :ptr

def self.attach_pfunc(*args)
attach_function(*args)
private args[0]
end

attach_pfunc :CloseHandle, [:handle], :bool
attach_pfunc :FindFirstFileW, [:buffer_in, :pointer], :handle
attach_pfunc :CreateSymbolicLinkW, [:buffer_in, :buffer_in, :dword], :bool
attach_pfunc :GetFileAttributesW, [:buffer_in], :dword

attach_pfunc :CreateFileW, [:buffer_in, :dword, :dword, :pointer, :dword, :dword, :handle], :handle
attach_pfunc :GetDiskFreeSpaceW, [:buffer_in, :pointer, :pointer, :pointer, :pointer], :bool
attach_pfunc :GetDriveTypeW, [:buffer_in], :uint
attach_pfunc :GetFileType, [:handle], :dword
attach_pfunc :GetFinalPathNameByHandleW, [:handle, :buffer_out, :dword, :dword], :dword
attach_pfunc :GetShortPathNameW, [:buffer_in, :buffer_out, :dword], :dword
attach_pfunc :GetLongPathNameW, [:buffer_in, :buffer_out, :dword], :dword
attach_pfunc :QueryDosDeviceA, [:string, :buffer_out, :dword], :dword
attach_pfunc :SetFileTime, [:handle, :ptr, :ptr, :ptr], :bool
attach_pfunc :SystemTimeToFileTime, [:ptr, :ptr], :bool

ffi_lib :shlwapi

attach_pfunc :PathFindExtensionW, [:buffer_in], :pointer
attach_pfunc :PathIsRootW, [:buffer_in], :bool
attach_pfunc :PathStripPathW, [:pointer], :void
attach_pfunc :PathRemoveBackslashW, [:buffer_in], :string
attach_pfunc :PathRemoveFileSpecW, [:pointer], :bool
attach_pfunc :PathRemoveExtensionW, [:buffer_in], :void
attach_pfunc :PathStripToRootW, [:buffer_in], :bool
end

module Structs
class FILETIME < FFI::Struct
layout(:dwLowDateTime, :ulong, :dwHighDateTime, :ulong)
end

class WIN32_FIND_DATA < FFI::Struct
layout(
:dwFileAttributes, :ulong,
:ftCreationTime, FILETIME,
:ftLastAccessTime, FILETIME,
:ftLastWriteTime, FILETIME,
:nFileSizeHigh, :ulong,
:nFileSizeLow, :ulong,
:dwReserved0, :ulong,
:dwReserved1, :ulong,
:cFileName, [:uint8, 260*2],
:cAlternateFileName, [:uint8, 14*2]
)
end
end
end
end
end

class File
include Windows::File::Constants
include Windows::File::Structs
extend Windows::File::Functions

# Creates a symbolic link called +new_name+ for the file or directory
# +old_name+.
#
# This method requires Windows Vista or later to work. Otherwise, it
# returns nil as per MRI.
#
def self.symlink(target, link)
target = string_check(target)
link = string_check(link)

flags = File.directory?(target) ? 1 : 0

wlink = link.wincode
wtarget = target.wincode

unless CreateSymbolicLinkW(wlink, wtarget, flags)
raise SystemCallError.new('CreateSymbolicLink', FFI.errno)
end

0 # Comply with spec
end

# Returns whether or not +file+ is a symlink.
#
def self.symlink?(file)
return false unless File.exist?(file)

bool = false
wfile = string_check(file).wincode

attrib = GetFileAttributesW(wfile)

if attrib == INVALID_FILE_ATTRIBUTES
raise SystemCallError.new('GetFileAttributes', FFI.errno)
end

if attrib & FILE_ATTRIBUTE_REPARSE_POINT > 0
begin
find_data = WIN32_FIND_DATA.new
handle = FindFirstFileW(wfile, find_data)

if handle == INVALID_HANDLE_VALUE
raise SystemCallError.new('FindFirstFile', FFI.errno)
end

if find_data[:dwReserved0] == IO_REPARSE_TAG_SYMLINK
bool = true
end
ensure
CloseHandle(handle)
end
end

bool
end

private

# Simulate Ruby's string checking
def self.string_check(arg)
return arg if arg.is_a?(String)
return arg.send(:to_str) if arg.respond_to?(:to_str, true) # MRI allows private to_str
return arg.to_path if arg.respond_to?(:to_path)
raise TypeError
end
end

class String
# Convenience method for converting strings to UTF-16LE for wide character
# functions that require it.
#
def wincode
(self.tr(File::SEPARATOR, File::ALT_SEPARATOR) + 0.chr).encode('UTF-16LE')
end
end
end