use std::io;
use std::path::Path;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Identity {
pub(super) filesystem: [i32; 2],
pub(super) kind: i32,
pub(super) bytes: Vec<u8>,
}
impl Identity {
#[cfg(target_os = "linux")]
pub fn of(target: &Path) -> io::Result<Self> {
use std::os::fd::{AsRawFd as _, FromRawFd as _, OwnedFd};
use nix::fcntl::{OFlag, open};
use nix::sys::stat::Mode;
#[repr(C)]
struct Handle {
header: libc::file_handle,
bytes: [u8; libc::MAX_HANDLE_SZ as usize],
}
let mut handle = Handle {
header: libc::file_handle {
handle_bytes: libc::MAX_HANDLE_SZ as u32,
handle_type: 0,
f_handle: [],
},
bytes: [0; libc::MAX_HANDLE_SZ as usize],
};
let fd = open(
target,
OFlag::O_PATH | OFlag::O_NOFOLLOW | OFlag::O_CLOEXEC,
Mode::empty(),
)?;
let file = unsafe { OwnedFd::from_raw_fd(fd) };
let mut mount_id = 0;
let result = unsafe {
libc::name_to_handle_at(
file.as_raw_fd(),
c"".as_ptr(),
&raw mut handle.header,
&raw mut mount_id,
libc::AT_HANDLE_FID | libc::AT_EMPTY_PATH,
)
};
if result != 0 {
return Err(io::Error::last_os_error());
}
let fsid = nix::sys::statfs::fstatfs(&file)?.filesystem_id();
let filesystem = unsafe { std::mem::transmute::<libc::fsid_t, [i32; 2]>(fsid) };
Ok(Self {
filesystem,
kind: handle.header.handle_type,
bytes: handle.bytes[..handle.header.handle_bytes as usize].to_vec(),
})
}
#[cfg(all(test, target_os = "macos"))]
pub fn of(target: &Path) -> io::Result<Self> {
use std::os::unix::fs::MetadataExt as _;
use std::time::UNIX_EPOCH;
let meta = std::fs::symlink_metadata(target)?;
let (secs, nanos) = match meta.created()?.duration_since(UNIX_EPOCH) {
Ok(after) => (after.as_secs().cast_signed(), after.subsec_nanos()),
Err(before) => (
-before.duration().as_secs().cast_signed(),
before.duration().subsec_nanos(),
),
};
let mut bytes = meta.ino().to_le_bytes().to_vec();
bytes.extend_from_slice(&secs.to_le_bytes());
bytes.extend_from_slice(&nanos.to_le_bytes());
Ok(Self {
filesystem: [0; 2],
kind: 0,
bytes,
})
}
}