use {
super::{CompressionType, Image, ImageIndex, Wim},
crate::{error::result_from_raw, string::TStr, sys, Error},
std::{
ffi,
fmt::Debug,
marker::PhantomData,
mem::MaybeUninit,
num::NonZero,
ptr::{null_mut, NonNull},
rc::Rc,
},
time::OffsetDateTime,
uuid::Uuid,
widestring::U16Str,
};
impl Wim {
#[doc(alias = "wimlib_get_wim_info")]
pub fn info(&self) -> WimInfo {
let raw_info = unsafe {
let mut raw_info = MaybeUninit::uninit();
sys::wimlib_get_wim_info(self.wimstruct, raw_info.as_mut_ptr());
raw_info.assume_init()
};
WimInfo::from_raw(raw_info)
}
#[doc(alias = "wimlib_get_xml_data")]
pub fn xml_data(&self) -> Result<&U16Str, Error> {
let mut out_buf_data = null_mut();
let mut out_buf_len = 0;
result_from_raw(unsafe {
sys::wimlib_get_xml_data(self.wimstruct, &mut out_buf_data, &mut out_buf_len)
})?;
let len_u16 = out_buf_len / 2;
let widestr = unsafe { U16Str::from_ptr(out_buf_data.cast(), len_u16) };
Ok(widestr)
}
#[doc(alias = "wimlib_image_name_in_use")]
pub fn image_name_in_use(&self, name: &TStr) -> bool {
unsafe { sys::wimlib_image_name_in_use(self.wimstruct, name.as_ptr()) }
}
#[doc(alias = "wimlib_iterate_lookup_table")]
pub fn iterate_lookup_table(
&self,
flags: IterateLookupTableFlags,
mut callback: impl FnMut(ResourceEntry) -> Result<(), Error>,
) -> Result<(), Error> {
fn inner(
self_: &Wim,
flags: IterateLookupTableFlags,
callback: &mut IterateLookupTableCallback,
) -> Result<(), Error> {
let callback_thin_ptr: *mut *mut IterateLookupTableCallback =
&mut (callback as *mut _) as *mut _;
result_from_raw(unsafe {
sys::wimlib_iterate_lookup_table(
self_.wimstruct,
flags.bits(),
Some(iterate_lookup_table_trampoline),
callback_thin_ptr.cast(),
)
})
}
inner(self, flags, &mut callback)
}
#[doc(alias = "wimlib_print_header")]
pub fn print_header(&self) {
unsafe { sys::wimlib_print_header(self.wimstruct) }
}
#[doc(alias = "wimlib_resolve_image")]
pub fn resolve_image(&self, image_name_or_num: &TStr) -> Option<ImageIndex> {
let value =
unsafe { sys::wimlib_resolve_image(self.wimstruct, image_name_or_num.as_ptr()) };
NonZero::new(value as u32)
}
}
impl<'a> Image<'a> {
pub fn property(&self, property_name: &TStr) -> Option<&'a TStr> {
unsafe {
let ptr = sys::wimlib_get_image_property(
self.wimstruct,
self.ffi_index(),
property_name.as_ptr(),
);
TStr::from_ptr_optional(ptr)
}
}
#[doc(alias = "wimlib_iterate_dir_tree")]
pub fn iterate_dir_tree(
&self,
path: &TStr,
flags: IterateDirTreeFlags,
mut callback: impl FnMut(DirEntry) -> Result<(), Error>,
) -> Result<(), Error> {
fn inner(
self_: &Image<'_>,
path: &TStr,
flags: IterateDirTreeFlags,
callback: &mut IterateDirTreeCallback<'_>,
) -> Result<(), Error> {
let callback_thin_ptr: *mut *mut IterateDirTreeCallback<'_> =
&mut (callback as *mut _) as *mut _;
result_from_raw(unsafe {
sys::wimlib_iterate_dir_tree(
self_.wimstruct,
self_.ffi_index(),
path.as_ptr(),
flags.bits(),
Some(iterate_dir_tree_trampoline),
callback_thin_ptr.cast(),
)
})
}
inner(self, path, flags, &mut callback)
}
}
macro_rules! gen_bitfield_bool_getters {
{
field: $field:ident;
$(
$(#[$meta:meta])*
$name:ident : $index:expr
),* $(,)?
} => {
$(
$(#[$meta])*
pub fn $name(&self) -> bool {
self.$field.get_bit($index)
}
)*
};
}
#[doc(alias = "wimlib_wim_info")]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct WimInfo {
pub guid: Uuid,
pub image_count: u32,
pub boot_index: u32,
pub wim_version: u32,
pub chunk_size: u32,
pub part_number: u16,
pub total_parts: u16,
pub compression_type: CompressionType,
pub total_bytes: u64,
bitfield_attrs: sys::__BindgenBitfieldUnit<[u8; 4]>,
}
impl Debug for WimInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WimInfo")
.field("guid", &self.guid)
.field("image_count", &self.image_count)
.field("boot_index", &self.boot_index)
.field("wim_version", &self.wim_version)
.field("chunk_size", &self.chunk_size)
.field("part_number", &self.part_number)
.field("total_parts", &self.total_parts)
.field("compression_type", &self.compression_type)
.field("total_bytes", &self.total_bytes)
.field("has_integrity_table", &self.has_integrity_table())
.field("opened_from_file", &self.opened_from_file())
.field("is_readonly", &self.is_readonly())
.field("has_rpfiex", &self.has_rpfix())
.field("is_marked_readonly", &self.is_marked_readonly())
.field("spanned", &self.spanned())
.field("write_in_progress", &self.write_in_progress())
.field("metadata_only", &self.metadata_only())
.field("resource_only", &self.resource_only())
.field("pipable", &self.pipable())
.finish()
}
}
impl WimInfo {
pub fn from_raw(ffi: sys::wimlib_wim_info) -> Self {
Self {
guid: Uuid::from_bytes(ffi.guid),
image_count: ffi.image_count,
boot_index: ffi.boot_index,
wim_version: ffi.wim_version,
chunk_size: ffi.chunk_size,
part_number: ffi.part_number,
total_parts: ffi.total_parts,
compression_type: CompressionType::from_raw(ffi.compression_type),
total_bytes: ffi.total_bytes,
bitfield_attrs: ffi._bitfield_1,
}
}
gen_bitfield_bool_getters! {
field: bitfield_attrs;
has_integrity_table : 0,
opened_from_file : 1,
is_readonly : 2,
has_rpfix : 3,
is_marked_readonly : 4,
spanned : 5,
write_in_progress : 6,
metadata_only : 7,
resource_only : 8,
pipable : 9,
}
}
#[doc(alias = "wimlib_resource_entry")]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct ResourceEntry {
pub uncompressed_size: u64,
pub compressed_size: u64,
pub offset: u64,
pub sha1_hash: [u8; 20],
pub part_number: u32,
pub reference_count: u32,
bitfield_attrs: sys::__BindgenBitfieldUnit<[u8; 4]>,
pub raw_resource_offset_in_wim: u64,
pub raw_resource_compressed_size: u64,
pub raw_resource_uncompressed_size: u64,
}
impl Debug for ResourceEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ResourceEntry")
.field("uncompressed_size", &self.uncompressed_size)
.field("compressed_size", &self.compressed_size)
.field("offset", &self.offset)
.field("sha1_hash", &self.sha1_hash)
.field("part_number", &self.part_number)
.field("reference_count", &self.reference_count)
.field(
"raw_resource_offset_in_wim",
&self.raw_resource_offset_in_wim,
)
.field(
"raw_resource_compressed_size",
&self.raw_resource_compressed_size,
)
.field(
"raw_resource_uncompressed_size",
&self.raw_resource_uncompressed_size,
)
.field("is_compressed", &self.is_compressed())
.field("is_metadata", &self.is_metadata())
.field("is_free", &self.is_free())
.field("is_spanned", &self.is_spanned())
.field("is_missing", &self.is_missing())
.field("packed", &self.packed())
.finish()
}
}
impl ResourceEntry {
pub fn from_raw(ffi: sys::wimlib_resource_entry) -> Self {
Self {
uncompressed_size: ffi.uncompressed_size,
compressed_size: ffi.compressed_size,
offset: ffi.offset,
sha1_hash: ffi.sha1_hash,
part_number: ffi.part_number,
reference_count: ffi.reference_count,
bitfield_attrs: ffi._bitfield_1,
raw_resource_offset_in_wim: ffi.raw_resource_offset_in_wim,
raw_resource_compressed_size: ffi.raw_resource_compressed_size,
raw_resource_uncompressed_size: ffi.raw_resource_uncompressed_size,
}
}
gen_bitfield_bool_getters! {
field: bitfield_attrs;
is_compressed: 0,
is_metadata: 1,
is_free: 2,
is_spanned: 3,
is_missing: 4,
packed: 5,
}
}
#[doc(alias = "wimlib_stream_entry")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StreamEntry<'a> {
pub stream_name: Option<&'a TStr>,
pub resource: ResourceEntry,
}
impl StreamEntry<'_> {
pub fn from_raw(ffi: sys::wimlib_stream_entry) -> Self {
let stream_name =
(!ffi.stream_name.is_null()).then(|| unsafe { TStr::from_ptr(ffi.stream_name) });
Self {
stream_name,
resource: ResourceEntry::from_raw(ffi.resource),
}
}
}
#[doc(alias = "wimlib_stream_entry")]
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct LazyStreamEntry<'a> {
raw: sys::wimlib_stream_entry,
_lt: PhantomData<StreamEntry<'a>>,
}
impl<'a> LazyStreamEntry<'a> {
pub fn into_stream_entry(self) -> StreamEntry<'a> {
StreamEntry::from_raw(self.raw)
}
}
impl Debug for LazyStreamEntry<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LazyStreamEntry").finish_non_exhaustive()
}
}
impl<'a> From<LazyStreamEntry<'a>> for StreamEntry<'a> {
fn from(value: LazyStreamEntry<'a>) -> Self {
value.into_stream_entry()
}
}
#[doc(alias = "wimlib_object_id")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ObjectId {
pub object_id: Uuid,
pub birth_volume_id: Uuid,
pub birth_object_id: Uuid,
pub domain_id: Uuid,
}
impl ObjectId {
pub fn from_raw(ffi: sys::wimlib_object_id) -> Self {
Self {
object_id: Uuid::from_bytes(ffi.object_id),
birth_volume_id: Uuid::from_bytes(ffi.birth_volume_id),
birth_object_id: Uuid::from_bytes(ffi.birth_object_id),
domain_id: Uuid::from_bytes(ffi.domain_id),
}
}
}
#[doc(alias = "wimlib_dir_entry")]
#[derive(Clone, Debug)]
pub struct DirEntry<'a> {
pub filename: Option<&'a TStr>,
pub short_name: Option<&'a TStr>,
pub full_path: &'a TStr,
pub depth: usize,
pub security_descriptor: Option<win32_security_descriptor::SECURITY_DESCRIPTOR_RELATIVE>,
pub file_attributes: FileAttributes,
pub reparse_tag: ReparseTag,
pub num_links: u32,
pub num_named_streams: u32,
pub hard_link_group_id: u64,
pub creation_time: OffsetDateTime,
pub last_write_time: OffsetDateTime,
pub last_access_time: OffsetDateTime,
pub unix_uid: u32,
pub unix_gid: u32,
pub unix_mode: u32,
pub unix_rdev: u32,
pub object_id: ObjectId,
pub streams: &'a [LazyStreamEntry<'a>],
}
impl<'a> DirEntry<'a> {
pub fn streams_converted(&self) -> Rc<[StreamEntry<'a>]> {
self.streams
.iter()
.map(|entry| entry.into_stream_entry())
.collect()
}
pub unsafe fn from_raw(ffi: *const sys::wimlib_dir_entry) -> Self {
unsafe {
let file_attributes =
FileAttributes::from_bits((*ffi).attributes).expect("Unsupported file attributes");
let num_named_streams = (*ffi).num_named_streams;
let streams = {
let streams_len = num_named_streams as usize + 1;
let ptr = (*ffi).streams.as_ptr();
std::slice::from_raw_parts(ptr.cast(), streams_len)
};
let creation_time = convert_timedate((*ffi).creation_time, (*ffi).creation_time_high);
let last_write_time =
convert_timedate((*ffi).last_write_time, (*ffi).last_write_time_high);
let last_access_time =
convert_timedate((*ffi).last_access_time, (*ffi).last_access_time_high);
let security_descriptor = NonNull::new(
(*ffi)
.security_descriptor
.cast::<win32_security_descriptor::SECURITY_DESCRIPTOR_RELATIVE>()
.cast_mut(),
)
.map(|ptr| ptr.read());
Self {
filename: TStr::from_ptr_optional((*ffi).filename),
short_name: TStr::from_ptr_optional((*ffi).dos_name),
full_path: TStr::from_ptr((*ffi).full_path),
depth: (*ffi).depth,
security_descriptor,
file_attributes,
reparse_tag: std::mem::transmute::<u32, ReparseTag>((*ffi).reparse_tag),
num_links: (*ffi).num_links,
num_named_streams,
hard_link_group_id: (*ffi).hard_link_group_id,
creation_time,
last_write_time,
last_access_time,
unix_uid: (*ffi).unix_uid,
unix_gid: (*ffi).unix_gid,
unix_mode: (*ffi).unix_mode,
unix_rdev: (*ffi).unix_rdev,
object_id: ObjectId::from_raw((*ffi).object_id),
streams,
}
}
}
}
bitflags::bitflags! {
#[doc(alias = "WIMLIB_FILE_ATTRIBUTE")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FileAttributes: std::ffi::c_uint {
const READONLY = sys::WIMLIB_FILE_ATTRIBUTE_READONLY;
const HIDDEN = sys::WIMLIB_FILE_ATTRIBUTE_HIDDEN;
const SYSTEM = sys::WIMLIB_FILE_ATTRIBUTE_SYSTEM;
const DIRECTORY = sys::WIMLIB_FILE_ATTRIBUTE_DIRECTORY;
const ARCHIVE = sys::WIMLIB_FILE_ATTRIBUTE_ARCHIVE;
const DEVICE = sys::WIMLIB_FILE_ATTRIBUTE_DEVICE;
const NORMAL = sys::WIMLIB_FILE_ATTRIBUTE_NORMAL;
const TEMPORARY = sys::WIMLIB_FILE_ATTRIBUTE_TEMPORARY;
const SPARSE_FILE = sys::WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE;
const REPARSE_POINT = sys::WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT;
const COMPRESSED = sys::WIMLIB_FILE_ATTRIBUTE_COMPRESSED;
const OFFLINE = sys::WIMLIB_FILE_ATTRIBUTE_OFFLINE;
const NOT_CONTENT_INDEXED = sys::WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
const ENCRYPTED = sys::WIMLIB_FILE_ATTRIBUTE_ENCRYPTED;
const VIRTUAL = sys::WIMLIB_FILE_ATTRIBUTE_VIRTUAL;
}
}
#[doc(alias = "WIMLIB_REPARSE_TAG")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
#[non_exhaustive]
pub enum ReparseTag {
Reserved0 = sys::WIMLIB_REPARSE_TAG_RESERVED_ZERO,
Reserved1 = sys::WIMLIB_REPARSE_TAG_RESERVED_ONE,
MountPoint = sys::WIMLIB_REPARSE_TAG_MOUNT_POINT,
Hsm = sys::WIMLIB_REPARSE_TAG_HSM,
Hsm2 = sys::WIMLIB_REPARSE_TAG_HSM2,
DriverExtender = sys::WIMLIB_REPARSE_TAG_DRIVER_EXTENDER,
Sis = sys::WIMLIB_REPARSE_TAG_SIS,
Dfs = sys::WIMLIB_REPARSE_TAG_DFS,
Dfsr = sys::WIMLIB_REPARSE_TAG_DFSR,
FilterManager = sys::WIMLIB_REPARSE_TAG_FILTER_MANAGER,
Wof = sys::WIMLIB_REPARSE_TAG_WOF,
Symlink = sys::WIMLIB_REPARSE_TAG_SYMLINK,
}
fn convert_timedate(timespec: sys::timespec, high_secs: i32) -> OffsetDateTime {
let seconds = if std::mem::size_of_val(×pec.tv_sec) == std::mem::size_of::<i32>() {
let high_part = (high_secs as i64) << 32;
#[allow(clippy::unnecessary_cast)]
let low_part = timespec.tv_sec as i64;
high_part | low_part
} else {
timespec.tv_sec
};
let nanoseconds = timespec.tv_nsec;
let duration = time::Duration::new(seconds, nanoseconds as i32);
OffsetDateTime::UNIX_EPOCH.saturating_add(duration)
}
#[doc(alias = "wimlib_iterate_dir_tree_callback_t")]
type IterateDirTreeCallback<'a> = dyn FnMut(DirEntry) -> Result<(), Error> + 'a;
unsafe extern "C" fn iterate_dir_tree_trampoline(
dentry: *const sys::wimlib_dir_entry,
user_ctx: *mut ffi::c_void,
) -> ffi::c_int {
unsafe {
let dir_entry = DirEntry::from_raw(dentry);
let callback_ptr = user_ctx as *mut *mut IterateDirTreeCallback;
let result = (**callback_ptr)(dir_entry);
match result {
Ok(()) => 0,
Err(e) => e as _,
}
}
}
bitflags::bitflags! {
pub struct IterateDirTreeFlags: ffi::c_int {
const RECURSIVE = sys::WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE as _;
const CHILDREN = sys::WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN as _;
const RESOURCES_NEEDED = sys::WIMLIB_ITERATE_DIR_TREE_FLAG_RESOURCES_NEEDED as _;
}
}
#[doc(alias = "wimlib_iterate_lookup_table_callback_t")]
type IterateLookupTableCallback<'a> = dyn FnMut(ResourceEntry) -> Result<(), Error> + 'a;
unsafe extern "C" fn iterate_lookup_table_trampoline(
resource: *const sys::wimlib_resource_entry,
user_ctx: *mut ffi::c_void,
) -> ffi::c_int {
unsafe {
let resource_entry = ResourceEntry::from_raw(*resource);
let callback_ptr = user_ctx as *mut *mut IterateLookupTableCallback;
let result = (**callback_ptr)(resource_entry);
match result {
Ok(()) => 0,
Err(e) => e as _,
}
}
}
bitflags::bitflags! {
pub struct IterateLookupTableFlags: ffi::c_int {}
}
#[cfg(not(windows))]
pub mod win32_security_descriptor {
#![allow(non_camel_case_types, non_snake_case, missing_docs)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[repr(C)]
pub struct SECURITY_DESCRIPTOR_RELATIVE {
pub Revision: u8,
pub Sbz1: u8,
pub Control: SECURITY_DESCRIPTOR_CONTROL,
pub Owner: u32,
pub Group: u32,
pub Sacl: u32,
pub Dacl: u32,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[repr(transparent)]
pub struct SECURITY_DESCRIPTOR_CONTROL(pub u16);
}
#[cfg(windows)]
pub mod win32_security_descriptor {
pub use windows::Win32::Security::{SECURITY_DESCRIPTOR_CONTROL, SECURITY_DESCRIPTOR_RELATIVE};
}