use eyre::{bail, Context, Result};
use std::{
env::current_dir,
os::windows::{ffi::OsStrExt, fs::OpenOptionsExt},
path::{Path, PathBuf},
};
use windows::{
core::PCWSTR,
Win32::{
Foundation::{CloseHandle, GENERIC_READ, MAX_PATH},
Storage::FileSystem::{
CreateFileW, FileIdType, GetFileInformationByHandleEx, OpenFileById,
FILE_FLAG_BACKUP_SEMANTICS, FILE_GENERIC_READ, FILE_ID_DESCRIPTOR,
FILE_ID_DESCRIPTOR_0, FILE_INFO_BY_HANDLE_CLASS, FILE_SHARE_DELETE, FILE_SHARE_READ,
FILE_SHARE_WRITE, OPEN_EXISTING,
},
},
};
use crate::NormalizePathTrait;
pub fn get_id(path: std::path::PathBuf) -> eyre::Result<String> {
let id = realid(&path)?.to_string();
Ok(id)
}
pub fn find_path(id: &str) -> eyre::Result<PathBuf> {
let id: u64 = id.trim().parse()?;
let path = realpath(id)?;
let path = PathBuf::from(path).normalize()?;
Ok(path)
}
fn realid(path: &Path) -> Result<u64> {
use std::os::windows::io::AsRawHandle;
use windows::Win32::{
Foundation::HANDLE,
Storage::FileSystem::{GetFileInformationByHandle, BY_HANDLE_FILE_INFORMATION},
};
let file = std::fs::OpenOptions::new()
.read(true)
.custom_flags(FILE_FLAG_BACKUP_SEMANTICS.0)
.open(path)?;
let mut info: BY_HANDLE_FILE_INFORMATION = unsafe { std::mem::zeroed() };
let res =
unsafe { GetFileInformationByHandle(HANDLE(file.as_raw_handle() as isize), &mut info) };
if res.is_err() {
let last_error = std::io::Error::last_os_error();
bail!("path {} {}", path.display(), last_error);
};
Ok(((info.nFileIndexHigh as u64) << 32) | (info.nFileIndexLow as u64))
}
fn realpath(id: u64) -> Result<String> {
let share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
let mut dir_path: Vec<u16> = current_dir()?
.as_mut_os_str()
.encode_wide()
.chain(Some(0)) .collect();
let dir_path = PCWSTR(dir_path.as_mut_ptr());
let dir_handle = unsafe {
CreateFileW(
dir_path,
FILE_GENERIC_READ.0,
share_mode,
None,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
None,
)
}?;
let file_name_info_size = std::mem::size_of::<FILE_ID_DESCRIPTOR>();
let file_id_descriptor_inner = FILE_ID_DESCRIPTOR_0 { FileId: id as _ };
let file_id_descriptor = FILE_ID_DESCRIPTOR {
Type: FileIdType,
Anonymous: file_id_descriptor_inner,
dwSize: file_name_info_size as _,
};
let file_handle = unsafe {
OpenFileById(
dir_handle,
&file_id_descriptor as _,
GENERIC_READ.0,
share_mode,
None,
FILE_FLAG_BACKUP_SEMANTICS,
)
}?;
unsafe {
CloseHandle(dir_handle)?;
}
let file_name_info_class = FILE_INFO_BY_HANDLE_CLASS(2 as _); let name_size = file_name_info_size + (4 * MAX_PATH) as usize; let mut path_buffer: Vec<u16> = vec![0; name_size];
let name_ptr = path_buffer.as_mut_ptr();
unsafe {
GetFileInformationByHandleEx(
file_handle,
file_name_info_class,
name_ptr as _,
name_size.try_into().context("try_into")?,
)
}?;
unsafe {
CloseHandle(file_handle)?;
}
let file_path = decode_path(path_buffer)?;
Ok(file_path)
}
fn decode_path(buf: Vec<u16>) -> Result<String> {
let skip = 2;
let null_pos = buf
.iter()
.skip(skip)
.position(|&c| c == 0) .map(|pos| pos + 1)
.unwrap_or(buf.len());
let string = String::from_utf16(&buf[skip..null_pos + 1])?;
Ok(string)
}