Skip to content

Instantly share code, notes, and snippets.

@kirillkh
Last active August 29, 2015 14:26
Show Gist options
  • Save kirillkh/5549fca1089d18835da0 to your computer and use it in GitHub Desktop.
Save kirillkh/5549fca1089d18835da0 to your computer and use it in GitHub Desktop.
win_canonicalize_bug
[package]
name = "win_canonicalize_bug"
version = "1.0.0"
[dependencies]
libc = "*"
kernel32-sys = "0.1.3"
#![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