use {
crate::{
sys,
utils::{
boxed_slice_from_ffi, boxed_str_from_ffi, check_pointer_null, check_status_zero,
last_os_error, wrap_handle,
},
value::{Value, ValueHandle, ValueString, ValueType},
BorrowedHive, LibCBox, SetValueFlags,
},
std::{ffi::CStr, mem::size_of},
time::OffsetDateTime,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct NodeHandle(pub(crate) sys::hive_node_h);
#[must_use]
pub struct SelectedNode<'hive> {
pub(crate) hive: BorrowedHive<'hive>,
pub(crate) handle: NodeHandle,
}
impl<'hive> SelectedNode<'hive> {
pub fn hive(&self) -> BorrowedHive<'hive> {
self.hive.clone()
}
pub const fn handle(&self) -> NodeHandle {
self.handle
}
pub fn name(&self) -> std::io::Result<LibCBox<str>> {
unsafe {
let raw_ptr = self.hive.as_handle();
let data = sys::hivex_node_name(raw_ptr, self.handle.0);
check_pointer_null(data)?;
let len = sys::hivex_node_name_len(raw_ptr, self.handle.0);
Ok(boxed_str_from_ffi(data, len))
}
}
pub fn timestamp(&self) -> OffsetDateTime {
let raw = unsafe { sys::hivex_node_timestamp(self.hive.as_handle(), self.handle.0) };
crate::win_filetime_to_offset_datetime(raw)
}
pub fn children(&self) -> LibCBox<[NodeHandle]> {
unsafe {
let raw_ptr = self.hive.as_handle();
let data = sys::hivex_node_children(raw_ptr, self.handle.0).cast();
let len = sys::hivex_node_nr_children(raw_ptr, self.handle.0);
boxed_slice_from_ffi(data, len)
}
}
pub fn get_child(&self, name: &str) -> Option<NodeHandle> {
let searched_name = name.trim_end_matches('\0');
let children = self.children();
children
.iter()
.find(|&&child| {
let child_name = self
.hive
.node(child)
.name()
.expect("The node was in child list, the name should exist");
&*child_name == searched_name
})
.copied()
}
pub fn parent(&self) -> std::io::Result<NodeHandle> {
let parent = unsafe { sys::hivex_node_parent(self.hive.as_handle(), self.handle.0) };
wrap_handle(parent, NodeHandle)
}
pub fn values(&self) -> LibCBox<[ValueHandle]> {
unsafe {
let raw_ptr = self.hive.as_handle();
let data = sys::hivex_node_values(raw_ptr, self.handle.0).cast();
let len = sys::hivex_node_nr_values(raw_ptr, self.handle.0);
boxed_slice_from_ffi(data, len)
}
}
pub fn get_value(&self, key: impl ValueString) -> std::io::Result<ValueHandle> {
let c_str_key = key.into_c_string();
let handle = unsafe {
sys::hivex_node_get_value(self.hive.as_handle(), self.handle.0, c_str_key.as_ptr())
};
wrap_handle(handle, ValueHandle)
}
pub fn node_add_child(&self, name: impl ValueString) -> std::io::Result<NodeHandle> {
let c_name = name.into_c_string();
let result = unsafe {
sys::hivex_node_add_child(self.hive.as_handle(), self.handle.0, c_name.as_ptr())
};
if result != 0 {
Ok(NodeHandle(result))
} else {
Err(last_os_error())
}
}
pub fn delete(self) -> std::io::Result<()> {
let status = unsafe { sys::hivex_node_delete_child(self.hive.as_handle(), self.handle.0) };
check_status_zero(status)
}
pub fn set_value<Str: ValueString>(
&self,
flags: SetValueFlags,
key: impl ValueString,
value: Value<Str>,
) -> std::io::Result<()> {
let c_key = key.into_c_string();
let ty = value.type_of();
let value = std::pin::pin!(value);
let (data, len) = match &*value {
Value::None => (std::ptr::null(), 0),
Value::Binary(x)
| Value::ResourceList(x)
| Value::FullResourceDescriptor(x)
| Value::ResourceRequirementsList(x) => ((*x).as_ptr(), x.len()),
Value::Dword(x) => ((&raw const *x).cast(), size_of::<u32>()),
Value::DwordBe(x) => ((&raw const *x).cast(), size_of::<u32>()),
Value::Qword(x) => ((&raw const *x).cast(), size_of::<u64>()),
Value::Sz(x) | Value::ExpandSz(x) | Value::Link(x) => {
return self.set_value_string(flags, &*c_key, ty, x);
}
Value::MultiSz(x) => return self.set_value_multistring(flags, &c_key, ty, x),
};
let set_value = sys::hive_set_value {
key: c_key.as_ptr().cast_mut().cast(),
t: ty as u32,
len,
value: data.cast_mut().cast(),
};
let status = unsafe {
sys::hivex_node_set_value(
self.hive.as_handle(),
self.handle.0,
&set_value,
flags.bits(),
)
};
check_status_zero(status)
}
pub fn set_value_string(
&self,
flags: SetValueFlags,
key: impl ValueString,
ty: ValueType,
bytes: &impl ValueString,
) -> std::io::Result<()> {
debug_assert!(
matches!(ty, ValueType::Sz | ValueType::ExpandSz | ValueType::Link),
"Provided type should be string-like",
);
let c_key = key.into_c_string();
let mut utf16 = bytes.to_utf16().map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
"The string is not valid Unicode",
)
})?;
let set_value = sys::hive_set_value {
key: c_key.as_ptr().cast_mut().cast(),
t: ty as u32,
len: utf16.len() * size_of::<u16>(),
value: utf16.as_mut_ptr().cast(),
};
let status = unsafe {
sys::hivex_node_set_value(
self.hive.as_handle(),
self.handle.0,
&set_value,
flags.bits(),
)
};
check_status_zero(status)
}
fn set_value_multistring(
&self,
flags: SetValueFlags,
key: &CStr,
ty: ValueType,
data: &[impl ValueString],
) -> std::io::Result<()> {
let mut utf16_data = vec![];
for string in data {
utf16_data.extend_from_slice(&string.to_utf16().unwrap());
if utf16_data.last() != Some(&0) {
utf16_data.push(0);
}
}
let len = utf16_data.len() * size_of::<u16>();
let set_value = sys::hive_set_value {
key: key.as_ptr().cast_mut().cast(),
t: ty as u32,
len,
value: utf16_data.as_ptr().cast_mut().cast(),
};
let status = unsafe {
sys::hivex_node_set_value(
self.hive.as_handle(),
self.handle.0,
&set_value,
flags.bits(),
)
};
check_status_zero(status)
}
}