Skip to content

Commit bb367e7

Browse files
authored
Merge pull request #132 from larsewi/refactor-ffi-cstr-helper
Extract cstr_arg helper for FFI null + UTF-8 validation
2 parents 69356ec + 10cca0d commit bb367e7

1 file changed

Lines changed: 32 additions & 38 deletions

File tree

src/lib.rs

Lines changed: 32 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,27 @@ fn ffi_guard<T>(name: &str, default: T, body: impl FnOnce() -> T) -> T {
3636
}
3737
}
3838

39+
/// Validate a required C string FFI argument and convert it to `&str`.
40+
///
41+
/// Logs an error and returns `None` if `ptr` is null or the bytes are not UTF-8.
42+
/// Callers translate `None` into the function's failure sentinel.
43+
///
44+
/// # Safety
45+
/// If `ptr` is non-null, it must point to a valid, null-terminated C string.
46+
unsafe fn cstr_arg<'a>(fn_name: &str, arg_name: &str, ptr: *const c_char) -> Option<&'a str> {
47+
if ptr.is_null() {
48+
log::error!("{}(): Bad argument: {} cannot be NULL", fn_name, arg_name);
49+
return None;
50+
}
51+
match unsafe { CStr::from_ptr(ptr) }.to_str() {
52+
Ok(s) => Some(s),
53+
Err(e) => {
54+
log::error!("{}(): Bad argument: {}: {}", fn_name, arg_name, e);
55+
None
56+
}
57+
}
58+
}
59+
3960
/// Install or replace the log callback.
4061
///
4162
/// The first call installs the global logger; subsequent calls atomically swap
@@ -72,18 +93,10 @@ pub unsafe extern "C" fn lch_log_init(
7293
#[unsafe(no_mangle)]
7394
pub unsafe extern "C" fn lch_init(work_dir: *const c_char) -> *mut config::Config {
7495
ffi_guard("lch_init", std::ptr::null_mut(), || {
75-
if work_dir.is_null() {
76-
log::error!("lch_init(): Bad argument: work directory cannot be NULL");
96+
let Some(work_dir) = (unsafe { cstr_arg("lch_init", "work_dir", work_dir) }) else {
7797
return std::ptr::null_mut();
78-
}
79-
80-
let path = match unsafe { CStr::from_ptr(work_dir) }.to_str() {
81-
Ok(path) => PathBuf::from(path),
82-
Err(e) => {
83-
log::error!("lch_init(): Bad argument: {e}");
84-
return std::ptr::null_mut();
85-
}
8698
};
99+
let path = PathBuf::from(work_dir);
87100

88101
log::debug!("lch_init(work_dir={})", path.display());
89102

@@ -298,43 +311,24 @@ pub unsafe extern "C" fn lch_patch_inject(
298311
return FAILURE;
299312
}
300313

301-
if name.is_null() || value.is_null() || sql_type.is_null() {
302-
log::error!(
303-
"lch_patch_inject(): Bad argument: name, value, and sql_type cannot be NULL"
304-
);
305-
return FAILURE;
306-
}
307-
308314
if out_buf.is_null() || out_len.is_null() {
309315
log::error!("lch_patch_inject(): Bad argument: out_buf and out_len cannot be NULL");
310316
return FAILURE;
311317
}
312318

313-
let config = unsafe { &*config };
314-
let data = unsafe { std::slice::from_raw_parts(in_buf, in_len) };
315-
316-
let name = match unsafe { CStr::from_ptr(name) }.to_str() {
317-
Ok(s) => s,
318-
Err(e) => {
319-
log::error!("lch_patch_inject(): Bad argument: name: {e}");
320-
return FAILURE;
321-
}
319+
let Some(name) = (unsafe { cstr_arg("lch_patch_inject", "name", name) }) else {
320+
return FAILURE;
322321
};
323-
let value = match unsafe { CStr::from_ptr(value) }.to_str() {
324-
Ok(s) => s,
325-
Err(e) => {
326-
log::error!("lch_patch_inject(): Bad argument: value: {e}");
327-
return FAILURE;
328-
}
322+
let Some(value) = (unsafe { cstr_arg("lch_patch_inject", "value", value) }) else {
323+
return FAILURE;
329324
};
330-
let sql_type = match unsafe { CStr::from_ptr(sql_type) }.to_str() {
331-
Ok(s) => s,
332-
Err(e) => {
333-
log::error!("lch_patch_inject(): Bad argument: sql_type: {e}");
334-
return FAILURE;
335-
}
325+
let Some(sql_type) = (unsafe { cstr_arg("lch_patch_inject", "sql_type", sql_type) }) else {
326+
return FAILURE;
336327
};
337328

329+
let config = unsafe { &*config };
330+
let data = unsafe { std::slice::from_raw_parts(in_buf, in_len) };
331+
338332
let mut patch = match wire::decode_patch(data) {
339333
Ok(patch) => patch,
340334
Err(e) => {

0 commit comments

Comments
 (0)