Skip to content

Instantly share code, notes, and snippets.

@phasis68
Created May 18, 2012 07:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save phasis68/1c431b679b26c947a7d2 to your computer and use it in GitHub Desktop.
Save phasis68/1c431b679b26c947a7d2 to your computer and use it in GitHub Desktop.
create junction example
require 'ffi'
from = "c:/work"
to = "c:/work_j"
module Dir::Functions
extend FFI::Library
FILE_DEVICE_FILE_SYSTEM = 0x00000009
INVALID_HANDLE_VALUE = 0xFFFFFFFF
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
ERROR_ALREADY_EXISTS = 183
METHOD_BUFFERED = 0
OPEN_EXISTING = 3
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
SHGFI_DISPLAYNAME = 0x000000200
SHGFI_PIDL = 0x000000008
typedef :ulong, :hwnd
typedef :ulong, :dword
typedef :ulong, :handle
typedef :ulong, :hresult
typedef :ushort, :word
ffi_lib :shell32
attach_function :SHGetFolderPathW, [:hwnd, :int, :handle, :dword, :buffer_out], :hresult
attach_function :SHGetFolderLocation, [:hwnd, :int, :handle, :dword, :pointer], :hresult
attach_function :SHGetFileInfo, [:ulong, :dword, :pointer, :uint, :uint], :ulong
ffi_lib :shlwapi
attach_function :PathIsDirectoryEmptyW, [:buffer_in], :bool
ffi_lib :kernel32
attach_function :CloseHandle, [:handle], :bool
attach_function :CreateDirectoryW, [:buffer_in, :pointer], :bool
attach_function :CreateFileW, [:buffer_in, :dword, :dword, :pointer, :dword, :dword, :handle], :handle
attach_function :DeviceIoControl, [:handle, :dword, :pointer, :dword, :pointer, :dword, :pointer, :pointer], :bool
attach_function :GetCurrentDirectoryW, [:dword, :buffer_out], :dword
attach_function :GetFileAttributesW, [:buffer_in], :dword
attach_function :GetLastError, [], :dword
attach_function :GetShortPathNameW, [:buffer_in, :buffer_out, :dword], :dword
attach_function :GetLongPathNameW, [:buffer_in, :buffer_out, :dword], :dword
attach_function :GetFullPathNameW, [:buffer_in, :dword, :buffer_out, :pointer], :dword
attach_function :RemoveDirectoryW, [:buffer_in], :bool
end
class REPARSE_JDATA_BUFFER < FFI::Struct
layout(
:ReparseTag, :ulong,
:ReparseDataLength, :ushort,
:Reserved, :ushort,
:SubstituteNameOffset, :ushort,
:SubstituteNameLength, :ushort,
:PrintNameOffset, :ushort,
:PrintNameLength, :ushort,
:PathBuffer, [:char, 1024]
)
end
include Dir::Functions
def self.CTL_CODE(device, function, method, access)
((device) << 16) | ((access) << 14) | ((function) << 2) | (method)
end
to = to.tr(File::SEPARATOR, File::ALT_SEPARATOR) # Normalize path
from = from.tr(File::SEPARATOR, File::ALT_SEPARATOR) # Normalize path
from_path = 0.chr * 1024
from.encode!("UTF-16LE")
from_path.encode!('UTF-16LE')
from += "\x00".encode("UTF-16LE");
length = GetFullPathNameW(from, from_path.size, from_path, nil)
if length == 0
raise SystemCallError, FFI.errno, "GetFullPathNameW"
else
from_path.strip!
end
to_path = 0.chr * 1024
to.encode!('UTF-16LE')
to_path.encode!('UTF-16LE')
to += "\x00".encode("UTF-16LE");
length = GetFullPathNameW(to, to_path.size, to_path, nil)
if length == 0
raise SystemCallError, FFI.errno, "GetFullPathNameW"
else
to_path.strip!
end
# You can create a junction to a directory that already exists, so
# long as it's empty.
unless CreateDirectoryW(to_path, nil)
if FFI.errno != ERROR_ALREADY_EXISTS
raise SystemCallError, FFI.errno, "CreateDirectoryW"
end
end
begin
# Generic read & write + open existing + reparse point & backup semantics
handle = CreateFileW(
to_path,
GENERIC_READ | GENERIC_WRITE,
0,
nil,
OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
0
)
if handle == INVALID_HANDLE_VALUE
raise SystemCallError, FFI.errno, "CreateFileW"
end
target = "\\??\\".encode('UTF-16LE') + from_path
rdb = REPARSE_JDATA_BUFFER.new
rdb[:ReparseTag] = 2684354563 # IO_REPARSE_TAG_MOUNT_POINT
rdb[:ReparseDataLength] = target.bytesize + 12
rdb[:Reserved] = 0
rdb[:SubstituteNameOffset] = 0
rdb[:SubstituteNameLength] = target.bytesize
rdb[:PrintNameOffset] = target.bytesize + 2
rdb[:PrintNameLength] = 0
rdb[:PathBuffer] = target
bytes = FFI::MemoryPointer.new(:ulong)
begin
bool = DeviceIoControl(
handle,
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, 0),
rdb,
rdb[:ReparseDataLength]+8,
nil,
0,
bytes,
nil
)
unless bool
error = GetLastError()
RemoveDirectoryW(to_path)
raise SystemCallError, error, "DeviceIoControl"
end
ensure
CloseHandle(handle)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment