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 {
|
if let Some(filter) = &self.filter {
|
||||||
options.filter_user_data = filter.to_glib_none().0;
|
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(
|
Stash(
|
||||||
|
|
@ -187,7 +187,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!((*ptr).unused_ints, [0; 6]);
|
assert_eq!((*ptr).unused_ints, [0; 6]);
|
||||||
assert_eq!((*ptr).unused_ptrs, [ptr::null_mut(); 3]);
|
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!(
|
assert_eq!(
|
||||||
(*ptr).filter_user_data,
|
(*ptr).filter_user_data,
|
||||||
options.filter.as_ref().unwrap().to_glib_none().0,
|
options.filter.as_ref().unwrap().to_glib_none().0,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@ use glib::translate::*;
|
||||||
use glib_sys::gpointer;
|
use glib_sys::gpointer;
|
||||||
use libc::c_char;
|
use libc::c_char;
|
||||||
use ostree_sys::*;
|
use ostree_sys::*;
|
||||||
|
use std::any::Any;
|
||||||
|
use std::panic::catch_unwind;
|
||||||
use std::path::{Path, PathBuf};
|
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
|
/// 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.
|
/// function is called for every directory and file in the dirtree.
|
||||||
|
|
@ -62,13 +65,12 @@ impl FromGlibPtrNone<gpointer> for &RepoCheckoutFilter {
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// If any parameter is a null pointer, the function 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,
|
repo: *mut OstreeRepo,
|
||||||
path: *const c_char,
|
path: *const c_char,
|
||||||
stat: *mut libc::stat,
|
stat: *mut libc::stat,
|
||||||
user_data: gpointer,
|
user_data: gpointer,
|
||||||
) -> OstreeRepoCheckoutFilterResult {
|
) -> OstreeRepoCheckoutFilterResult {
|
||||||
// TODO: handle unwinding
|
|
||||||
// We can't guarantee it's a valid pointer, but we can make sure it's not null.
|
// We can't guarantee it's a valid pointer, but we can make sure it's not null.
|
||||||
assert!(!stat.is_null());
|
assert!(!stat.is_null());
|
||||||
let stat = &*stat;
|
let stat = &*stat;
|
||||||
|
|
@ -85,6 +87,36 @@ pub(super) unsafe extern "C" fn filter_trampoline(
|
||||||
result.to_glib()
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue