Skip to content

Commit fe3ed76

Browse files
rbranemesare
authored andcommitted
Implement Rust Repository
1 parent 4075371 commit fe3ed76

File tree

5 files changed

+642
-3
lines changed

5 files changed

+642
-3
lines changed

rust/src/lib.rs

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub mod references;
7070
pub mod relocation;
7171
pub mod render_layer;
7272
pub mod secrets_provider;
73+
pub mod repository;
7374
pub mod section;
7475
pub mod segment;
7576
pub mod settings;
@@ -95,6 +96,7 @@ use binaryninjacore_sys::*;
9596
use metadata::Metadata;
9697
use metadata::MetadataType;
9798
use rc::Ref;
99+
use std::cmp;
98100
use std::collections::HashMap;
99101
use std::ffi::{c_char, c_void, CStr};
100102
use std::path::{Path, PathBuf};
@@ -103,6 +105,7 @@ use string::BnString;
103105
use string::IntoJson;
104106

105107
use crate::progress::{NoProgressCallback, ProgressCallback};
108+
use crate::string::raw_to_string;
106109
pub use binaryninjacore_sys::BNBranchType as BranchType;
107110
pub use binaryninjacore_sys::BNDataFlowQueryOption as DataFlowQueryOption;
108111
pub use binaryninjacore_sys::BNEndianness as Endianness;
@@ -436,7 +439,7 @@ pub fn build_id() -> u32 {
436439
unsafe { BNGetBuildId() }
437440
}
438441

