Last active
August 29, 2015 14:26
-
-
Save kirillkh/5549fca1089d18835da0 to your computer and use it in GitHub Desktop.
win_canonicalize_bug
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "win_canonicalize_bug" | |
version = "1.0.0" | |
[dependencies] | |
libc = "*" | |
kernel32-sys = "0.1.3" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(linkage)] | |
#![feature(const_fn)] | |
#![feature(slice_patterns)] | |
#![feature(box_syntax)] | |
extern crate libc; | |
extern crate kernel32; | |
use std::os::windows::ffi::{OsStrExt, OsStringExt}; | |
use std::path::{Path, PathBuf}; | |
use std::io; | |
use std::ptr; | |
use std::ffi::OsString; | |
use kernel32::{GetFinalPathNameByHandleW, SetLastError}; | |
fn get_path(path: &Path) -> io::Result<PathBuf> { | |
let pw = path.as_os_str().encode_wide().collect::<Vec<_>>(); | |
let handle = unsafe { | |
kernel32::CreateFileW(pw.as_ptr(), | |
0, | |
0, | |
0 as *mut _, | |
libc::OPEN_EXISTING, | |
libc::FILE_ATTRIBUTE_NORMAL, | |
ptr::null_mut()) | |
}; | |
fill_utf16_buf(|buf, sz| unsafe { | |
kernel32::GetFinalPathNameByHandleW(handle, buf, sz, | |
libc::VOLUME_NAME_DOS) | |
// libc::VOLUME_NAME_GUID) | |
// libc::VOLUME_NAME_NONE) | |
// libc::VOLUME_NAME_NT) | |
}, |buf| { | |
PathBuf::from(OsString::from_wide(buf)) | |
}) | |
} | |
fn main() { | |
let p = Path::new("M:\\xx.txt"); | |
let g = get_path(&p); | |
println!("{:?}", g); | |
} | |
// copied from src/libstd/sys/windows/mod.rs | |
fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> io::Result<T> | |
where F1: FnMut(*mut u16, libc::DWORD) -> libc::DWORD, | |
F2: FnOnce(&[u16]) -> T | |
{ | |
// Start off with a stack buf but then spill over to the heap if we end up | |
// needing more space. | |
let mut stack_buf = [0u16; 512]; | |
let mut heap_buf = Vec::new(); | |
unsafe { | |
let mut n = stack_buf.len(); | |
loop { | |
let buf = if n <= stack_buf.len() { | |
&mut stack_buf[..] | |
} else { | |
let extra = n - heap_buf.len(); | |
heap_buf.reserve(extra); | |
heap_buf.set_len(n); | |
&mut heap_buf[..] | |
}; | |
// This function is typically called on windows API functions which | |
// will return the correct length of the string, but these functions | |
// also return the `0` on error. In some cases, however, the | |
// returned "correct length" may actually be 0! | |
// | |
// To handle this case we call `SetLastError` to reset it to 0 and | |
// then check it again if we get the "0 error value". If the "last | |
// error" is still 0 then we interpret it as a 0 length buffer and | |
// not an actual error. | |
SetLastError(0); | |
let k = match f1(buf.as_mut_ptr(), n as libc::DWORD) { | |
0 if libc::GetLastError() == 0 => 0, | |
0 => return Err(io::Error::last_os_error()), | |
n => n, | |
} as usize; | |
if k == n && libc::GetLastError() == | |
libc::ERROR_INSUFFICIENT_BUFFER as libc::DWORD { | |
n *= 2; | |
} else if k >= n { | |
n = k; | |
} else { | |
return Ok(f2(&buf[..k])) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment