From 073d7ffc2745386f42048847ec7209e47cbdab8f Mon Sep 17 00:00:00 2001 From: Alan Williams Date: Sat, 3 May 2014 14:27:36 -0700 Subject: [PATCH 1/3] Implement fallbacks for functions unavailable in older versions of Windows --- src/liblibc/lib.rs | 15 ++----- src/libnative/io/file_win32.rs | 12 +++--- src/libstd/os.rs | 78 ++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 16 deletions(-) diff --git a/src/liblibc/lib.rs b/src/liblibc/lib.rs index a4593c1cb5afc..e96ddfbf7bdc1 100644 --- a/src/liblibc/lib.rs +++ b/src/liblibc/lib.rs @@ -250,7 +250,6 @@ pub use funcs::bsd43::{shutdown}; #[cfg(windows)] pub use funcs::extra::kernel32::{FlushFileBuffers, SetEndOfFile, CreateFileW}; #[cfg(windows)] pub use funcs::extra::kernel32::{CreateDirectoryW, FindFirstFileW}; #[cfg(windows)] pub use funcs::extra::kernel32::{FindNextFileW, FindClose, DeleteFileW}; -#[cfg(windows)] pub use funcs::extra::kernel32::{GetFinalPathNameByHandleW, CreateSymbolicLinkW}; #[cfg(windows)] pub use funcs::extra::kernel32::{CreateHardLinkW, CreateEventW}; #[cfg(windows)] pub use funcs::extra::kernel32::{FlushFileBuffers, CreateNamedPipeW}; #[cfg(windows)] pub use funcs::extra::kernel32::{SetNamedPipeHandleState, WaitNamedPipeW}; @@ -1733,6 +1732,7 @@ pub mod consts { pub static ERROR_INVALID_HANDLE : c_int = 6; pub static ERROR_BROKEN_PIPE: c_int = 109; pub static ERROR_DISK_FULL : c_int = 112; + pub static ERROR_CALL_NOT_IMPLEMENTED : c_int = 120; pub static ERROR_INSUFFICIENT_BUFFER : c_int = 122; pub static ERROR_INVALID_NAME : c_int = 123; pub static ERROR_ALREADY_EXISTS : c_int = 183; @@ -4185,9 +4185,9 @@ pub mod funcs { LPSTARTUPINFO, LPPROCESS_INFORMATION, LPMEMORY_BASIC_INFORMATION, - LPSYSTEM_INFO, BOOLEAN, - HANDLE, LPHANDLE, LARGE_INTEGER, - PLARGE_INTEGER, LPFILETIME}; + LPSYSTEM_INFO, HANDLE, LPHANDLE, + LARGE_INTEGER, PLARGE_INTEGER, + LPFILETIME}; extern "system" { pub fn GetEnvironmentVariableW(n: LPCWSTR, @@ -4297,9 +4297,6 @@ pub mod funcs { pub fn MoveFileExW(lpExistingFileName: LPCWSTR, lpNewFileName: LPCWSTR, dwFlags: DWORD) -> BOOL; - pub fn CreateSymbolicLinkW(lpSymlinkFileName: LPCWSTR, - lpTargetFileName: LPCWSTR, - dwFlags: DWORD) -> BOOLEAN; pub fn CreateHardLinkW(lpSymlinkFileName: LPCWSTR, lpTargetFileName: LPCWSTR, lpSecurityAttributes: LPSECURITY_ATTRIBUTES) @@ -4312,10 +4309,6 @@ pub mod funcs { dwCreationDisposition: DWORD, dwFlagsAndAttributes: DWORD, hTemplateFile: HANDLE) -> HANDLE; - pub fn GetFinalPathNameByHandleW(hFile: HANDLE, - lpszFilePath: LPCWSTR, - cchFilePath: DWORD, - dwFlags: DWORD) -> DWORD; pub fn ReadFile(hFile: HANDLE, lpBuffer: LPVOID, nNumberOfBytesToRead: DWORD, diff --git a/src/libnative/io/file_win32.rs b/src/libnative/io/file_win32.rs index 6a6fb31d3e3f7..ea105b267c1ed 100644 --- a/src/libnative/io/file_win32.rs +++ b/src/libnative/io/file_win32.rs @@ -408,6 +408,7 @@ pub fn chown(_p: &CString, _uid: int, _gid: int) -> IoResult<()> { pub fn readlink(p: &CString) -> IoResult { // FIXME: I have a feeling that this reads intermediate symlinks as well. + use std::os::win32::compat::kernel32::GetFinalPathNameByHandleW; let handle = unsafe { as_utf16_p(p.as_str().unwrap(), |p| { libc::CreateFileW(p, @@ -425,10 +426,10 @@ pub fn readlink(p: &CString) -> IoResult { // Specify (sz - 1) because the documentation states that it's the size // without the null pointer let ret = fill_utf16_buf_and_decode(|buf, sz| unsafe { - libc::GetFinalPathNameByHandleW(handle, - buf as *u16, - sz - 1, - libc::VOLUME_NAME_DOS) + GetFinalPathNameByHandleW(handle, + buf as *u16, + sz - 1, + libc::VOLUME_NAME_DOS) }); let ret = match ret { Some(ref s) if s.starts_with(r"\\?\") => Ok(Path::new(s.slice_from(4))), @@ -440,9 +441,10 @@ pub fn readlink(p: &CString) -> IoResult { } pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> { + use std::os::win32::compat::kernel32::CreateSymbolicLinkW; super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| { as_utf16_p(dst.as_str().unwrap(), |dst| { - unsafe { libc::CreateSymbolicLinkW(dst, src, 0) } + unsafe { CreateSymbolicLinkW(dst, src, 0) } }) as libc::BOOL })) } diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 071aae974db1e..5152399bf7eba 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -145,6 +145,84 @@ pub mod win32 { t.push(0u16); f(t.as_ptr()) } + + pub mod compat { + use kinds::Copy; + use option::Option; + use c_str::ToCStr; + use intrinsics::{atomic_store_relaxed, transmute}; + use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID}; + use os::win32::as_utf16_p; + + #[link_name="kernel32"] + extern "system" { + fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; + fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID; + } + + unsafe fn store_func(ptr: *mut T, module: &str, symbol: &str, fallback: T) { + as_utf16_p(module, |module| { + symbol.with_c_str(|symbol| { + let handle = GetModuleHandleW(module); + let func: Option = transmute(GetProcAddress(handle, symbol)); + atomic_store_relaxed(ptr, func.unwrap_or(fallback)) + }) + }) + } + + macro_rules! compat_fn( + ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) + -> $rettype:ty $fallback:block) => ( + #[inline(always)] + pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype { + static mut ptr: extern "system" fn($($argname: $argtype),*) -> $rettype = thunk; + + extern "system" fn thunk($($argname: $argtype),*) -> $rettype { + unsafe { + ::os::win32::compat::store_func(&mut ptr, + stringify!($module), + stringify!($symbol), + fallback); + ::intrinsics::atomic_load_relaxed(&ptr)($($argname),*) + } + } + + extern "system" fn fallback($($argname: $argtype),*) -> $rettype $fallback + + ::intrinsics::atomic_load_relaxed(&ptr)($($argname),*) + } + ); + + ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) $fallback:block) => ( + compat_fn!($module::$symbol($($argname: $argtype),*) -> () $fallback) + ) + ) + + pub mod kernel32 { + use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE}; + use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED; + + #[link_name="kernel32"] + extern "system" { + fn SetLastError(dwErrCode: DWORD); + } + + compat_fn!(kernel32::CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR, + _lpTargetFileName: LPCWSTR, + _dwFlags: DWORD) -> BOOLEAN { + unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); } + 0 + }) + + compat_fn!(kernel32::GetFinalPathNameByHandleW(_hFile: HANDLE, + _lpszFilePath: LPCWSTR, + _cchFilePath: DWORD, + _dwFlags: DWORD) -> DWORD { + unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); } + 0 + }) + } + } } /* From 8543e1b6ced6547057a07f43c413193deb3f9cb1 Mon Sep 17 00:00:00 2001 From: Alan Williams Date: Tue, 6 May 2014 20:39:06 -0700 Subject: [PATCH 2/3] Add documentation for Windows compatibility layer and remove unneeded attributes --- src/libstd/os.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 5152399bf7eba..0458b9135f6ba 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -154,12 +154,14 @@ pub mod win32 { use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID}; use os::win32::as_utf16_p; - #[link_name="kernel32"] extern "system" { fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID; } + // store_func() is idempotent, so using relaxed ordering for the atomics should be enough. + // This way, calling a function in this compatibility layer (after it's loaded) shouldn't + // be any slower than a regular DLL call. unsafe fn store_func(ptr: *mut T, module: &str, symbol: &str, fallback: T) { as_utf16_p(module, |module| { symbol.with_c_str(|symbol| { @@ -170,6 +172,17 @@ pub mod win32 { }) } + /// Macro for creating a compatibility fallback for a Windows function + /// + /// # Example + /// ``` + /// compat_fn!(adll32::SomeFunctionW(_arg: LPCWSTR) { + /// // Fallback implementation + /// }) + /// ``` + /// + /// Note that arguments unused by the fallback implementation should not be called `_` as + /// they are used to be passed to the real function if available. macro_rules! compat_fn( ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback:block) => ( @@ -198,11 +211,16 @@ pub mod win32 { ) ) + /// Compatibility layer for functions in `kernel32.dll` + /// + /// Latest versions of Windows this is needed for: + /// + /// * `CreateSymbolicLinkW`: Windows XP, Windows Server 2003 + /// * `GetFinalPathNameByHandleW`: Windows XP, Windows Server 2003 pub mod kernel32 { use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE}; use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED; - #[link_name="kernel32"] extern "system" { fn SetLastError(dwErrCode: DWORD); } From facd1270c636beb7c2c8153424ffd6dda4179d59 Mon Sep 17 00:00:00 2001 From: Alan Williams Date: Wed, 7 May 2014 11:06:15 -0700 Subject: [PATCH 3/3] Move Windows compatibility layer to libnative --- src/libnative/io/c_win32.rs | 93 ++++++++++++++++++++++++++++++++ src/libnative/io/file_win32.rs | 4 +- src/libnative/lib.rs | 1 + src/libstd/os.rs | 96 ---------------------------------- 4 files changed, 96 insertions(+), 98 deletions(-) diff --git a/src/libnative/io/c_win32.rs b/src/libnative/io/c_win32.rs index 6c84424e97a0d..382746decd2d2 100644 --- a/src/libnative/io/c_win32.rs +++ b/src/libnative/io/c_win32.rs @@ -62,3 +62,96 @@ extern "system" { pub fn CancelIo(hFile: libc::HANDLE) -> libc::BOOL; } + +pub mod compat { + use std::intrinsics::{atomic_store_relaxed, transmute}; + use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID}; + use std::os::win32::as_utf16_p; + + extern "system" { + fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; + fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID; + } + + // store_func() is idempotent, so using relaxed ordering for the atomics should be enough. + // This way, calling a function in this compatibility layer (after it's loaded) shouldn't + // be any slower than a regular DLL call. + unsafe fn store_func(ptr: *mut T, module: &str, symbol: &str, fallback: T) { + as_utf16_p(module, |module| { + symbol.with_c_str(|symbol| { + let handle = GetModuleHandleW(module); + let func: Option = transmute(GetProcAddress(handle, symbol)); + atomic_store_relaxed(ptr, func.unwrap_or(fallback)) + }) + }) + } + + /// Macro for creating a compatibility fallback for a Windows function + /// + /// # Example + /// ``` + /// compat_fn!(adll32::SomeFunctionW(_arg: LPCWSTR) { + /// // Fallback implementation + /// }) + /// ``` + /// + /// Note that arguments unused by the fallback implementation should not be called `_` as + /// they are used to be passed to the real function if available. + macro_rules! compat_fn( + ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) + -> $rettype:ty $fallback:block) => ( + #[inline(always)] + pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype { + static mut ptr: extern "system" fn($($argname: $argtype),*) -> $rettype = thunk; + + extern "system" fn thunk($($argname: $argtype),*) -> $rettype { + unsafe { + ::io::c::compat::store_func(&mut ptr, + stringify!($module), + stringify!($symbol), + fallback); + ::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*) + } + } + + extern "system" fn fallback($($argname: $argtype),*) -> $rettype $fallback + + ::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*) + } + ); + + ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) $fallback:block) => ( + compat_fn!($module::$symbol($($argname: $argtype),*) -> () $fallback) + ) + ) + + /// Compatibility layer for functions in `kernel32.dll` + /// + /// Latest versions of Windows this is needed for: + /// + /// * `CreateSymbolicLinkW`: Windows XP, Windows Server 2003 + /// * `GetFinalPathNameByHandleW`: Windows XP, Windows Server 2003 + pub mod kernel32 { + use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE}; + use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED; + + extern "system" { + fn SetLastError(dwErrCode: DWORD); + } + + compat_fn!(kernel32::CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR, + _lpTargetFileName: LPCWSTR, + _dwFlags: DWORD) -> BOOLEAN { + unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); } + 0 + }) + + compat_fn!(kernel32::GetFinalPathNameByHandleW(_hFile: HANDLE, + _lpszFilePath: LPCWSTR, + _cchFilePath: DWORD, + _dwFlags: DWORD) -> DWORD { + unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); } + 0 + }) + } +} diff --git a/src/libnative/io/file_win32.rs b/src/libnative/io/file_win32.rs index ea105b267c1ed..1c69392165d9a 100644 --- a/src/libnative/io/file_win32.rs +++ b/src/libnative/io/file_win32.rs @@ -408,7 +408,7 @@ pub fn chown(_p: &CString, _uid: int, _gid: int) -> IoResult<()> { pub fn readlink(p: &CString) -> IoResult { // FIXME: I have a feeling that this reads intermediate symlinks as well. - use std::os::win32::compat::kernel32::GetFinalPathNameByHandleW; + use io::c::compat::kernel32::GetFinalPathNameByHandleW; let handle = unsafe { as_utf16_p(p.as_str().unwrap(), |p| { libc::CreateFileW(p, @@ -441,7 +441,7 @@ pub fn readlink(p: &CString) -> IoResult { } pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> { - use std::os::win32::compat::kernel32::CreateSymbolicLinkW; + use io::c::compat::kernel32::CreateSymbolicLinkW; super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| { as_utf16_p(dst.as_str().unwrap(), |dst| { unsafe { CreateSymbolicLinkW(dst, src, 0) } diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs index 4c0c4dcc18e9e..6309e4df40e60 100644 --- a/src/libnative/lib.rs +++ b/src/libnative/lib.rs @@ -50,6 +50,7 @@ html_root_url = "http://static.rust-lang.org/doc/master")] #![deny(unused_result, unused_must_use)] #![allow(non_camel_case_types)] +#![feature(macro_rules)] // NB this crate explicitly does *not* allow glob imports, please seriously // consider whether they're needed before adding that feature here (the diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 0458b9135f6ba..071aae974db1e 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -145,102 +145,6 @@ pub mod win32 { t.push(0u16); f(t.as_ptr()) } - - pub mod compat { - use kinds::Copy; - use option::Option; - use c_str::ToCStr; - use intrinsics::{atomic_store_relaxed, transmute}; - use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID}; - use os::win32::as_utf16_p; - - extern "system" { - fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; - fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID; - } - - // store_func() is idempotent, so using relaxed ordering for the atomics should be enough. - // This way, calling a function in this compatibility layer (after it's loaded) shouldn't - // be any slower than a regular DLL call. - unsafe fn store_func(ptr: *mut T, module: &str, symbol: &str, fallback: T) { - as_utf16_p(module, |module| { - symbol.with_c_str(|symbol| { - let handle = GetModuleHandleW(module); - let func: Option = transmute(GetProcAddress(handle, symbol)); - atomic_store_relaxed(ptr, func.unwrap_or(fallback)) - }) - }) - } - - /// Macro for creating a compatibility fallback for a Windows function - /// - /// # Example - /// ``` - /// compat_fn!(adll32::SomeFunctionW(_arg: LPCWSTR) { - /// // Fallback implementation - /// }) - /// ``` - /// - /// Note that arguments unused by the fallback implementation should not be called `_` as - /// they are used to be passed to the real function if available. - macro_rules! compat_fn( - ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) - -> $rettype:ty $fallback:block) => ( - #[inline(always)] - pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype { - static mut ptr: extern "system" fn($($argname: $argtype),*) -> $rettype = thunk; - - extern "system" fn thunk($($argname: $argtype),*) -> $rettype { - unsafe { - ::os::win32::compat::store_func(&mut ptr, - stringify!($module), - stringify!($symbol), - fallback); - ::intrinsics::atomic_load_relaxed(&ptr)($($argname),*) - } - } - - extern "system" fn fallback($($argname: $argtype),*) -> $rettype $fallback - - ::intrinsics::atomic_load_relaxed(&ptr)($($argname),*) - } - ); - - ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) $fallback:block) => ( - compat_fn!($module::$symbol($($argname: $argtype),*) -> () $fallback) - ) - ) - - /// Compatibility layer for functions in `kernel32.dll` - /// - /// Latest versions of Windows this is needed for: - /// - /// * `CreateSymbolicLinkW`: Windows XP, Windows Server 2003 - /// * `GetFinalPathNameByHandleW`: Windows XP, Windows Server 2003 - pub mod kernel32 { - use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE}; - use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED; - - extern "system" { - fn SetLastError(dwErrCode: DWORD); - } - - compat_fn!(kernel32::CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR, - _lpTargetFileName: LPCWSTR, - _dwFlags: DWORD) -> BOOLEAN { - unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); } - 0 - }) - - compat_fn!(kernel32::GetFinalPathNameByHandleW(_hFile: HANDLE, - _lpszFilePath: LPCWSTR, - _cchFilePath: DWORD, - _dwFlags: DWORD) -> DWORD { - unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); } - 0 - }) - } - } } /*