use std::fs::File;
#[cfg(unix)]
use std::os::unix::fs::MetadataExt;
#[cfg(windows)]
use std::{mem::zeroed, ptr};
#[cfg(windows)]
use winapi::shared::minwindef::DWORD;
#[cfg(windows)]
use winapi::um::{
fileapi::GetFileInformationByHandle, fileapi::BY_HANDLE_FILE_INFORMATION,
ioapiset::DeviceIoControl, winioctl::FSCTL_GET_REPARSE_POINT,
winnt::FILE_ATTRIBUTE_REPARSE_POINT, winnt::MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
};
#[cfg(not(windows))]
pub trait PortableFileExt {
fn portable_dev(&self) -> std::io::Result<u64>;
fn portable_ino(&self) -> std::io::Result<u64>;
}
#[cfg(windows)]
pub trait PortableFileExt: std::os::windows::io::AsRawHandle {
fn portable_dev(&self) -> std::io::Result<u64>;
fn portable_ino(&self) -> std::io::Result<u64>;
#[allow(unused_assignments, unused_variables)]
fn reparse_point<'a>(
&self,
space: &'a mut [u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize],
) -> std::io::Result<(DWORD, &'a REPARSE_DATA_BUFFER)> {
unsafe {
let mut bytes = 0;
cvt({
DeviceIoControl(
self.as_raw_handle(),
FSCTL_GET_REPARSE_POINT,
ptr::null_mut(),
0,
space.as_mut_ptr() as *mut _,
space.len() as DWORD,
&mut bytes,
ptr::null_mut(),
)
})?;
Ok((bytes, &*(space.as_ptr() as *const REPARSE_DATA_BUFFER)))
}
}
#[allow(unused_assignments, unused_variables)]
fn get_file_info(&self) -> std::io::Result<BY_HANDLE_FILE_INFORMATION> {
unsafe {
let mut info: BY_HANDLE_FILE_INFORMATION = zeroed();
cvt(GetFileInformationByHandle(self.as_raw_handle(), &mut info))?;
let mut reparse_tag = 0;
if info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
let mut b = [0; MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize];
if let Ok((_, buf)) = self.reparse_point(&mut b) {
reparse_tag = buf.ReparseTag;
}
}
Ok(info)
}
}
}
#[cfg(unix)]
impl PortableFileExt for File {
fn portable_dev(&self) -> std::io::Result<u64> {
Ok(self.metadata()?.dev())
}
fn portable_ino(&self) -> std::io::Result<u64> {
Ok(self.metadata()?.ino())
}
}
#[cfg(windows)]
impl PortableFileExt for File {
fn portable_dev(&self) -> std::io::Result<u64> {
let info = self.get_file_info()?;
Ok(info.dwVolumeSerialNumber.into())
}
fn portable_ino(&self) -> std::io::Result<u64> {
let info = self.get_file_info()?;
Ok((info.nFileIndexLow as u64) | ((info.nFileIndexHigh as u64) << 32))
}
}
#[cfg(windows)]
#[allow(dead_code, non_snake_case, non_camel_case_types)]
pub struct REPARSE_DATA_BUFFER {
pub ReparseTag: libc::c_uint,
pub ReparseDataLength: libc::c_ushort,
pub Reserved: libc::c_ushort,
pub rest: (),
}
#[cfg(windows)]
pub fn cvt(result: i32) -> std::io::Result<usize> {
if result < 0 {
Err(std::io::Error::from_raw_os_error(-result))
} else {
Ok(result as usize)
}
}