From 7ddcb5adb1c0d5d3bc13a0abd7504e5080847321 Mon Sep 17 00:00:00 2001 From: Rubens Brandao Date: Thu, 20 Jun 2024 09:15:03 -0300 Subject: [PATCH 1/4] implement rust Repository --- rust/src/lib.rs | 1 + rust/src/repository.rs | 584 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 585 insertions(+) create mode 100644 rust/src/repository.rs diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 0cb0484e65..5daff7ff8b 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -69,6 +69,7 @@ pub mod rc; pub mod references; pub mod relocation; pub mod render_layer; +pub mod repository; pub mod section; pub mod segment; pub mod settings; diff --git a/rust/src/repository.rs b/rust/src/repository.rs new file mode 100644 index 0000000000..c77380e5c2 --- /dev/null +++ b/rust/src/repository.rs @@ -0,0 +1,584 @@ +use core::{cmp, ffi, mem, ptr}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +use binaryninjacore_sys::*; + +use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner}; +use crate::string::{BnStrCompatible, BnString}; + +pub type PluginType = BNPluginType; +pub type PluginStatus = BNPluginStatus; + +/// Keeps track of all the repositories and keeps the `enabled_plugins.json` +/// file coherent with the plugins that are installed/uninstalled enabled/disabled +#[repr(transparent)] +pub struct RepositoryManager { + handle: ptr::NonNull, +} + +impl Drop for RepositoryManager { + fn drop(&mut self) { + unsafe { BNFreeRepositoryManager(self.as_raw()) } + } +} + +impl Clone for RepositoryManager { + fn clone(&self) -> Self { + unsafe { + Self::from_raw( + ptr::NonNull::new(BNNewRepositoryManagerReference(self.as_raw())).unwrap(), + ) + } + } +} + +impl Default for RepositoryManager { + fn default() -> Self { + let result = unsafe { BNGetRepositoryManager() }; + unsafe { Self::from_raw(ptr::NonNull::new(result).unwrap()) } + } +} + +impl RepositoryManager { + pub(crate) unsafe fn from_raw(handle: ptr::NonNull) -> Self { + Self { handle } + } + + #[allow(clippy::mut_from_ref)] + pub(crate) unsafe fn as_raw(&self) -> &mut BNRepositoryManager { + &mut *self.handle.as_ptr() + } + + pub fn new(plugins_path: S) -> Self { + let plugins_path = plugins_path.into_bytes_with_nul(); + let result = unsafe { + BNCreateRepositoryManager(plugins_path.as_ref().as_ptr() as *const ffi::c_char) + }; + unsafe { Self::from_raw(ptr::NonNull::new(result).unwrap()) } + } + + /// Check for updates for all managed Repository objects + pub fn check_for_updates(&self) -> bool { + unsafe { BNRepositoryManagerCheckForUpdates(self.as_raw()) } + } + + /// List of Repository objects being managed + pub fn repositories(&self) -> Array { + let mut count = 0; + let result = unsafe { BNRepositoryManagerGetRepositories(self.as_raw(), &mut count) }; + assert!(!result.is_null()); + unsafe { Array::new(result, count, ()) } + } + + /// Adds a new plugin repository for the manager to track. + /// + /// To remove a repository, restart Binary Ninja (and don't re-add the repository!). + /// File artifacts will remain on disk under repositories/ file in the User Folder. + /// + /// Before you can query plugin metadata from a repository, you need to call [RepositoryManager::check_for_updates]. + /// + /// * `url` - URL to the plugins.json containing the records for this repository + /// * `repopath` - path to where the repository will be stored on disk locally + /// + /// Returns true if the repository was successfully added, false otherwise. + pub fn add_repository( + &self, + url: U, + repository_path: P, + ) -> bool { + let url = url.into_bytes_with_nul(); + let repo_path = repository_path.into_bytes_with_nul(); + unsafe { + BNRepositoryManagerAddRepository( + self.as_raw(), + url.as_ref().as_ptr() as *const ffi::c_char, + repo_path.as_ref().as_ptr() as *const ffi::c_char, + ) + } + } + + pub fn repository_by_path(&self, path: P) -> Repository { + let path = path.into_bytes_with_nul(); + let result = unsafe { + BNRepositoryGetRepositoryByPath( + self.as_raw(), + path.as_ref().as_ptr() as *const ffi::c_char, + ) + }; + unsafe { Repository::from_raw(ptr::NonNull::new(result).unwrap()) } + } + + /// Gets the default Repository + pub fn default_repository(&self) -> Repository { + let result = unsafe { BNRepositoryManagerGetDefaultRepository(self.as_raw()) }; + assert!(!result.is_null()); + // NOTE result is not onwed, we need to clone it + let default = unsafe { Repository::ref_from_raw(&result) }; + default.clone() + } +} + +#[repr(transparent)] +pub struct Repository { + handle: ptr::NonNull, +} + +impl Drop for Repository { + fn drop(&mut self) { + unsafe { BNFreeRepository(self.as_raw()) } + } +} + +impl Clone for Repository { + fn clone(&self) -> Self { + unsafe { + Self::from_raw(ptr::NonNull::new(BNNewRepositoryReference(self.as_raw())).unwrap()) + } + } +} + +impl Repository { + pub(crate) unsafe fn from_raw(handle: ptr::NonNull) -> Self { + Self { handle } + } + + pub(crate) unsafe fn ref_from_raw(handle: &*mut BNRepository) -> &Self { + assert!(!handle.is_null()); + mem::transmute(handle) + } + + #[allow(clippy::mut_from_ref)] + pub(crate) unsafe fn as_raw(&self) -> &mut BNRepository { + &mut *self.handle.as_ptr() + } + + /// String URL of the git repository where the plugin repository's are stored + pub fn url(&self) -> BnString { + let result = unsafe { BNRepositoryGetUrl(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut ffi::c_char) } + } + + /// String local path to store the given plugin repository + pub fn path(&self) -> BnString { + let result = unsafe { BNRepositoryGetRepoPath(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut ffi::c_char) } + } + + /// List of RepoPlugin objects contained within this repository + pub fn plugins(&self) -> Array { + let mut count = 0; + let result = unsafe { BNRepositoryGetPlugins(self.as_raw(), &mut count) }; + assert!(!result.is_null()); + unsafe { Array::new(result, count, ()) } + } + + pub fn plugin_by_path(&self, path: S) -> Option { + let path = path.into_bytes_with_nul(); + let result = unsafe { + BNRepositoryGetPluginByPath(self.as_raw(), path.as_ref().as_ptr() as *const ffi::c_char) + }; + ptr::NonNull::new(result).map(|h| unsafe { RepoPlugin::from_raw(h) }) + } + + /// String full path the repository + pub fn full_path(&self) -> BnString { + let result = unsafe { BNRepositoryGetPluginsPath(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut ffi::c_char) } + } +} + +impl CoreArrayProvider for Repository { + type Raw = *mut BNRepository; + type Context = (); + type Wrapped<'a> = &'a Self; +} + +unsafe impl CoreArrayProviderInner for Repository { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeRepositoryManagerRepositoriesList(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::ref_from_raw(raw) + } +} + +#[repr(transparent)] +pub struct RepoPlugin { + handle: ptr::NonNull, +} + +impl Drop for RepoPlugin { + fn drop(&mut self) { + unsafe { BNFreePlugin(self.as_raw()) } + } +} + +impl Clone for RepoPlugin { + fn clone(&self) -> Self { + unsafe { Self::from_raw(ptr::NonNull::new(BNNewPluginReference(self.as_raw())).unwrap()) } + } +} + +impl RepoPlugin { + pub(crate) unsafe fn from_raw(handle: ptr::NonNull) -> Self { + Self { handle } + } + + pub(crate) unsafe fn ref_from_raw(handle: &*mut BNRepoPlugin) -> &Self { + assert!(!handle.is_null()); + mem::transmute(handle) + } + + #[allow(clippy::mut_from_ref)] + pub(crate) unsafe fn as_raw(&self) -> &mut BNRepoPlugin { + &mut *self.handle.as_ptr() + } + + /// String indicating the API used by the plugin + pub fn apis(&self) -> Array { + let mut count = 0; + let result = unsafe { BNPluginGetApis(self.as_raw(), &mut count) }; + assert!(!result.is_null()); + unsafe { Array::new(result, count, ()) } + } + + /// String of the plugin author + pub fn author(&self) -> BnString { + let result = unsafe { BNPluginGetAuthor(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut ffi::c_char) } + } + + /// String short description of the plugin + pub fn description(&self) -> BnString { + let result = unsafe { BNPluginGetDescription(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut ffi::c_char) } + } + + /// String complete license text for the given plugin + pub fn license_text(&self) -> BnString { + let result = unsafe { BNPluginGetLicenseText(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut ffi::c_char) } + } + + /// String long description of the plugin + pub fn long_description(&self) -> BnString { + let result = unsafe { BNPluginGetLongdescription(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut ffi::c_char) } + } + + /// Minimum version info the plugin was tested on + pub fn minimum_version_info(&self) -> VersionInfo { + let result = unsafe { BNPluginGetMinimumVersionInfo(self.as_raw()) }; + unsafe { VersionInfo::from_raw(result) } + } + + /// Maximum version info the plugin will support + pub fn maximum_version_info(&self) -> VersionInfo { + let result = unsafe { BNPluginGetMaximumVersionInfo(self.as_raw()) }; + unsafe { VersionInfo::from_raw(result) } + } + + /// String plugin name + pub fn name(&self) -> BnString { + let result = unsafe { BNPluginGetName(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut ffi::c_char) } + } + + /// String URL of the plugin's git repository + pub fn project_url(&self) -> BnString { + let result = unsafe { BNPluginGetProjectUrl(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut ffi::c_char) } + } + + /// String URL of the plugin's git repository + pub fn package_url(&self) -> BnString { + let result = unsafe { BNPluginGetPackageUrl(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut ffi::c_char) } + } + + /// String URL of the plugin author's url + pub fn author_url(&self) -> BnString { + let result = unsafe { BNPluginGetAuthorUrl(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut ffi::c_char) } + } + /// String version of the plugin + pub fn version(&self) -> BnString { + let result = unsafe { BNPluginGetVersion(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut i8) } + } + + /// String of the commit of this plugin git repository + pub fn commit(&self) -> BnString { + let result = unsafe { BNPluginGetCommit(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut i8) } + } + + /// Relative path from the base of the repository to the actual plugin + pub fn path(&self) -> BnString { + let result = unsafe { BNPluginGetPath(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut i8) } + } + + /// Optional sub-directory the plugin code lives in as a relative path from the plugin root + pub fn subdir(&self) -> BnString { + let result = unsafe { BNPluginGetSubdir(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut i8) } + } + + /// Dependencies required for installing this plugin + pub fn dependencies(&self) -> BnString { + let result = unsafe { BNPluginGetDependencies(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut i8) } + } + + /// true if the plugin is installed, false otherwise + pub fn is_installed(&self) -> bool { + unsafe { BNPluginIsInstalled(self.as_raw()) } + } + + /// true if the plugin is enabled, false otherwise + pub fn is_enabled(&self) -> bool { + unsafe { BNPluginIsEnabled(self.as_raw()) } + } + + pub fn status(&self) -> PluginStatus { + unsafe { BNPluginGetPluginStatus(self.as_raw()) } + } + + /// List of PluginType enumeration objects indicating the plugin type(s) + pub fn types(&self) -> Array { + let mut count = 0; + let result = unsafe { BNPluginGetPluginTypes(self.as_raw(), &mut count) }; + assert!(!result.is_null()); + unsafe { Array::new(result, count, ()) } + } + + /// Enable this plugin, optionally trying to force it. + /// Force loading a plugin with ignore platform and api constraints. + pub fn enable(&self, force: bool) -> bool { + unsafe { BNPluginEnable(self.as_raw(), force) } + } + + pub fn disable(&self) -> bool { + unsafe { BNPluginDisable(self.as_raw()) } + } + + /// Attempt to install the given plugin + pub fn install(&self) -> bool { + unsafe { BNPluginInstall(self.as_raw()) } + } + + pub fn install_dependencies(&self) -> bool { + unsafe { BNPluginInstallDependencies(self.as_raw()) } + } + + /// Attempt to uninstall the given plugin + pub fn uninstall(&self) -> bool { + unsafe { BNPluginUninstall(self.as_raw()) } + } + + pub fn updated(&self) -> bool { + unsafe { BNPluginUpdate(self.as_raw()) } + } + + /// List of platforms this plugin can execute on + pub fn platforms(&self) -> Array { + let mut count = 0; + let result = unsafe { BNPluginGetPlatforms(self.as_raw(), &mut count) }; + assert!(!result.is_null()); + unsafe { Array::new(result, count, ()) } + } + + pub fn repository(&self) -> BnString { + let result = unsafe { BNPluginGetRepository(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result as *mut i8) } + } + + /// Boolean status indicating that the plugin is being deleted + pub fn is_being_deleted(&self) -> bool { + unsafe { BNPluginIsBeingDeleted(self.as_raw()) } + } + + /// Boolean status indicating that the plugin is being updated + pub fn is_being_updated(&self) -> bool { + unsafe { BNPluginIsBeingUpdated(self.as_raw()) } + } + + /// Boolean status indicating that the plugin is currently running + pub fn is_running(&self) -> bool { + unsafe { BNPluginIsRunning(self.as_raw()) } + } + + /// Boolean status indicating that the plugin has updates will be installed after the next restart + pub fn is_update_pending(&self) -> bool { + unsafe { BNPluginIsUpdatePending(self.as_raw()) } + } + + /// Boolean status indicating that the plugin will be disabled after the next restart + pub fn is_disable_pending(&self) -> bool { + unsafe { BNPluginIsDisablePending(self.as_raw()) } + } + + /// Boolean status indicating that the plugin will be deleted after the next restart + pub fn is_delete_pending(&self) -> bool { + unsafe { BNPluginIsDeletePending(self.as_raw()) } + } + + /// Boolean status indicating that the plugin has updates available + pub fn is_updated_available(&self) -> bool { + unsafe { BNPluginIsUpdateAvailable(self.as_raw()) } + } + + /// Boolean status indicating that the plugin's dependencies are currently being installed + pub fn are_dependencies_being_installed(&self) -> bool { + unsafe { BNPluginAreDependenciesBeingInstalled(self.as_raw()) } + } + + /// Gets a json object of the project data field + pub fn project_data(&self) -> BnString { + let result = unsafe { BNPluginGetProjectData(self.as_raw()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result) } + } + + /// Returns a datetime object representing the plugins last update + pub fn last_update(&self) -> SystemTime { + let result = unsafe { BNPluginGetLastUpdate(self.as_raw()) }; + UNIX_EPOCH + Duration::from_secs(result) + } +} + +impl CoreArrayProvider for RepoPlugin { + type Raw = *mut BNRepoPlugin; + type Context = (); + type Wrapped<'a> = &'a Self; +} + +unsafe impl CoreArrayProviderInner for RepoPlugin { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeRepositoryPluginList(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::ref_from_raw(raw) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[repr(C)] +pub struct VersionInfo { + pub major: u32, + pub minor: u32, + pub build: u32, + pub channel: BnString, +} + +impl cmp::PartialOrd for VersionInfo { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl cmp::Ord for VersionInfo { + fn cmp(&self, other: &Self) -> cmp::Ordering { + if self == other { + return cmp::Ordering::Equal; + } + if unsafe { BNVersionLessThan(*self.as_raw(), *other.as_raw()) } { + cmp::Ordering::Less + } else { + cmp::Ordering::Greater + } + } +} + +impl VersionInfo { + pub(crate) unsafe fn from_raw(value: BNVersionInfo) -> Self { + assert!(!value.channel.is_null()); + Self { + major: value.major, + minor: value.minor, + build: value.build, + channel: BnString::from_raw(value.channel), + } + } + + pub(crate) unsafe fn as_raw(&self) -> &BNVersionInfo { + mem::transmute(self) + } + + pub fn parser_version_string(string: S) -> Self { + let string = string.into_bytes_with_nul(); + let result = + unsafe { BNParseVersionString(string.as_ref().as_ptr() as *const ffi::c_char) }; + unsafe { Self::from_raw(result) } + } +} + +impl CoreArrayProvider for PluginType { + type Raw = BNPluginType; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for PluginType { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreePluginTypes(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + *raw + } +} + +pub struct PluginPlatforms; +impl CoreArrayProvider for PluginPlatforms { + type Raw = *mut ffi::c_char; + type Context = (); + type Wrapped<'a> = &'a ffi::CStr; +} + +unsafe impl CoreArrayProviderInner for PluginPlatforms { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreePluginPlatforms(raw, count) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + ffi::CStr::from_ptr(*raw) + } +} + +pub struct PluginDirectorys; +impl CoreArrayProvider for PluginDirectorys { + type Raw = *mut ffi::c_char; + type Context = (); + type Wrapped<'a> = &'a ffi::CStr; +} + +unsafe impl CoreArrayProviderInner for PluginDirectorys { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNRepositoryFreePluginDirectoryList(raw, count) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + ffi::CStr::from_ptr(*raw) + } +} From c8dc23b3033325673951e1f96e7a19265d824bef Mon Sep 17 00:00:00 2001 From: rbran Date: Wed, 5 Feb 2025 16:22:42 +0000 Subject: [PATCH 2/4] make rust Repository impl Ref --- rust/src/repository.rs | 108 ++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 49 deletions(-) diff --git a/rust/src/repository.rs b/rust/src/repository.rs index c77380e5c2..51554f8e3b 100644 --- a/rust/src/repository.rs +++ b/rust/src/repository.rs @@ -3,7 +3,7 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use binaryninjacore_sys::*; -use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner}; +use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::string::{BnStrCompatible, BnString}; pub type PluginType = BNPluginType; @@ -16,32 +16,34 @@ pub struct RepositoryManager { handle: ptr::NonNull, } -impl Drop for RepositoryManager { - fn drop(&mut self) { - unsafe { BNFreeRepositoryManager(self.as_raw()) } +impl ToOwned for RepositoryManager { + type Owned = Ref; + + fn to_owned(&self) -> Self::Owned { + unsafe { ::inc_ref(self) } } } -impl Clone for RepositoryManager { - fn clone(&self) -> Self { - unsafe { - Self::from_raw( - ptr::NonNull::new(BNNewRepositoryManagerReference(self.as_raw())).unwrap(), - ) - } +unsafe impl RefCountable for RepositoryManager { + unsafe fn inc_ref(handle: &Self) -> crate::rc::Ref { + Self::ref_from_raw( + ptr::NonNull::new(BNNewRepositoryManagerReference(handle.as_raw())).unwrap(), + ) } -} -impl Default for RepositoryManager { - fn default() -> Self { - let result = unsafe { BNGetRepositoryManager() }; - unsafe { Self::from_raw(ptr::NonNull::new(result).unwrap()) } + unsafe fn dec_ref(handle: &Self) { + BNFreeRepositoryManager(handle.as_raw()) } } impl RepositoryManager { - pub(crate) unsafe fn from_raw(handle: ptr::NonNull) -> Self { - Self { handle } + pub fn default() -> Ref { + let result = unsafe { BNGetRepositoryManager() }; + unsafe { Self::ref_from_raw(ptr::NonNull::new(result).unwrap()) } + } + + pub(crate) unsafe fn ref_from_raw(handle: ptr::NonNull) -> Ref { + Ref::new(Self { handle }) } #[allow(clippy::mut_from_ref)] @@ -49,12 +51,12 @@ impl RepositoryManager { &mut *self.handle.as_ptr() } - pub fn new(plugins_path: S) -> Self { + pub fn new(plugins_path: S) -> Ref { let plugins_path = plugins_path.into_bytes_with_nul(); let result = unsafe { BNCreateRepositoryManager(plugins_path.as_ref().as_ptr() as *const ffi::c_char) }; - unsafe { Self::from_raw(ptr::NonNull::new(result).unwrap()) } + unsafe { Self::ref_from_raw(ptr::NonNull::new(result).unwrap()) } } /// Check for updates for all managed Repository objects @@ -109,12 +111,12 @@ impl RepositoryManager { } /// Gets the default Repository - pub fn default_repository(&self) -> Repository { + pub fn default_repository(&self) -> Ref { let result = unsafe { BNRepositoryManagerGetDefaultRepository(self.as_raw()) }; assert!(!result.is_null()); // NOTE result is not onwed, we need to clone it - let default = unsafe { Repository::ref_from_raw(&result) }; - default.clone() + let default = unsafe { Repository::from_raw(ptr::NonNull::new(result).unwrap()) }; + default.to_owned() } } @@ -123,17 +125,21 @@ pub struct Repository { handle: ptr::NonNull, } -impl Drop for Repository { - fn drop(&mut self) { - unsafe { BNFreeRepository(self.as_raw()) } +impl ToOwned for Repository { + type Owned = Ref; + + fn to_owned(&self) -> Self::Owned { + unsafe { ::inc_ref(self) } } } -impl Clone for Repository { - fn clone(&self) -> Self { - unsafe { - Self::from_raw(ptr::NonNull::new(BNNewRepositoryReference(self.as_raw())).unwrap()) - } +unsafe impl RefCountable for Repository { + unsafe fn inc_ref(handle: &Self) -> Ref { + Self::ref_from_raw(ptr::NonNull::new(BNNewRepositoryReference(handle.as_raw())).unwrap()) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeRepository(handle.as_raw()) } } @@ -142,9 +148,8 @@ impl Repository { Self { handle } } - pub(crate) unsafe fn ref_from_raw(handle: &*mut BNRepository) -> &Self { - assert!(!handle.is_null()); - mem::transmute(handle) + pub(crate) unsafe fn ref_from_raw(handle: ptr::NonNull) -> Ref { + Ref::new(Self { handle }) } #[allow(clippy::mut_from_ref)] @@ -193,7 +198,7 @@ impl Repository { impl CoreArrayProvider for Repository { type Raw = *mut BNRepository; type Context = (); - type Wrapped<'a> = &'a Self; + type Wrapped<'a> = Guard<'a, Self>; } unsafe impl CoreArrayProviderInner for Repository { @@ -201,8 +206,8 @@ unsafe impl CoreArrayProviderInner for Repository { BNFreeRepositoryManagerRepositoriesList(raw) } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::ref_from_raw(raw) + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { + Guard::new(Self::from_raw(ptr::NonNull::new(*raw).unwrap()), context) } } @@ -211,15 +216,21 @@ pub struct RepoPlugin { handle: ptr::NonNull, } -impl Drop for RepoPlugin { - fn drop(&mut self) { - unsafe { BNFreePlugin(self.as_raw()) } +impl ToOwned for RepoPlugin { + type Owned = Ref; + + fn to_owned(&self) -> Self::Owned { + unsafe { ::inc_ref(self) } } } -impl Clone for RepoPlugin { - fn clone(&self) -> Self { - unsafe { Self::from_raw(ptr::NonNull::new(BNNewPluginReference(self.as_raw())).unwrap()) } +unsafe impl RefCountable for RepoPlugin { + unsafe fn inc_ref(handle: &Self) -> Ref { + Self::ref_from_raw(ptr::NonNull::new(BNNewPluginReference(handle.as_raw())).unwrap()) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreePlugin(handle.as_raw()) } } @@ -228,9 +239,8 @@ impl RepoPlugin { Self { handle } } - pub(crate) unsafe fn ref_from_raw(handle: &*mut BNRepoPlugin) -> &Self { - assert!(!handle.is_null()); - mem::transmute(handle) + pub(crate) unsafe fn ref_from_raw(handle: ptr::NonNull) -> Ref { + Ref::new(Self { handle }) } #[allow(clippy::mut_from_ref)] @@ -469,7 +479,7 @@ impl RepoPlugin { impl CoreArrayProvider for RepoPlugin { type Raw = *mut BNRepoPlugin; type Context = (); - type Wrapped<'a> = &'a Self; + type Wrapped<'a> = Guard<'a, Self>; } unsafe impl CoreArrayProviderInner for RepoPlugin { @@ -477,8 +487,8 @@ unsafe impl CoreArrayProviderInner for RepoPlugin { BNFreeRepositoryPluginList(raw) } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::ref_from_raw(raw) + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { + Guard::new(Self::from_raw(ptr::NonNull::new(*raw).unwrap()), context) } } From 0e1b6cc59269727fb684d223c93e5c80541582ae Mon Sep 17 00:00:00 2001 From: rbran Date: Wed, 5 Feb 2025 16:59:16 +0000 Subject: [PATCH 3/4] add tests for rust Repository --- rust/src/repository.rs | 6 +++--- rust/tests/repository.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 rust/tests/repository.rs diff --git a/rust/src/repository.rs b/rust/src/repository.rs index 51554f8e3b..2ecd036b5e 100644 --- a/rust/src/repository.rs +++ b/rust/src/repository.rs @@ -80,7 +80,7 @@ impl RepositoryManager { /// Before you can query plugin metadata from a repository, you need to call [RepositoryManager::check_for_updates]. /// /// * `url` - URL to the plugins.json containing the records for this repository - /// * `repopath` - path to where the repository will be stored on disk locally + /// * `repository_path` - path to where the repository will be stored on disk locally /// /// Returns true if the repository was successfully added, false otherwise. pub fn add_repository( @@ -99,7 +99,7 @@ impl RepositoryManager { } } - pub fn repository_by_path(&self, path: P) -> Repository { + pub fn repository_by_path(&self, path: P) -> Option { let path = path.into_bytes_with_nul(); let result = unsafe { BNRepositoryGetRepositoryByPath( @@ -107,7 +107,7 @@ impl RepositoryManager { path.as_ref().as_ptr() as *const ffi::c_char, ) }; - unsafe { Repository::from_raw(ptr::NonNull::new(result).unwrap()) } + ptr::NonNull::new(result).map(|raw| unsafe { Repository::from_raw(raw) }) } /// Gets the default Repository diff --git a/rust/tests/repository.rs b/rust/tests/repository.rs new file mode 100644 index 0000000000..6047bcaebe --- /dev/null +++ b/rust/tests/repository.rs @@ -0,0 +1,30 @@ +use binaryninja::headless::Session; +use binaryninja::repository::RepositoryManager; +use rstest::*; + +#[fixture] +fn session() -> Session { + Session::new().expect("Failed to initialize session") +} + +#[rstest] +fn test_list(_session: Session) { + let manager = RepositoryManager::default(); + let repositories = manager.repositories(); + for repository in &repositories { + let repo_path = repository.path(); + let repository_by_path = manager.repository_by_path(repo_path).unwrap(); + assert_eq!(repository.url(), repository_by_path.url()); + } + + let repository = manager.default_repository(); + let _full_path = repository.full_path(); + let _path = repository.path(); + let _url = repository.url(); + let plugins = repository.plugins(); + for plugin in &plugins { + let plugin_path = plugin.path(); + let plugin_by_path = repository.plugin_by_path(plugin_path).unwrap(); + assert_eq!(plugin.package_url(), plugin_by_path.package_url()); + } +} From ba9913faa7903056f6513c5d069be53c16bc24af Mon Sep 17 00:00:00 2001 From: rbran Date: Thu, 6 Feb 2025 11:40:28 +0000 Subject: [PATCH 4/4] fix typo --- rust/src/repository.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/src/repository.rs b/rust/src/repository.rs index 2ecd036b5e..420b9b23ae 100644 --- a/rust/src/repository.rs +++ b/rust/src/repository.rs @@ -114,7 +114,7 @@ impl RepositoryManager { pub fn default_repository(&self) -> Ref { let result = unsafe { BNRepositoryManagerGetDefaultRepository(self.as_raw()) }; assert!(!result.is_null()); - // NOTE result is not onwed, we need to clone it + // NOTE result is not owned, we need to clone it let default = unsafe { Repository::from_raw(ptr::NonNull::new(result).unwrap()) }; default.to_owned() } @@ -576,14 +576,14 @@ unsafe impl CoreArrayProviderInner for PluginPlatforms { } } -pub struct PluginDirectorys; -impl CoreArrayProvider for PluginDirectorys { +pub struct PluginDirectories; +impl CoreArrayProvider for PluginDirectories { type Raw = *mut ffi::c_char; type Context = (); type Wrapped<'a> = &'a ffi::CStr; } -unsafe impl CoreArrayProviderInner for PluginDirectorys { +unsafe impl CoreArrayProviderInner for PluginDirectories { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNRepositoryFreePluginDirectoryList(raw, count) }