439-
#[derive(Clone, PartialEq, Eq, Hash)]
442+
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
440443
pub struct VersionInfo {
441444
pub major: u32,
442445
pub minor: u32,
@@ -445,12 +448,59 @@ pub struct VersionInfo {
445448
}
446449

447450
impl VersionInfo {
448-
pub(crate) fn from_owned_raw(value: BNVersionInfo) -> Self {
451+
pub(crate) fn from_raw(value: &BNVersionInfo) -> Self {
449452
Self {
450453
major: value.major,
451454
minor: value.minor,
452455
build: value.build,
453-
channel: unsafe { BnString::from_raw(value.channel) }.to_string(),
456+
// NOTE: Because of plugin manager the channel might not be filled.
457+
channel: raw_to_string(value.channel).unwrap_or_default(),
458+
}
459+
}
460+
461+
pub(crate) fn from_owned_raw(value: BNVersionInfo) -> Self {
462+
let owned = Self::from_raw(&value);
463+
Self::free_raw(value);
464+
owned
465+
}
466+
467+
pub(crate) fn into_owned_raw(value: &Self) -> BNVersionInfo {
468+
BNVersionInfo {
469+
major: value.major,
470+
minor: value.minor,
471+
build: value.build,
472+
channel: value.channel.as_ptr() as *mut c_char,
473+
}
474+
}
475+
476+
pub(crate) fn free_raw(value: BNVersionInfo) {
477+
let _ = unsafe { BnString::from_raw(value.channel) };
478+
}
479+
480+
pub fn from_string<S: BnStrCompatible>(string: S) -> Self {
481+
let string = string.into_bytes_with_nul();
482+
let result = unsafe { BNParseVersionString(string.as_ref().as_ptr() as *const c_char) };
483+
Self::from_owned_raw(result)
484+
}
485+
}
486+
487+
impl PartialOrd for VersionInfo {
488+
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
489+
Some(self.cmp(other))
490+
}
491+
}
492+
493+
impl Ord for VersionInfo {
494+
fn cmp(&self, other: &Self) -> cmp::Ordering {
495+
if self == other {
496+
return cmp::Ordering::Equal;
497+
}
498+
let bn_version_0 = VersionInfo::into_owned_raw(self);
499+
let bn_version_1 = VersionInfo::into_owned_raw(other);
500+
if unsafe { BNVersionLessThan(bn_version_0, bn_version_1) } {
501+
cmp::Ordering::Less
502+
} else {
503+
cmp::Ordering::Greater
454504
}
455505
}
456506
}

rust/src/repository.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
mod manager;
2+
mod plugin;
3+
4+
use std::ffi::c_char;
5+
use std::fmt::Debug;
6+
use std::ptr::NonNull;
7+
8+
use binaryninjacore_sys::*;
9+
10+
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
11+
use crate::repository::plugin::RepositoryPlugin;
12+
use crate::string::{BnStrCompatible, BnString};
13+
14+
pub use manager::RepositoryManager;
15+
16+
pub type PluginType = BNPluginType;
17+
pub type PluginStatus = BNPluginStatus;
18+
19+
#[repr(transparent)]
20+
pub struct Repository {
21+
handle: NonNull<BNRepository>,
22+
}
23+
24+
impl Repository {
25+
pub(crate) unsafe fn from_raw(handle: NonNull<BNRepository>) -> Self {
26+
Self { handle }
27+
}
28+
29+
pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNRepository>) -> Ref<Self> {
30+
Ref::new(Self { handle })
31+
}
32+
33+
/// String URL of the git repository where the plugin repository's are stored
34+
pub fn url(&self) -> BnString {
35+
let result = unsafe { BNRepositoryGetUrl(self.handle.as_ptr()) };
36+
assert!(!result.is_null());
37+
unsafe { BnString::from_raw(result as *mut c_char) }
38+
}
39+
40+
/// String local path to store the given plugin repository
41+
pub fn path(&self) -> BnString {
42+
let result = unsafe { BNRepositoryGetRepoPath(self.handle.as_ptr()) };
43+
assert!(!result.is_null());
44+
unsafe { BnString::from_raw(result as *mut c_char) }
45+
}
46+
47+
/// List of RepoPlugin objects contained within this repository
48+
pub fn plugins(&self) -> Array<RepositoryPlugin> {
49+
let mut count = 0;
50+
let result = unsafe { BNRepositoryGetPlugins(self.handle.as_ptr(), &mut count) };
51+
assert!(!result.is_null());
52+
unsafe { Array::new(result, count, ()) }
53+
}
54+
55+
pub fn plugin_by_path<S: BnStrCompatible>(&self, path: S) -> Option<Ref<RepositoryPlugin>> {
56+
let path = path.into_bytes_with_nul();
57+
let result = unsafe {
58+
BNRepositoryGetPluginByPath(
59+
self.handle.as_ptr(),
60+
path.as_ref().as_ptr() as *const c_char,
61+
)
62+
};
63+
NonNull::new(result).map(|h| unsafe { RepositoryPlugin::ref_from_raw(h) })
64+
}
65+
66+
// TODO: Make this a PathBuf?
67+
/// String full path the repository
68+
pub fn full_path(&self) -> BnString {
69+
let result = unsafe { BNRepositoryGetPluginsPath(self.handle.as_ptr()) };
70+
assert!(!result.is_null());
71+
unsafe { BnString::from_raw(result as *mut c_char) }
72+
}
73+
}
74+
75+
impl Debug for Repository {
76+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77+
f.debug_struct("Repository")
78+
.field("url", &self.url())
79+
.field("path", &self.path())
80+
.field("full_path", &self.full_path())
81+
.field("plugins", &self.plugins().to_vec())
82+
.finish()
83+
}
84+
}
85+
86+
impl ToOwned for Repository {
87+
type Owned = Ref<Self>;
88+
89+
fn to_owned(&self) -> Self::Owned {
90+
unsafe { <Self as RefCountable>::inc_ref(self) }
91+
}
92+
}
93+
94+
unsafe impl RefCountable for Repository {
95+
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
96+
Self::ref_from_raw(NonNull::new(BNNewRepositoryReference(handle.handle.as_ptr())).unwrap())
97+
}
98+
99+
unsafe fn dec_ref(handle: &Self) {
100+
BNFreeRepository(handle.handle.as_ptr())
101+
}
102+
}
103+
104+
impl CoreArrayProvider for Repository {
105+
type Raw = *mut BNRepository;
106+
type Context = ();
107+
type Wrapped<'a> = Guard<'a, Self>;
108+
}
109+
110+
unsafe impl CoreArrayProviderInner for Repository {
111+
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
112+
BNFreeRepositoryManagerRepositoriesList(raw)
113+
}
114+
115+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
116+
Guard::new(Self::from_raw(NonNull::new(*raw).unwrap()), context)
117+
}
118+
}
119+
120+
impl CoreArrayProvider for PluginType {
121+
type Raw = BNPluginType;
122+
type Context = ();
123+
type Wrapped<'a> = Self;
124+
}
125+
126+
unsafe impl CoreArrayProviderInner for PluginType {
127+
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
128+
BNFreePluginTypes(raw)
129+
}
130+
131+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
132+
*raw
133+
}
134+
}

