#![forbid(unsafe_op_in_unsafe_fn)]
pub mod alloc;
pub mod node;
pub mod value;
mod utils;
pub use {alloc::LibCBox, sys::VERSION};
pub use hivex_sys as sys;
use {
node::{NodeHandle, SelectedNode},
std::{
ffi::{CStr, CString},
io::Write,
marker::PhantomData,
mem::ManuallyDrop,
ops::Deref,
path::Path,
},
time::OffsetDateTime,
utils::{check_pointer_null, check_status_zero, wrap_handle},
value::{SelectedValue, ValueHandle},
};
pub static EMPTY_HIVE_TEMPLATE: &[u8] = include_bytes!("../EmptyHive.dat");
bitflags::bitflags! {
#[derive(Clone, Copy, Default, PartialEq, Eq)]
pub struct OpenFlags: std::ffi::c_int {
const VERBOSE = sys::HIVEX_OPEN_VERBOSE as _;
const DEBUG = sys::HIVEX_OPEN_DEBUG as _;
const WRITE = sys::HIVEX_OPEN_WRITE as _;
const UNSAFE = sys::HIVEX_OPEN_UNSAFE as _;
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct CommitFlags: std::ffi::c_int {}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct SetValueFlags: std::ffi::c_int {}
}
#[repr(transparent)]
pub struct Hive(*mut sys::hive_h);
unsafe impl Send for Hive {}
impl Hive {
pub fn create(path: impl AsRef<Path>, flags: OpenFlags) -> std::io::Result<Self> {
{
let mut file = std::fs::File::options()
.create_new(true)
.write(true)
.open(&path)?;
file.write_all(EMPTY_HIVE_TEMPLATE)?;
}
Self::open(path, flags)
}
pub fn open(path: impl AsRef<Path>, flags: OpenFlags) -> std::io::Result<Self> {
let path_c_str = CString::new(
path.as_ref()
.to_owned()
.into_os_string()
.into_encoded_bytes(),
)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
let handle = unsafe { sys::hivex_open(path_c_str.as_ptr().cast(), flags.bits()) };
check_pointer_null(handle)?;
Ok(Self(handle))
}
pub fn close(self) -> std::io::Result<()> {
let status = unsafe { sys::hivex_close(self.as_handle()) };
let result = check_status_zero(status);
std::mem::forget(self);
result
}
pub fn root(&self) -> std::io::Result<NodeHandle> {
let node = unsafe { sys::hivex_root(self.as_handle()) };
wrap_handle(node, NodeHandle)
}
pub fn last_modified(&self) -> OffsetDateTime {
let raw_wintime = unsafe { sys::hivex_last_modified(self.as_handle()) };
win_filetime_to_offset_datetime(raw_wintime)
}
pub fn commit(&self, filename: Option<&CStr>, flags: CommitFlags) -> std::io::Result<()> {
let filename = match filename {
Some(cstr) => cstr.as_ptr(),
None => std::ptr::null(),
};
let status = unsafe { sys::hivex_commit(self.as_handle(), filename, flags.bits()) };
check_status_zero(status)
}
pub const fn node(&self, node: NodeHandle) -> SelectedNode<'_> {
SelectedNode {
hive: self.borrow(),
handle: node,
}
}
pub const fn value(&self, value: ValueHandle) -> SelectedValue<'_> {
SelectedValue {
hive: self.borrow(),
handle: value,
}
}
pub const fn as_handle(&self) -> *mut sys::hive_h {
self.0
}
pub const unsafe fn from_handle(handle: *mut sys::hive_h) -> Self {
Self(handle)
}
pub const fn borrow(&self) -> BorrowedHive<'_> {
let self_copy = unsafe { ManuallyDrop::new(Self::from_handle(self.as_handle())) };
BorrowedHive {
hive: self_copy,
_lifetime: PhantomData,
}
}
}
impl Drop for Hive {
fn drop(&mut self) {
unsafe {
let _ = sys::hivex_close(self.as_handle());
}
}
}
#[repr(transparent)]
pub struct BorrowedHive<'a> {
pub hive: ManuallyDrop<Hive>,
_lifetime: PhantomData<&'a Hive>,
}
impl Clone for BorrowedHive<'_> {
fn clone(&self) -> Self {
Self {
hive: ManuallyDrop::new(Hive(self.hive.0)),
_lifetime: PhantomData,
}
}
}
impl Deref for BorrowedHive<'_> {
type Target = Hive;
fn deref(&self) -> &Self::Target {
&self.hive
}
}
fn win_filetime_to_offset_datetime(win_time: i64) -> OffsetDateTime {
let epoch_start = time::macros::datetime!(1601-01-01 0:00 UTC);
let duration = time::Duration::milliseconds(win_time / 10);
epoch_start
.checked_add(duration)
.expect("This should be a valid date")
}