From f4feed01f5367c56db825c813bc00faa43245dd0 Mon Sep 17 00:00:00 2001 From: Jackson Coxson Date: Tue, 25 Mar 2025 00:09:57 -0600 Subject: [PATCH] Mounter bindings --- ffi/examples/mounter.c | 317 ++++++++++++++++++++++++++ ffi/src/lib.rs | 1 + ffi/src/mounter.rs | 494 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 812 insertions(+) create mode 100644 ffi/examples/mounter.c create mode 100644 ffi/src/mounter.rs diff --git a/ffi/examples/mounter.c b/ffi/examples/mounter.c new file mode 100644 index 0000000..3b3710e --- /dev/null +++ b/ffi/examples/mounter.c @@ -0,0 +1,317 @@ +// Jackson Coxson + +#include "idevice.h" +#include "plist/plist.h" +#include +#include +#include +#include +#include +#include + +void print_usage() { + printf("Usage: image_mounter_test [options] [command]\n"); + printf("Options:\n"); + printf(" --ip IP_ADDRESS Device IP address (default: 10.7.0.2)\n"); + printf(" --pairing FILE Pairing file path (default: " + "pairing_file.plist)\n"); + printf("\nCommands:\n"); + printf(" list-devices List mounted devices\n"); + printf(" lookup TYPE Lookup image signature by type\n"); + printf(" upload TYPE IMG SIG Upload an image with signature\n"); + printf(" mount TYPE SIG [TC] Mount an image (optional trust cache)\n"); + printf(" unmount PATH Unmount image at path\n"); + printf(" dev-status Query developer mode status\n"); + printf(" mount-dev IMG SIG Mount developer image\n"); + printf(" help Show this help message\n"); +} + +int read_file(const char *filename, uint8_t **data, size_t *length) { + FILE *file = fopen(filename, "rb"); + if (!file) { + perror("Failed to open file"); + return 0; + } + + fseek(file, 0, SEEK_END); + *length = ftell(file); + fseek(file, 0, SEEK_SET); + + *data = malloc(*length); + if (!*data) { + perror("Failed to allocate memory"); + fclose(file); + return 0; + } + + if (fread(*data, 1, *length, file) != *length) { + perror("Failed to read file"); + free(*data); + fclose(file); + return 0; + } + + fclose(file); + return 1; +} + +int main(int argc, char **argv) { + // Initialize logger + idevice_init_logger(Debug, Disabled, NULL); + + // Default values + char *ip = "10.7.0.2"; + char *pairing_file_path = "pairing_file.plist"; + char *command = NULL; + + // Parse arguments + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--ip") == 0) { + if (i + 1 >= argc) { + printf("Error: Missing IP address argument\n"); + return 1; + } + ip = argv[++i]; + } else if (strcmp(argv[i], "--pairing") == 0) { + if (i + 1 >= argc) { + printf("Error: Missing pairing file argument\n"); + return 1; + } + pairing_file_path = argv[++i]; + } else if (strcmp(argv[i], "help") == 0) { + print_usage(); + return 0; + } else { + command = argv[i]; + break; + } + } + + if (!command) { + print_usage(); + return 1; + } + + // Create the socket address + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(LOCKDOWN_PORT); + if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1) { + fprintf(stderr, "Invalid IP address\n"); + return 1; + } + + // Read pairing file + IdevicePairingFile *pairing_file = NULL; + IdeviceErrorCode err = + idevice_pairing_file_read(pairing_file_path, &pairing_file); + if (err != IdeviceSuccess) { + fprintf(stderr, "Failed to read pairing file: %d\n", err); + return 1; + } + + // Create TCP provider + TcpProviderHandle *provider = NULL; + err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file, + "ImageMounterTest", &provider); + if (err != IdeviceSuccess) { + fprintf(stderr, "Failed to create TCP provider: %d\n", err); + idevice_pairing_file_free(pairing_file); + return 1; + } + + // Connect to image mounter + ImageMounterHandle *client = NULL; + err = image_mounter_connect_tcp(provider, &client); + if (err != IdeviceSuccess) { + fprintf(stderr, "Failed to connect to image mounter: %d\n", err); + tcp_provider_free(provider); + return 1; + } + tcp_provider_free(provider); + + // Process command + int success = 1; + if (strcmp(command, "list-devices") == 0) { + void *devices = NULL; + size_t devices_len = 0; + err = image_mounter_copy_devices(client, &devices, &devices_len); + if (err == IdeviceSuccess) { + plist_t *device_list = (plist_t *)devices; + printf("Mounted devices:\n"); + for (size_t i = 0; i < devices_len; i++) { + plist_t device = device_list[i]; + char *xml = NULL; + uint32_t len = 0; + plist_to_xml(device, &xml, &len); + printf("- %s\n", xml); + plist_mem_free(xml); + plist_free(device); + } + } else { + fprintf(stderr, "Failed to get device list: %d\n", err); + success = 0; + } + } else if (strcmp(command, "lookup") == 0) { + if (argc < 3) { + printf("Error: Missing image type argument\n"); + success = 0; + } else { + char *image_type = argv[2]; + uint8_t *signature = NULL; + size_t signature_len = 0; + + err = image_mounter_lookup_image(client, image_type, &signature, + &signature_len); + if (err == IdeviceSuccess) { + printf("Signature for %s (%zu bytes):\n", image_type, signature_len); + for (size_t i = 0; i < signature_len; i++) { + printf("%02x", signature[i]); + } + printf("\n"); + free(signature); + } else { + fprintf(stderr, "Failed to lookup image: %d\n", err); + success = 0; + } + } + } else if (strcmp(command, "upload") == 0) { + if (argc < 5) { + printf("Error: Missing arguments for upload\n"); + success = 0; + } else { + char *image_type = argv[2]; + char *image_file = argv[3]; + char *signature_file = argv[4]; + + uint8_t *image_data = NULL; + size_t image_len = 0; + uint8_t *signature_data = NULL; + size_t signature_len = 0; + + if (!read_file(image_file, &image_data, &image_len)) { + success = 0; + } else if (!read_file(signature_file, &signature_data, &signature_len)) { + free(image_data); + success = 0; + } else { + err = image_mounter_upload_image(client, image_type, image_data, + image_len, signature_data, + signature_len); + if (err == IdeviceSuccess) { + printf("Image uploaded successfully\n"); + } else { + fprintf(stderr, "Failed to upload image: %d\n", err); + success = 0; + } + + free(image_data); + free(signature_data); + } + } + } else if (strcmp(command, "mount") == 0) { + if (argc < 4) { + printf("Error: Missing arguments for mount\n"); + success = 0; + } else { + char *image_type = argv[2]; + char *signature_file = argv[3]; + char *trust_cache_file = (argc > 4) ? argv[4] : NULL; + + uint8_t *signature_data = NULL; + size_t signature_len = 0; + uint8_t *trust_cache_data = NULL; + size_t trust_cache_len = 0; + + if (!read_file(signature_file, &signature_data, &signature_len)) { + success = 0; + } else { + if (trust_cache_file && + !read_file(trust_cache_file, &trust_cache_data, &trust_cache_len)) { + free(signature_data); + success = 0; + } else { + err = image_mounter_mount_image( + client, image_type, signature_data, signature_len, + trust_cache_data, trust_cache_len, + NULL); // No info plist in this example + if (err == IdeviceSuccess) { + printf("Image mounted successfully\n"); + } else { + fprintf(stderr, "Failed to mount image: %d\n", err); + success = 0; + } + + free(signature_data); + if (trust_cache_data) + free(trust_cache_data); + } + } + } + } else if (strcmp(command, "unmount") == 0) { + if (argc < 3) { + printf("Error: Missing mount path argument\n"); + success = 0; + } else { + char *mount_path = argv[2]; + err = image_mounter_unmount_image(client, mount_path); + if (err == IdeviceSuccess) { + printf("Image unmounted successfully\n"); + } else { + fprintf(stderr, "Failed to unmount image: %d\n", err); + success = 0; + } + } + } else if (strcmp(command, "dev-status") == 0) { + int status = 0; + err = image_mounter_query_developer_mode_status(client, &status); + if (err == IdeviceSuccess) { + printf("Developer mode status: %s\n", status ? "enabled" : "disabled"); + } else { + fprintf(stderr, "Failed to query developer mode status: %d\n", err); + success = 0; + } + } else if (strcmp(command, "mount-dev") == 0) { + if (argc < 4) { + printf("Error: Missing arguments for mount-dev\n"); + success = 0; + } else { + char *image_file = argv[2]; + char *signature_file = argv[3]; + + uint8_t *image_data = NULL; + size_t image_len = 0; + uint8_t *signature_data = NULL; + size_t signature_len = 0; + + if (!read_file(image_file, &image_data, &image_len)) { + success = 0; + } else if (!read_file(signature_file, &signature_data, &signature_len)) { + free(image_data); + success = 0; + } else { + err = image_mounter_mount_developer(client, image_data, image_len, + signature_data, signature_len); + if (err == IdeviceSuccess) { + printf("Developer image mounted successfully\n"); + } else { + fprintf(stderr, "Failed to mount developer image: %d\n", err); + success = 0; + } + + free(image_data); + free(signature_data); + } + } + } else { + printf("Unknown command: %s\n", command); + print_usage(); + success = 0; + } + + // Cleanup + image_mounter_free(client); + + return success ? 0 : 1; +} diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index 73f2139..b0b6070 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -4,6 +4,7 @@ mod errors; pub mod heartbeat; pub mod installation_proxy; pub mod logging; +pub mod mounter; mod pairing_file; pub mod provider; pub mod usbmuxd; diff --git a/ffi/src/mounter.rs b/ffi/src/mounter.rs new file mode 100644 index 0000000..315794e --- /dev/null +++ b/ffi/src/mounter.rs @@ -0,0 +1,494 @@ +// Jackson Coxson + +use std::ffi::c_void; + +use idevice::{IdeviceError, IdeviceService, mounter::ImageMounter}; +use plist::Value; + +use crate::{ + IdeviceErrorCode, IdeviceHandle, RUNTIME, + provider::{TcpProviderHandle, UsbmuxdProviderHandle}, + util, +}; + +pub struct ImageMounterHandle(pub ImageMounter); + +/// Connects to the Image Mounter service using a TCP provider +/// +/// # Arguments +/// * [`provider`] - A TcpProvider +/// * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `provider` must be a valid pointer to a handle allocated by this library +/// `client` must be a valid, non-null pointer to a location where the handle will be stored +#[unsafe(no_mangle)] +pub unsafe extern "C" fn image_mounter_connect_tcp( + provider: *mut TcpProviderHandle, + client: *mut *mut ImageMounterHandle, +) -> IdeviceErrorCode { + if provider.is_null() || client.is_null() { + log::error!("Null pointer provided"); + return IdeviceErrorCode::InvalidArg; + } + + let res: Result = RUNTIME.block_on(async move { + let provider_box = unsafe { Box::from_raw(provider) }; + let provider_ref = &provider_box.0; + let result = ImageMounter::connect(provider_ref).await; + std::mem::forget(provider_box); + result + }); + + match res { + Ok(r) => { + let boxed = Box::new(ImageMounterHandle(r)); + unsafe { *client = Box::into_raw(boxed) }; + IdeviceErrorCode::IdeviceSuccess + } + Err(e) => { + let _ = unsafe { Box::from_raw(provider) }; + e.into() + } + } +} + +/// Connects to the Image Mounter service using a Usbmuxd provider +/// +/// # Arguments +/// * [`provider`] - A UsbmuxdProvider +/// * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `provider` must be a valid pointer to a handle allocated by this library +/// `client` must be a valid, non-null pointer to a location where the handle will be stored +#[unsafe(no_mangle)] +pub unsafe extern "C" fn image_mounter_connect_usbmuxd( + provider: *mut UsbmuxdProviderHandle, + client: *mut *mut ImageMounterHandle, +) -> IdeviceErrorCode { + if provider.is_null() { + log::error!("Provider is null"); + return IdeviceErrorCode::InvalidArg; + } + + let res: Result = RUNTIME.block_on(async move { + let provider_box = unsafe { Box::from_raw(provider) }; + let provider_ref = &provider_box.0; + let result = ImageMounter::connect(provider_ref).await; + std::mem::forget(provider_box); + result + }); + + match res { + Ok(r) => { + let boxed = Box::new(ImageMounterHandle(r)); + unsafe { *client = Box::into_raw(boxed) }; + IdeviceErrorCode::IdeviceSuccess + } + Err(e) => e.into(), + } +} + +/// Creates a new ImageMounter client from an existing Idevice connection +/// +/// # Arguments +/// * [`socket`] - An IdeviceSocket handle +/// * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `socket` must be a valid pointer to a handle allocated by this library +/// `client` must be a valid, non-null pointer to a location where the handle will be stored +#[unsafe(no_mangle)] +pub unsafe extern "C" fn image_mounter_new( + socket: *mut IdeviceHandle, + client: *mut *mut ImageMounterHandle, +) -> IdeviceErrorCode { + if socket.is_null() { + return IdeviceErrorCode::InvalidArg; + } + let socket = unsafe { Box::from_raw(socket) }.0; + let r = ImageMounter::new(socket); + let boxed = Box::new(ImageMounterHandle(r)); + unsafe { *client = Box::into_raw(boxed) }; + IdeviceErrorCode::IdeviceSuccess +} + +/// Frees an ImageMounter handle +/// +/// # Arguments +/// * [`handle`] - The handle to free +/// +/// # Safety +/// `handle` must be a valid pointer to the handle that was allocated by this library, +/// or NULL (in which case this function does nothing) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn image_mounter_free(handle: *mut ImageMounterHandle) { + if !handle.is_null() { + log::debug!("Freeing image_mounter_client"); + let _ = unsafe { Box::from_raw(handle) }; + } +} + +/// Gets a list of mounted devices +/// +/// # Arguments +/// * [`client`] - A valid ImageMounter handle +/// * [`devices`] - Will be set to point to a slice of device plists on success +/// * [`devices_len`] - Will be set to the number of devices copied +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `client` must be a valid pointer to a handle allocated by this library +/// `devices` must be a valid, non-null pointer to a location where the plist will be stored +#[unsafe(no_mangle)] +pub unsafe extern "C" fn image_mounter_copy_devices( + client: *mut ImageMounterHandle, + devices: *mut *mut c_void, + devices_len: *mut libc::size_t, +) -> IdeviceErrorCode { + let res: Result, IdeviceError> = RUNTIME.block_on(async move { + let mut client_box = unsafe { Box::from_raw(client) }; + let client_ref = &mut client_box.0; + let result = client_ref.copy_devices().await; + std::mem::forget(client_box); + result + }); + + match res { + Ok(devices_list) => { + let devices_list = devices_list + .into_iter() + .map(|x| util::plist_to_libplist(&x)) + .collect::>(); + let len = devices_list.len(); + let boxed_slice = devices_list.into_boxed_slice(); + let ptr = Box::leak(boxed_slice).as_mut_ptr(); + + unsafe { + *devices = ptr as *mut c_void; + *devices_len = len; + } + IdeviceErrorCode::IdeviceSuccess + } + Err(e) => e.into(), + } +} + +/// Looks up an image and returns its signature +/// +/// # Arguments +/// * [`client`] - A valid ImageMounter handle +/// * [`image_type`] - The type of image to look up +/// * [`signature`] - Will be set to point to the signature data on success +/// * [`signature_len`] - Will be set to the length of the signature data +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `client` must be a valid pointer to a handle allocated by this library +/// `image_type` must be a valid null-terminated C string +/// `signature` and `signature_len` must be valid pointers +#[unsafe(no_mangle)] +pub unsafe extern "C" fn image_mounter_lookup_image( + client: *mut ImageMounterHandle, + image_type: *const libc::c_char, + signature: *mut *mut u8, + signature_len: *mut libc::size_t, +) -> IdeviceErrorCode { + if image_type.is_null() || signature.is_null() || signature_len.is_null() { + return IdeviceErrorCode::InvalidArg; + } + + let image_type_cstr = unsafe { std::ffi::CStr::from_ptr(image_type) }; + let image_type = match image_type_cstr.to_str() { + Ok(s) => s, + Err(_) => return IdeviceErrorCode::InvalidArg, + }; + + let res: Result, IdeviceError> = RUNTIME.block_on(async move { + let mut client_box = unsafe { Box::from_raw(client) }; + let client_ref = &mut client_box.0; + let result = client_ref.lookup_image(image_type).await; + std::mem::forget(client_box); + result + }); + + match res { + Ok(sig) => { + let mut boxed = sig.into_boxed_slice(); + unsafe { + *signature = boxed.as_mut_ptr(); + *signature_len = boxed.len(); + } + std::mem::forget(boxed); + IdeviceErrorCode::IdeviceSuccess + } + Err(e) => e.into(), + } +} + +/// Uploads an image to the device +/// +/// # Arguments +/// * [`client`] - A valid ImageMounter handle +/// * [`image_type`] - The type of image being uploaded +/// * [`image`] - Pointer to the image data +/// * [`image_len`] - Length of the image data +/// * [`signature`] - Pointer to the signature data +/// * [`signature_len`] - Length of the signature data +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// All pointers must be valid and non-null +/// `image_type` must be a valid null-terminated C string +#[unsafe(no_mangle)] +pub unsafe extern "C" fn image_mounter_upload_image( + client: *mut ImageMounterHandle, + image_type: *const libc::c_char, + image: *const u8, + image_len: libc::size_t, + signature: *const u8, + signature_len: libc::size_t, +) -> IdeviceErrorCode { + if image_type.is_null() || image.is_null() || signature.is_null() { + return IdeviceErrorCode::InvalidArg; + } + + let image_type_cstr = unsafe { std::ffi::CStr::from_ptr(image_type) }; + let image_type = match image_type_cstr.to_str() { + Ok(s) => s, + Err(_) => return IdeviceErrorCode::InvalidArg, + }; + + let image_slice = unsafe { std::slice::from_raw_parts(image, image_len) }; + let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) }; + + let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { + let mut client_box = unsafe { Box::from_raw(client) }; + let client_ref = &mut client_box.0; + let result = client_ref + .upload_image(image_type, image_slice, signature_slice.to_vec()) + .await; + std::mem::forget(client_box); + result + }); + + match res { + Ok(_) => IdeviceErrorCode::IdeviceSuccess, + Err(e) => e.into(), + } +} + +/// Mounts an image on the device +/// +/// # Arguments +/// * [`client`] - A valid ImageMounter handle +/// * [`image_type`] - The type of image being mounted +/// * [`signature`] - Pointer to the signature data +/// * [`signature_len`] - Length of the signature data +/// * [`trust_cache`] - Pointer to trust cache data (optional) +/// * [`trust_cache_len`] - Length of trust cache data (0 if none) +/// * [`info_plist`] - Pointer to info plist (optional) +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// All pointers must be valid (except optional ones which can be null) +/// `image_type` must be a valid null-terminated C string +#[unsafe(no_mangle)] +pub unsafe extern "C" fn image_mounter_mount_image( + client: *mut ImageMounterHandle, + image_type: *const libc::c_char, + signature: *const u8, + signature_len: libc::size_t, + trust_cache: *const u8, + trust_cache_len: libc::size_t, + info_plist: *const c_void, +) -> IdeviceErrorCode { + if image_type.is_null() || signature.is_null() { + return IdeviceErrorCode::InvalidArg; + } + + let image_type_cstr = unsafe { std::ffi::CStr::from_ptr(image_type) }; + let image_type = match image_type_cstr.to_str() { + Ok(s) => s, + Err(_) => return IdeviceErrorCode::InvalidArg, + }; + + let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) }; + let trust_cache = if !trust_cache.is_null() && trust_cache_len > 0 { + Some(unsafe { std::slice::from_raw_parts(trust_cache, trust_cache_len).to_vec() }) + } else { + None + }; + + let info_plist = if !info_plist.is_null() { + Some( + unsafe { Box::from_raw(info_plist as *mut Value) } + .as_ref() + .clone(), + ) + } else { + None + }; + + let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { + let mut client_box = unsafe { Box::from_raw(client) }; + let client_ref = &mut client_box.0; + let result = client_ref + .mount_image( + image_type, + signature_slice.to_vec(), + trust_cache, + info_plist, + ) + .await; + std::mem::forget(client_box); + result + }); + + match res { + Ok(_) => IdeviceErrorCode::IdeviceSuccess, + Err(e) => e.into(), + } +} + +/// Unmounts an image from the device +/// +/// # Arguments +/// * [`client`] - A valid ImageMounter handle +/// * [`mount_path`] - The path where the image is mounted +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `client` must be a valid pointer to a handle allocated by this library +/// `mount_path` must be a valid null-terminated C string +#[unsafe(no_mangle)] +pub unsafe extern "C" fn image_mounter_unmount_image( + client: *mut ImageMounterHandle, + mount_path: *const libc::c_char, +) -> IdeviceErrorCode { + if mount_path.is_null() { + return IdeviceErrorCode::InvalidArg; + } + + let mount_path_cstr = unsafe { std::ffi::CStr::from_ptr(mount_path) }; + let mount_path = match mount_path_cstr.to_str() { + Ok(s) => s, + Err(_) => return IdeviceErrorCode::InvalidArg, + }; + + let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { + let mut client_box = unsafe { Box::from_raw(client) }; + let client_ref = &mut client_box.0; + let result = client_ref.unmount_image(mount_path).await; + std::mem::forget(client_box); + result + }); + + match res { + Ok(_) => IdeviceErrorCode::IdeviceSuccess, + Err(e) => e.into(), + } +} + +/// Queries the developer mode status +/// +/// # Arguments +/// * [`client`] - A valid ImageMounter handle +/// * [`status`] - Will be set to the developer mode status (1 = enabled, 0 = disabled) +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `client` must be a valid pointer to a handle allocated by this library +/// `status` must be a valid pointer +#[unsafe(no_mangle)] +pub unsafe extern "C" fn image_mounter_query_developer_mode_status( + client: *mut ImageMounterHandle, + status: *mut libc::c_int, +) -> IdeviceErrorCode { + if status.is_null() { + return IdeviceErrorCode::InvalidArg; + } + + let res: Result = RUNTIME.block_on(async move { + let mut client_box = unsafe { Box::from_raw(client) }; + let client_ref = &mut client_box.0; + let result = client_ref.query_developer_mode_status().await; + std::mem::forget(client_box); + result + }); + + match res { + Ok(s) => { + unsafe { *status = if s { 1 } else { 0 } }; + IdeviceErrorCode::IdeviceSuccess + } + Err(e) => e.into(), + } +} + +/// Mounts a developer image +/// +/// # Arguments +/// * [`client`] - A valid ImageMounter handle +/// * [`image`] - Pointer to the image data +/// * [`image_len`] - Length of the image data +/// * [`signature`] - Pointer to the signature data +/// * [`signature_len`] - Length of the signature data +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// All pointers must be valid and non-null +#[unsafe(no_mangle)] +pub unsafe extern "C" fn image_mounter_mount_developer( + client: *mut ImageMounterHandle, + image: *const u8, + image_len: libc::size_t, + signature: *const u8, + signature_len: libc::size_t, +) -> IdeviceErrorCode { + if image.is_null() || signature.is_null() { + return IdeviceErrorCode::InvalidArg; + } + + let image_slice = unsafe { std::slice::from_raw_parts(image, image_len) }; + let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) }; + + let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { + let mut client_box = unsafe { Box::from_raw(client) }; + let client_ref = &mut client_box.0; + let result = client_ref + .mount_developer(image_slice, signature_slice.to_vec()) + .await; + std::mem::forget(client_box); + result + }); + + match res { + Ok(_) => IdeviceErrorCode::IdeviceSuccess, + Err(e) => e.into(), + } +}