use std::io;
use std::path::Path;
pub fn is_same_file<P, Q>(
path1: P,
path2: Q,
) -> io::Result<bool> where P: AsRef<Path>, Q: AsRef<Path> {
impl_is_same_file(path1, path2)
}
#[cfg(unix)]
fn impl_is_same_file<P, Q>(
p1: P,
p2: Q,
) -> io::Result<bool>
where P: AsRef<Path>, Q: AsRef<Path> {
use std::fs;
use std::os::unix::fs::MetadataExt;
let md1 = try!(fs::metadata(p1));
let md2 = try!(fs::metadata(p2));
Ok((md1.dev(), md1.ino()) == (md2.dev(), md2.ino()))
}
#[cfg(windows)]
fn impl_is_same_file<P, Q>(
p1: P,
p2: Q,
) -> io::Result<bool>
where P: AsRef<Path>, Q: AsRef<Path> {
use std::ops::{Deref, Drop};
use std::os::windows::prelude::*;
use std::ptr;
use kernel32;
use winapi::{self, HANDLE};
struct Handle(HANDLE);
impl Drop for Handle {
fn drop(&mut self) {
unsafe { let _ = kernel32::CloseHandle(self.0); }
}
}
impl Deref for Handle {
type Target = HANDLE;
fn deref(&self) -> &HANDLE { &self.0 }
}
#[repr(C)]
#[allow(non_snake_case)]
struct BY_HANDLE_FILE_INFORMATION {
dwFileAttributes: winapi::DWORD,
ftCreationTime: winapi::FILETIME,
ftLastAccessTime: winapi::FILETIME,
ftLastWriteTime: winapi::FILETIME,
dwVolumeSerialNumber: winapi::DWORD,
nFileSizeHigh: winapi::DWORD,
nFileSizeLow: winapi::DWORD,
nNumberOfLinks: winapi::DWORD,
nFileIndexHigh: winapi::DWORD,
nFileIndexLow: winapi::DWORD,
}
#[allow(non_camel_case_types)]
type LPBY_HANDLE_FILE_INFORMATION = *mut BY_HANDLE_FILE_INFORMATION;
fn file_info(h: &Handle) -> io::Result<BY_HANDLE_FILE_INFORMATION> {
#[link(name = "ws2_32")]
#[link(name = "userenv")]
extern "system" {
fn GetFileInformationByHandle(
hFile: HANDLE,
lpFileInformation: LPBY_HANDLE_FILE_INFORMATION,
) -> winapi::BOOL;
}
unsafe {
let mut info: BY_HANDLE_FILE_INFORMATION = ::std::mem::zeroed();
if GetFileInformationByHandle(**h, &mut info) == 0 {
Err(io::Error::last_os_error())
} else {
Ok(info)
}
}
}
fn open_read_attr<P: AsRef<Path>>(p: P) -> io::Result<Handle> {
let h = unsafe {
kernel32::CreateFileW(
to_utf16(p.as_ref()).as_ptr(),
0,
winapi::FILE_SHARE_READ
| winapi::FILE_SHARE_WRITE
| winapi::FILE_SHARE_DELETE,
ptr::null_mut(),
winapi::OPEN_EXISTING,
winapi::FILE_FLAG_BACKUP_SEMANTICS,
ptr::null_mut())
};
if h == winapi::INVALID_HANDLE_VALUE {
Err(io::Error::last_os_error())
} else {
Ok(Handle(h))
}
}
fn to_utf16(s: &Path) -> Vec<u16> {
s.as_os_str().encode_wide().chain(Some(0)).collect()
}
let h1 = try!(open_read_attr(&p1));
let h2 = try!(open_read_attr(&p2));
let i1 = try!(file_info(&h1));
let i2 = try!(file_info(&h2));
let k1 = (
i1.dwVolumeSerialNumber,
i1.nFileIndexHigh, i1.nFileIndexLow,
i1.nFileSizeHigh, i1.nFileSizeLow,
);
let k2 = (
i2.dwVolumeSerialNumber,
i2.nFileIndexHigh, i2.nFileIndexLow,
i2.nFileSizeHigh, i2.nFileSizeLow,
);
Ok(k1 == k2)
}