1pub mod kvs;
2pub mod snapshot;
3pub mod undo;
4
5use binaryninjacore_sys::*;
6use std::collections::HashMap;
7use std::ffi::c_void;
8use std::fmt::Debug;
9use std::ptr::NonNull;
10
11use crate::binary_view::BinaryView;
12use crate::data_buffer::DataBuffer;
13use crate::database::kvs::KeyValueStore;
14use crate::database::snapshot::{Snapshot, SnapshotId};
15use crate::file_metadata::FileMetadata;
16use crate::progress::{NoProgressCallback, ProgressCallback};
17use crate::rc::{Array, Ref, RefCountable};
18use crate::string::{BnString, IntoCStr};
19
20pub struct Database {
21 pub(crate) handle: NonNull<BNDatabase>,
22}
23
24impl Database {
25 pub(crate) unsafe fn from_raw(handle: NonNull<BNDatabase>) -> Self {
26 Self { handle }
27 }
28
29 pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNDatabase>) -> Ref<Self> {
30 Ref::new(Self { handle })
31 }
32
33 pub fn snapshot_by_id(&self, id: SnapshotId) -> Option<Ref<Snapshot>> {
35 let result = unsafe { BNGetDatabaseSnapshot(self.handle.as_ptr(), id.0) };
36 NonNull::new(result).map(|handle| unsafe { Snapshot::ref_from_raw(handle) })
37 }
38
39 pub fn snapshots(&self) -> Array<Snapshot> {
41 let mut count = 0;
42 let result = unsafe { BNGetDatabaseSnapshots(self.handle.as_ptr(), &mut count) };
43 assert!(!result.is_null());
44 unsafe { Array::new(result, count, ()) }
45 }
46
47 pub fn current_snapshot(&self) -> Option<Ref<Snapshot>> {
49 let result = unsafe { BNGetDatabaseCurrentSnapshot(self.handle.as_ptr()) };
50 NonNull::new(result).map(|handle| unsafe { Snapshot::ref_from_raw(handle) })
51 }
52
53 pub fn set_current_snapshot(&self, value: &Snapshot) {
55 self.set_current_snapshot_id(value.id())
56 }
57
58 pub fn set_current_snapshot_id(&self, id: SnapshotId) {
62 unsafe { BNSetDatabaseCurrentSnapshot(self.handle.as_ptr(), id.0) }
63 }
64
65 pub fn write_snapshot_data(
66 &self,
67 parents: &[SnapshotId],
68 file: &BinaryView,
69 name: &str,
70 data: &KeyValueStore,
71 auto_save: bool,
72 ) -> SnapshotId {
73 self.write_snapshot_data_with_progress(
74 parents,
75 file,
76 name,
77 data,
78 auto_save,
79 NoProgressCallback,
80 )
81 }
82
83 pub fn write_snapshot_data_with_progress<P>(
84 &self,
85 parents: &[SnapshotId],
86 file: &BinaryView,
87 name: &str,
88 data: &KeyValueStore,
89 auto_save: bool,
90 mut progress: P,
91 ) -> SnapshotId
92 where
93 P: ProgressCallback,
94 {
95 let name_raw = name.to_cstr();
96 let name_ptr = name_raw.as_ptr();
97
98 let new_id = unsafe {
99 BNWriteDatabaseSnapshotData(
100 self.handle.as_ptr(),
101 parents.as_ptr() as *mut _,
103 parents.len(),
104 file.handle,
105 name_ptr,
106 data.handle.as_ptr(),
107 auto_save,
108 &mut progress as *mut P as *mut c_void,
109 Some(P::cb_progress_callback),
110 )
111 };
112
113 SnapshotId(new_id)
114 }
115
116 pub fn trim_snapshot(&self, id: SnapshotId) -> Result<(), ()> {
120 if unsafe { BNTrimDatabaseSnapshot(self.handle.as_ptr(), id.0) } {
121 Ok(())
122 } else {
123 Err(())
124 }
125 }
126
127 pub fn remove_snapshot(&self, id: SnapshotId) -> Result<(), ()> {
130 if unsafe { BNRemoveDatabaseSnapshot(self.handle.as_ptr(), id.0) } {
131 Ok(())
132 } else {
133 Err(())
134 }
135 }
136 pub fn has_global(&self, key: &str) -> bool {
137 let key_raw = key.to_cstr();
138 unsafe { BNDatabaseHasGlobal(self.handle.as_ptr(), key_raw.as_ptr()) != 0 }
139 }
140
141 pub fn global_keys(&self) -> Array<BnString> {
143 let mut count = 0;
144 let result = unsafe { BNGetDatabaseGlobalKeys(self.handle.as_ptr(), &mut count) };
145 assert!(!result.is_null());
146 unsafe { Array::new(result, count, ()) }
147 }
148
149 pub fn globals(&self) -> HashMap<String, BnString> {
151 self.global_keys()
152 .iter()
153 .filter_map(|key| Some((key.to_string(), self.read_global(key)?)))
154 .collect()
155 }
156
157 pub fn read_global(&self, key: &str) -> Option<BnString> {
159 let key_raw = key.to_cstr();
160 let result = unsafe { BNReadDatabaseGlobal(self.handle.as_ptr(), key_raw.as_ptr()) };
161 unsafe { NonNull::new(result).map(|_| BnString::from_raw(result)) }
162 }
163
164 pub fn write_global(&self, key: &str, value: &str) -> bool {
166 let key_raw = key.to_cstr();
167 let value_raw = value.to_cstr();
168 unsafe { BNWriteDatabaseGlobal(self.handle.as_ptr(), key_raw.as_ptr(), value_raw.as_ptr()) }
169 }
170
171 pub fn read_global_data(&self, key: &str) -> Option<DataBuffer> {
173 let key_raw = key.to_cstr();
174 let result = unsafe { BNReadDatabaseGlobalData(self.handle.as_ptr(), key_raw.as_ptr()) };
175 NonNull::new(result).map(|_| DataBuffer::from_raw(result))
176 }
177
178 pub fn write_global_data(&self, key: &str, value: &DataBuffer) -> bool {
180 let key_raw = key.to_cstr();
181 unsafe { BNWriteDatabaseGlobalData(self.handle.as_ptr(), key_raw.as_ptr(), value.as_raw()) }
182 }
183
184 pub fn file(&self) -> Ref<FileMetadata> {
186 let result = unsafe { BNGetDatabaseFile(self.handle.as_ptr()) };
187 assert!(!result.is_null());
188 FileMetadata::ref_from_raw(result)
189 }
190
191 pub fn analysis_cache(&self) -> Ref<KeyValueStore> {
193 let result = unsafe { BNReadDatabaseAnalysisCache(self.handle.as_ptr()) };
194 unsafe { KeyValueStore::ref_from_raw(NonNull::new(result).unwrap()) }
195 }
196
197 pub fn reload_connection(&self) {
199 unsafe { BNDatabaseReloadConnection(self.handle.as_ptr()) }
200 }
201
202 pub fn write_analysis_cache(&self, val: &KeyValueStore) -> Result<(), ()> {
203 if unsafe { BNWriteDatabaseAnalysisCache(self.handle.as_ptr(), val.handle.as_ptr()) } {
204 Ok(())
205 } else {
206 Err(())
207 }
208 }
209
210 pub fn snapshot_has_data(&self, id: SnapshotId) -> bool {
211 unsafe { BNSnapshotHasData(self.handle.as_ptr(), id.0) }
212 }
213}
214
215impl Debug for Database {
216 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217 f.debug_struct("Database")
218 .field("current_snapshot", &self.current_snapshot())
219 .field("snapshot_count", &self.snapshots().len())
220 .field("globals", &self.globals())
221 .field("analysis_cache", &self.analysis_cache())
222 .finish()
223 }
224}
225
226impl ToOwned for Database {
227 type Owned = Ref<Self>;
228
229 fn to_owned(&self) -> Self::Owned {
230 unsafe { RefCountable::inc_ref(self) }
231 }
232}
233
234unsafe impl RefCountable for Database {
235 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
236 Ref::new(Self {
237 handle: NonNull::new(BNNewDatabaseReference(handle.handle.as_ptr())).unwrap(),
238 })
239 }
240
241 unsafe fn dec_ref(handle: &Self) {
242 BNFreeDatabase(handle.handle.as_ptr());
243 }
244}