use std::ffi::c_void;
use std::io::Error;
use std::mem::size_of;
use windows::core::PWSTR;
use windows::Wdk::Foundation::{NtClose, OBJECT_ATTRIBUTES};
use windows::Wdk::Storage::FileSystem::{NtOpenDirectoryObject, NtQueryDirectoryObject};
use windows::Wdk::System::SystemServices::DIRECTORY_QUERY;
use windows::Win32::Foundation::{
HANDLE, NTSTATUS, OBJ_CASE_INSENSITIVE, STATUS_BUFFER_TOO_SMALL, STATUS_SUCCESS, UNICODE_STRING
};
#[repr(C)]
struct ObjectDirectoryInformation {
name: UNICODE_STRING,
type_name: UNICODE_STRING,
}
pub fn find_sym_link(dir: &str, name: &str) -> Result<String, Error> {
let dir_handle = open_directory(None, dir, DIRECTORY_QUERY)?;
let mut query_context: u32 = 0; let mut length: u32;
unsafe {
loop {
length = 0;
let status = NtQueryDirectoryObject(
dir_handle,
Some(std::ptr::null_mut()), 0, true, false, &mut query_context, Some(&mut length), );
if status != STATUS_BUFFER_TOO_SMALL {
if status != STATUS_SUCCESS {
return Err(status_to_error(status, "unexpected error occurred:"));
}
break; }
let mut buffer = vec![0u8; length as usize];
let obj_info = buffer.as_mut_ptr() as *mut ObjectDirectoryInformation;
let status = NtQueryDirectoryObject(
dir_handle,
Some(buffer.as_mut_ptr() as *mut c_void),
length,
true, false, &mut query_context, Some(&mut length), );
if status != STATUS_SUCCESS {
return Err(status_to_error(status, "unexpected error occurred:"));
}
let obj_name = String::from_utf16_lossy(std::slice::from_raw_parts(
(*obj_info).name.Buffer.0,
(*obj_info).name.Length as usize / 2, ));
if obj_name.contains(name) {
let _ = NtClose(dir_handle); return Ok(obj_name); }
}
let _ = NtClose(dir_handle);
}
Err(Error::new(
std::io::ErrorKind::NotFound,
format!("symbolic link name '{}' not found in '{}'", name, dir),
))
}
fn open_directory(
root_handle: Option<HANDLE>,
dir: &str,
desired_access: u32,
) -> Result<HANDLE, Error> {
let dir_wide: Vec<u16> = dir.encode_utf16().chain(Some(0)).collect();
let us_dir = UNICODE_STRING {
Buffer: PWSTR(dir_wide.as_ptr() as *mut _),
Length: ((dir_wide.len() - 1) * 2) as u16,
MaximumLength: (dir_wide.len() * 2) as u16,
};
let obj_attr = OBJECT_ATTRIBUTES {
Length: size_of::<OBJECT_ATTRIBUTES>() as u32,
RootDirectory: root_handle.unwrap_or_default(),
ObjectName: &us_dir as *const _,
Attributes: OBJ_CASE_INSENSITIVE,
..Default::default()
};
let mut dir_handle = HANDLE::default();
unsafe {
let status = NtOpenDirectoryObject(&mut dir_handle, desired_access, &obj_attr);
if status == STATUS_SUCCESS {
Ok(dir_handle)
} else {
Err(status_to_error(status, "Error opening directory:"))
}
}
}
fn status_to_error(status: NTSTATUS, message: &str) -> Error {
let hresult = status.to_hresult();
let message = format!("{} {}", message, hresult.message());
Error::new(std::io::ErrorKind::Other, message)
}