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::path::Path;
10use std::ptr::NonNull;
11
12use crate::binary_view::BinaryView;
13use crate::data_buffer::DataBuffer;
14use crate::database::kvs::KeyValueStore;
15use crate::database::snapshot::{Snapshot, SnapshotId};
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 open_existing(path: impl AsRef<Path>) -> Result<Ref<Self>, ()> {
35 let db = unsafe { Self::ref_from_raw(NonNull::new(BNCreateDatabaseInstance()).ok_or(())?) };
36 let path_raw = path.as_ref().to_cstr();
37 if unsafe { BNDatabaseOpenExisting(db.handle.as_ptr(), path_raw.as_ptr()) } {
38 Ok(db)
39 } else {
40 Err(())
41 }
42 }
43
44 pub fn snapshot_by_id(&self, id: SnapshotId) -> Option<Ref<Snapshot>> {
46 let result = unsafe { BNGetDatabaseSnapshot(self.handle.as_ptr(), id.0) };
47 NonNull::new(result).map(|handle| unsafe { Snapshot::ref_from_raw(handle) })
48 }
49
50 pub fn snapshots(&self) -> Array<Snapshot> {
52 let mut count = 0;
53 let result = unsafe { BNGetDatabaseSnapshots(self.handle.as_ptr(), &mut count) };
54 assert!(!result.is_null());
55 unsafe { Array::new(result, count, ()) }
56 }
57
58 pub fn current_snapshot(&self) -> Option<Ref<Snapshot>> {
60 let result = unsafe { BNGetDatabaseCurrentSnapshot(self.handle.as_ptr()) };
61 NonNull::new(result).map(|handle| unsafe { Snapshot::ref_from_raw(handle) })
62 }
63
64 pub fn set_current_snapshot(&self, value: &Snapshot) {
66 self.set_current_snapshot_id(value.id())
67 }
68
69 pub fn set_current_snapshot_id(&self, id: SnapshotId) {
73 unsafe { BNSetDatabaseCurrentSnapshot(self.handle.as_ptr(), id.0) }
74 }
75
76 pub fn write_snapshot_data(
77 &self,
78 parents: &[SnapshotId],
79 file: &BinaryView,
80 name: &str,
81 data: &KeyValueStore,
82 auto_save: bool,
83 ) -> SnapshotId {
84 self.write_snapshot_data_with_progress(
85 parents,
86 file,
87 name,
88 data,
89 auto_save,
90 NoProgressCallback,
91 )
92 }
93
94 pub fn write_snapshot_data_with_progress<P>(
95 &self,
96 parents: &[SnapshotId],
97 file: &BinaryView,
98 name: &str,
99 data: &KeyValueStore,
100 auto_save: bool,
101 mut progress: P,
102 ) -> SnapshotId
103 where
104 P: ProgressCallback,
105 {
106 let name_raw = name.to_cstr();
107 let name_ptr = name_raw.as_ptr();
108
109 let new_id = unsafe {
110 BNWriteDatabaseSnapshotData(
111 self.handle.as_ptr(),
112 parents.as_ptr() as *mut _,
114 parents.len(),
115 file.handle,
116 name_ptr,
117 data.handle.as_ptr(),
118 auto_save,
119 &mut progress as *mut P as *mut c_void,
120 Some(P::cb_progress_callback),
121 )
122 };
123
124 SnapshotId(new_id)
125 }
126
127 pub fn trim_snapshot(&self, id: SnapshotId) -> Result<(), ()> {
131 if unsafe { BNTrimDatabaseSnapshot(self.handle.as_ptr(), id.0) } {
132 Ok(())
133 } else {
134 Err(())
135 }
136 }
137
138 pub fn remove_snapshot(&self, id: SnapshotId) -> Result<(), ()> {
141 if unsafe { BNRemoveDatabaseSnapshot(self.handle.as_ptr(), id.0) } {
142 Ok(())
143 } else {
144 Err(())
145 }
146 }
147 pub fn has_global(&self, key: &str) -> bool {
148 let key_raw = key.to_cstr();
149 unsafe { BNDatabaseHasGlobal(self.handle.as_ptr(), key_raw.as_ptr()) != 0 }
150 }
151
152 pub fn global_keys(&self) -> Array<BnString> {
154 let mut count = 0;
155 let result = unsafe { BNGetDatabaseGlobalKeys(self.handle.as_ptr(), &mut count) };
156 assert!(!result.is_null());
157 unsafe { Array::new(result, count, ()) }
158 }
159
160 pub fn globals(&self) -> HashMap<String, BnString> {
162 self.global_keys()
163 .iter()
164 .filter_map(|key| Some((key.to_string(), self.read_global(key)?)))
165 .collect()
166 }
167
168 pub fn read_global(&self, key: &str) -> Option<BnString> {
170 let key_raw = key.to_cstr();
171 let result = unsafe { BNReadDatabaseGlobal(self.handle.as_ptr(), key_raw.as_ptr()) };
172 unsafe { NonNull::new(result).map(|_| BnString::from_raw(result)) }
173 }
174
175 pub fn write_global(&self, key: &str, value: &str) -> bool {
177 let key_raw = key.to_cstr();
178 let value_raw = value.to_cstr();
179 unsafe { BNWriteDatabaseGlobal(self.handle.as_ptr(), key_raw.as_ptr(), value_raw.as_ptr()) }
180 }
181
182 pub fn read_global_data(&self, key: &str) -> Option<DataBuffer> {
184 let key_raw = key.to_cstr();
185 let result = unsafe { BNReadDatabaseGlobalData(self.handle.as_ptr(), key_raw.as_ptr()) };
186 NonNull::new(result).map(|_| DataBuffer::from_raw(result))
187 }
188
189 pub fn write_global_data(&self, key: &str, value: &DataBuffer) -> bool {
191 let key_raw = key.to_cstr();
192 unsafe { BNWriteDatabaseGlobalData(self.handle.as_ptr(), key_raw.as_ptr(), value.as_raw()) }
193 }
194
195 pub fn analysis_cache(&self) -> Ref<KeyValueStore> {
197 let result = unsafe { BNReadDatabaseAnalysisCache(self.handle.as_ptr()) };
198 unsafe { KeyValueStore::ref_from_raw(NonNull::new(result).unwrap()) }
199 }
200
201 #[deprecated(note = "Use crate::file_metadata::FileMetadata::reopen_moved_database instead")]
202 pub fn reload_connection(&self) {}
204
205 pub fn write_analysis_cache(&self, val: &KeyValueStore) -> Result<(), ()> {
206 if unsafe { BNWriteDatabaseAnalysisCache(self.handle.as_ptr(), val.handle.as_ptr()) } {
207 Ok(())
208 } else {
209 Err(())
210 }
211 }
212
213 pub fn snapshot_has_data(&self, id: SnapshotId) -> bool {
214 unsafe { BNSnapshotHasData(self.handle.as_ptr(), id.0) }
215 }
216}
217
218impl Debug for Database {
219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220 f.debug_struct("Database")
221 .field("current_snapshot", &self.current_snapshot())
222 .field("snapshot_count", &self.snapshots().len())
223 .field("globals", &self.globals())
224 .field("analysis_cache", &self.analysis_cache())
225 .finish()
226 }
227}
228
229impl ToOwned for Database {
230 type Owned = Ref<Self>;
231
232 fn to_owned(&self) -> Self::Owned {
233 unsafe { RefCountable::inc_ref(self) }
234 }
235}
236
237unsafe impl RefCountable for Database {
238 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
239 Ref::new(Self {
240 handle: NonNull::new(BNNewDatabaseReference(handle.handle.as_ptr())).unwrap(),
241 })
242 }
243
244 unsafe fn dec_ref(handle: &Self) {
245 BNFreeDatabase(handle.handle.as_ptr());
246 }
247}