This commit is contained in:
tux
2026-11-03 22:13:16 +01:00
parent 158e115242
commit 8012e912ec
19 changed files with 975 additions and 208 deletions
+149
View File
@@ -17,6 +17,13 @@ pub struct BackendHolder {
pub drive: Box<dyn Drive>,
}
fn ffi_guard<T>(default: T, f: impl FnOnce() -> T) -> T {
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
Ok(value) => value,
Err(_) => default,
}
}
unsafe fn cstr(s: &str) -> *mut c_char {
let clean: String = s.chars().filter(|c| *c != '\0').collect();
match CString::new(clean) {
@@ -212,6 +219,16 @@ pub unsafe extern "C" fn cdda_identify(
device: *const c_char,
messagedest: c_int,
messages: *mut *mut c_char,
) -> *mut CdromDrive {
ffi_guard(ptr::null_mut(), || {
cdda_identify_impl(device, messagedest, messages)
})
}
unsafe fn cdda_identify_impl(
device: *const c_char,
messagedest: c_int,
messages: *mut *mut c_char,
) -> *mut CdromDrive {
if !messages.is_null() {
*messages = ptr::null_mut();
@@ -234,6 +251,16 @@ pub unsafe extern "C" fn cdda_identify_cooked(
device: *const c_char,
messagedest: c_int,
messages: *mut *mut c_char,
) -> *mut CdromDrive {
ffi_guard(ptr::null_mut(), || {
cdda_identify_cooked_impl(device, messagedest, messages)
})
}
unsafe fn cdda_identify_cooked_impl(
device: *const c_char,
messagedest: c_int,
messages: *mut *mut c_char,
) -> *mut CdromDrive {
if !messages.is_null() {
*messages = ptr::null_mut();
@@ -257,6 +284,17 @@ pub unsafe extern "C" fn cdda_identify_scsi(
ioctl_device: *const c_char,
messagedest: c_int,
messages: *mut *mut c_char,
) -> *mut CdromDrive {
ffi_guard(ptr::null_mut(), || {
cdda_identify_scsi_impl(generic_device, ioctl_device, messagedest, messages)
})
}
unsafe fn cdda_identify_scsi_impl(
generic_device: *const c_char,
ioctl_device: *const c_char,
messagedest: c_int,
messages: *mut *mut c_char,
) -> *mut CdromDrive {
if !messages.is_null() {
*messages = ptr::null_mut();
@@ -289,6 +327,15 @@ pub unsafe extern "C" fn cdda_identify_scsi(
pub unsafe extern "C" fn cdda_find_a_cdrom(
messagedest: c_int,
messages: *mut *mut c_char,
) -> *mut CdromDrive {
ffi_guard(ptr::null_mut(), || {
cdda_find_a_cdrom_impl(messagedest, messages)
})
}
unsafe fn cdda_find_a_cdrom_impl(
messagedest: c_int,
messages: *mut *mut c_char,
) -> *mut CdromDrive {
if !messages.is_null() {
*messages = ptr::null_mut();
@@ -301,6 +348,10 @@ pub unsafe extern "C" fn cdda_find_a_cdrom(
#[no_mangle]
pub unsafe extern "C" fn cdda_open(d: *mut CdromDrive) -> c_int {
ffi_guard(-100, || cdda_open_impl(d))
}
unsafe fn cdda_open_impl(d: *mut CdromDrive) -> c_int {
if d.is_null() {
return -100;
}
@@ -343,6 +394,10 @@ pub unsafe extern "C" fn cdda_open(d: *mut CdromDrive) -> c_int {
#[no_mangle]
pub unsafe extern "C" fn cdda_close(d: *mut CdromDrive) -> c_int {
ffi_guard(0, || cdda_close_impl(d))
}
unsafe fn cdda_close_impl(d: *mut CdromDrive) -> c_int {
if d.is_null() {
return 0;
}
@@ -357,6 +412,18 @@ pub unsafe extern "C" fn cdda_read_timed(
beginsector: c_long,
sectors: c_long,
ms: *mut c_int,
) -> c_long {
ffi_guard(-1, || {
cdda_read_timed_impl(d, buffer, beginsector, sectors, ms)
})
}
unsafe fn cdda_read_timed_impl(
d: *mut CdromDrive,
buffer: *mut c_void,
beginsector: c_long,
sectors: c_long,
ms: *mut c_int,
) -> c_long {
if !ms.is_null() {
*ms = -1;
@@ -422,6 +489,10 @@ pub unsafe extern "C" fn cdda_read(
#[no_mangle]
pub unsafe extern "C" fn cdda_speed_set(d: *mut CdromDrive, speed: c_int) -> c_int {
ffi_guard(-405, || cdda_speed_set_impl(d, speed))
}
unsafe fn cdda_speed_set_impl(d: *mut CdromDrive, speed: c_int) -> c_int {
match holder(d) {
Some(h) => {
let ret = h.drive.speed_set(speed);
@@ -569,3 +640,81 @@ pub unsafe extern "C" fn cdda_track_bitmap(
// Keep the c_long import used on all targets.
#[allow(dead_code)]
fn _typecheck(_: c_long) {}
#[cfg(test)]
mod tests {
use super::*;
use cdrustanoia_core::device::{DiscToc, InterfaceKind, ReadError, TimedRead};
struct PanicDrive {
toc: DiscToc,
}
impl Drive for PanicDrive {
fn toc(&self) -> &DiscToc {
&self.toc
}
fn toc_mut(&mut self) -> &mut DiscToc {
&mut self.toc
}
fn opened(&self) -> bool {
false
}
fn open_disc(&mut self) -> Result<(), String> {
panic!("intentional test panic");
}
fn read_audio(
&mut self,
_buffer: Option<&mut [u8]>,
_begin: i64,
_sectors: i64,
) -> Result<usize, ReadError> {
Err(ReadError::closed())
}
fn read_timed(
&mut self,
_buffer: Option<&mut [u8]>,
_begin: i64,
_sectors: i64,
) -> TimedRead {
TimedRead {
result: Err(ReadError::closed()),
milliseconds: -1,
}
}
fn speed_set(&mut self, _speed: i32) -> i32 {
-405
}
fn close(&mut self) {}
fn drive_model(&self) -> &str {
"panic drive"
}
fn bigendianp(&self) -> i32 {
0
}
fn set_bigendianp(&mut self, _value: i32) {}
fn nsectors(&self) -> i64 {
1
}
fn set_nsectors(&mut self, _value: i64) {}
fn interface(&self) -> InterfaceKind {
InterfaceKind::SgIo
}
}
#[test]
fn c_api_catches_panics() {
unsafe {
let d = make_drive(
"panic",
None,
Box::new(PanicDrive {
toc: DiscToc::default(),
}),
);
assert!(!d.is_null());
assert_eq!(cdda_open(d), -100);
assert_eq!(cdda_close(d), 0);
}
}
}