rust/src/repository/manager.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
use crate::rc::{Array, Ref, RefCountable};
2+
use crate::repository::Repository;
3+
use crate::string::BnStrCompatible;
4+
use binaryninjacore_sys::{
5+
BNCreateRepositoryManager, BNFreeRepositoryManager, BNGetRepositoryManager,
6+
BNNewRepositoryManagerReference, BNRepositoryGetRepositoryByPath, BNRepositoryManager,
7+
BNRepositoryManagerAddRepository, BNRepositoryManagerCheckForUpdates,
8+
BNRepositoryManagerGetDefaultRepository, BNRepositoryManagerGetRepositories,
9+
};
10+
use std::ffi::c_char;
11+
use std::fmt::Debug;
12+
use std::ptr::NonNull;
13+
14+
/// Keeps track of all the repositories and keeps the `enabled_plugins.json`
15+
/// file coherent with the plugins that are installed/uninstalled enabled/disabled
16+
#[repr(transparent)]
17+
pub struct RepositoryManager {
18+
handle: NonNull<BNRepositoryManager>,
19+
}
20+
21+
impl RepositoryManager {
22+
#[allow(clippy::should_implement_trait)]
23+
pub fn default() -> Ref<Self> {
24+
let result = unsafe { BNGetRepositoryManager() };
25+
unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) }
26+
}
27+
28+
pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNRepositoryManager>) -> Ref<Self> {
29+
Ref::new(Self { handle })
30+
}
31+
32+
pub fn new<S: BnStrCompatible>(plugins_path: S) -> Ref<Self> {
33+
let plugins_path = plugins_path.into_bytes_with_nul();
34+
let result =
35+
unsafe { BNCreateRepositoryManager(plugins_path.as_ref().as_ptr() as *const c_char) };
36+
unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) }
37+
}
38+
39+
/// Check for updates for all managed [`Repository`] objects
40+
pub fn check_for_updates(&self) -> bool {
41+
unsafe { BNRepositoryManagerCheckForUpdates(self.handle.as_ptr()) }
42+
}
43+
44+
/// List of [`Repository`] objects being managed
45+
pub fn repositories(&self) -> Array<Repository> {
46+
let mut count = 0;
47+
let result =
48+
unsafe { BNRepositoryManagerGetRepositories(self.handle.as_ptr(), &mut count) };
49+
assert!(!result.is_null());
50+
unsafe { Array::new(result, count, ()) }
51+
}
52+
53+
/// Adds a new plugin repository for the manager to track.
54+
///
55+
/// To remove a repository, restart Binary Ninja (and don't re-add the repository!).
56+
/// File artifacts will remain on disk under repositories/ file in the User Folder.
57+
///
58+
/// Before you can query plugin metadata from a repository, you need to call [`RepositoryManager::check_for_updates`].
59+
///
60+
/// * `url` - URL to the plugins.json containing the records for this repository
61+
/// * `repository_path` - path to where the repository will be stored on disk locally
62+
///
63+
/// Returns true if the repository was successfully added, false otherwise.
64+
pub fn add_repository<U: BnStrCompatible, P: BnStrCompatible>(
65+
&self,
66+
url: U,
67+
repository_path: P,
68+
) -> bool {
69+
let url = url.into_bytes_with_nul();
70+
let repo_path = repository_path.into_bytes_with_nul();
71+
unsafe {
72+
BNRepositoryManagerAddRepository(
73+
self.handle.as_ptr(),
74+
url.as_ref().as_ptr() as *const c_char,
75+
repo_path.as_ref().as_ptr() as *const c_char,
76+
)
77+
}
78+
}
79+
80+
pub fn repository_by_path<P: BnStrCompatible>(&self, path: P) -> Option<Repository> {
81+
let path = path.into_bytes_with_nul();
82+
let result = unsafe {
83+
BNRepositoryGetRepositoryByPath(
84+
self.handle.as_ptr(),
85+
path.as_ref().as_ptr() as *const c_char,
86+
)
87+
};
88+
NonNull::new(result).map(|raw| unsafe { Repository::from_raw(raw) })
89+
}
90+
91+
/// Gets the default [`Repository`]
92+
pub fn default_repository(&self) -> Ref<Repository> {
93+
let result = unsafe { BNRepositoryManagerGetDefaultRepository(self.handle.as_ptr()) };
94+
assert!(!result.is_null());
95+
unsafe { Repository::ref_from_raw(NonNull::new(result).unwrap()) }
96+
}
97+
}
98+
99+
impl Debug for RepositoryManager {
100+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101+
f.debug_struct("RepositoryManager")
102+
.field("repositories", &self.repositories().to_vec())
103+
.finish()
104+
}
105+
}
106+
107+
impl ToOwned for RepositoryManager {
108+
type Owned = Ref<Self>;
109+
110+
fn to_owned(&self) -> Self::Owned {
111+
unsafe { RefCountable::inc_ref(self) }
112+
}
113+
}
114+
115+
unsafe impl RefCountable for RepositoryManager {
116+
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
117+
Self::ref_from_raw(
118+
NonNull::new(BNNewRepositoryManagerReference(handle.handle.as_ptr())).unwrap(),
119+
)
120+
}
121+
122+
unsafe fn dec_ref(handle: &Self) {
123+
BNFreeRepositoryManager(handle.handle.as_ptr())
124+
}
125+
}

0 commit comments

Comments
 (0)