use crate::c_includes;
pub use c_includes::UDF_BLOCK_SIZE;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::os::raw::{c_int, c_void};
use std::path::Path;
#[repr(C)]
struct FileReader {
funcs: c_includes::udfread_block_input,
file: File,
}
impl FileReader {
fn new(path: impl AsRef<Path>) -> std::io::Result<*mut c_includes::udfread_block_input> {
let file = File::open(path)?;
let funcs = c_includes::udfread_block_input {
size: Some(Self::c_size_in_blocks),
read: Some(Self::c_read_blocks),
close: Some(Self::c_free_reader),
};
Ok(Box::into_raw(Box::<Self>::new(Self { funcs, file }))
as *mut c_includes::udfread_block_input)
}
unsafe extern "C" fn c_size_in_blocks(ptr: *mut c_includes::udfread_block_input) -> u32 {
(&mut *(ptr as *mut Self)).size_in_blocks()
}
fn size_in_blocks(&mut self) -> u32 {
self.file
.seek(SeekFrom::End(0))
.map(|val| val / u64::from(UDF_BLOCK_SIZE))
.unwrap_or_else(|err| {
eprintln!("Error getting file size: {}", err);
0
})
.try_into()
.unwrap_or(0)
}
unsafe extern "C" fn c_read_blocks(
ptr: *mut c_includes::udfread_block_input,
block_offset: u32,
buf: *mut c_void,
num_blocks: u32,
_flags: c_int,
) -> c_int {
(&mut *(ptr as *mut Self)).read_blocks(block_offset, buf as *mut u8, num_blocks)
}
fn read_blocks(&mut self, block_offset: u32, buf: *mut u8, num_blocks: u32) -> c_int {
// if num_blocks > c_int::MAX as u32 || num_blocks > usize::MAX / UDF_BLOCK_SIZE {
// eprintln!("Too many blocks requested.");
// return -1;
// }
let result: std::io::Result<usize> = (|| {
let byte_offset = u64::from(block_offset) * u64::from(UDF_BLOCK_SIZE);
// TODO: Ensure this doesn't overflow
let num_bytes = num_blocks as usize * UDF_BLOCK_SIZE as usize;
// TODO: Use ReadBuf once it is stable to avoid needing to zero the buffer.
unsafe { std::ptr::write_bytes(buf, 0, num_bytes) };
let mut buffer = unsafe { std::slice::from_raw_parts_mut(buf, num_bytes) };
let mut total_bytes_read = 0usize;
self.file.seek(SeekFrom::Start(byte_offset))?;
loop {
let bytes_read = self.file.read(buffer)?;
if bytes_read == 0 {
break;
} else if bytes_read < buffer.len() {
total_bytes_read += bytes_read;
buffer = &mut buffer[bytes_read..];
} else {
// A broken Read implementation could hypothetically return bytes_read >
// buffer.len(). Adding buffer.len() rather than bytes_read here ensures any
// such brokenness isn't propegated to udfread.
total_bytes_read += buffer.len();
break;
}
}
Ok(total_bytes_read)
})();
match result {
// TODO: Ensure this doesn't overflow
Ok(read) => (read / UDF_BLOCK_SIZE as usize) as c_int,
Err(err) => {
eprintln!("Error reading file: {}", err);
-1
}
}
}
unsafe extern "C" fn c_free_reader(ptr: *mut c_includes::udfread_block_input) -> c_int {
Box::from_raw(ptr as *mut Self);
0
}
}
pub struct UdfRead {
udfread: *mut c_includes::udfread,
}
impl UdfRead {
pub fn open_file(path: impl AsRef<Path>) -> Option<UdfRead> {
let file_reader = match FileReader::new(path) {
Ok(val) => Some(val),
Err(err) => {
eprintln!("Error opening file: {}", err);
None
}
}?;
let udfread = unsafe { c_includes::udfread_init() };
if udfread.is_null() {
return None;
}
let open_result = unsafe { c_includes::udfread_open_input(udfread, file_reader) };
if open_result < 0 {
eprintln!("udfread_open_input failed");
unsafe { c_includes::udfread_close(udfread) };
return None;
}
Some(UdfRead { udfread })
}
}
impl Drop for UdfRead {
fn drop(&mut self) {
unsafe { c_includes::udfread_close(self.udfread) };
}
}