lib: catch unwinds in RepoCheckoutFilter
This commit is contained in:
parent
315cd5394e
commit
87b34be855
|
|
@ -91,7 +91,7 @@ impl<'a> ToGlibPtr<'a, *const OstreeRepoCheckoutAtOptions> for RepoCheckoutAtOpt
|
|||
|
||||
if let Some(filter) = &self.filter {
|
||||
options.filter_user_data = filter.to_glib_none().0;
|
||||
options.filter = Some(repo_checkout_filter::filter_trampoline);
|
||||
options.filter = Some(repo_checkout_filter::filter_trampoline_unwindsafe);
|
||||
}
|
||||
|
||||
Stash(
|
||||
|
|
@ -187,7 +187,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!((*ptr).unused_ints, [0; 6]);
|
||||
assert_eq!((*ptr).unused_ptrs, [ptr::null_mut(); 3]);
|
||||
assert!((*ptr).filter == Some(repo_checkout_filter::filter_trampoline));
|
||||
assert!((*ptr).filter == Some(repo_checkout_filter::filter_trampoline_unwindsafe));
|
||||
assert_eq!(
|
||||
(*ptr).filter_user_data,
|
||||
options.filter.as_ref().unwrap().to_glib_none().0,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ use glib::translate::*;
|
|||
use glib_sys::gpointer;
|
||||
use libc::c_char;
|
||||
use ostree_sys::*;
|
||||
use std::any::Any;
|
||||
use std::panic::catch_unwind;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::abort;
|
||||
|
||||
/// A filter callback to decide which files to checkout from a [Repo](struct.Repo.html). The
|
||||
/// function is called for every directory and file in the dirtree.
|
||||
|
|
@ -62,13 +65,12 @@ impl FromGlibPtrNone<gpointer> for &RepoCheckoutFilter {
|
|||
///
|
||||
/// # Panics
|
||||
/// If any parameter is a null pointer, the function panics.
|
||||
pub(super) unsafe extern "C" fn filter_trampoline(
|
||||
unsafe extern "C" fn filter_trampoline(
|
||||
repo: *mut OstreeRepo,
|
||||
path: *const c_char,
|
||||
stat: *mut libc::stat,
|
||||
user_data: gpointer,
|
||||
) -> OstreeRepoCheckoutFilterResult {
|
||||
// TODO: handle unwinding
|
||||
// We can't guarantee it's a valid pointer, but we can make sure it's not null.
|
||||
assert!(!stat.is_null());
|
||||
let stat = &*stat;
|
||||
|
|
@ -85,6 +87,36 @@ pub(super) unsafe extern "C" fn filter_trampoline(
|
|||
result.to_glib()
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn filter_trampoline_unwindsafe(
|
||||
repo: *mut OstreeRepo,
|
||||
path: *const c_char,
|
||||
stat: *mut libc::stat,
|
||||
user_data: gpointer,
|
||||
) -> OstreeRepoCheckoutFilterResult {
|
||||
// Unwinding across an FFI boundary is Undefined Behavior and we have no other way to communicate
|
||||
// the error. We abort() safely to avoid further problems.
|
||||
let result = catch_unwind(move || filter_trampoline(repo, path, stat, user_data));
|
||||
result.unwrap_or_else(|panic| {
|
||||
print_panic(panic);
|
||||
abort()
|
||||
})
|
||||
}
|
||||
|
||||
fn print_panic(panic: Box<dyn Any>) {
|
||||
eprintln!("A Rust callback invoked by C code panicked.");
|
||||
eprintln!("Unwinding across FFI boundaries is Undefined Behavior so abort() will be called.");
|
||||
let msg = {
|
||||
if let Some(s) = panic.as_ref().downcast_ref::<&str>() {
|
||||
s
|
||||
} else if let Some(s) = panic.as_ref().downcast_ref::<String>() {
|
||||
s
|
||||
} else {
|
||||
"UNABLE TO SHOW VALUE OF PANIC"
|
||||
}
|
||||
};
|
||||
eprintln!("Panic value: {}", msg);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
Loading…
Reference in New Issue