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<(), ()> {
119 if unsafe { BNTrimDatabaseSnapshot(self.handle.as_ptr(), id.0) } {
120 Ok(())
121 } else {
122 Err(())
123 }
124 }
125
126 pub fn remove_snapshot(&self, id: SnapshotId) -> Result<(), ()> {
129 if unsafe { BNRemoveDatabaseSnapshot(self.handle.as_ptr(), id.0) } {
130 Ok(())
131 } else {
132 Err(())
133 }
134 }
135 pub fn has_global(&self, key: &str) -> bool {
136 let key_raw = key.to_cstr();
137 unsafe { BNDatabaseHasGlobal(self.handle.as_ptr(), key_raw.as_ptr()) != 0 }
138 }
139
140 pub fn global_keys(&self) -> Array<BnString> {
142 let mut count = 0;
143 let result = unsafe { BNGetDatabaseGlobalKeys(self.handle.as_ptr(), &mut count) };
144 assert!(!result.is_null());
145 unsafe { Array::new(result, count, ()) }
146 }
147
148 pub fn globals(&self) -> HashMap<String, BnString> {
150 self.global_keys()
151 .iter()
152 .filter_map(|key| Some((key.to_string(), self.read_global(key)?)))
153 .collect()
154 }
155
156 pub fn read_global(&self, key: &str) -> Option<BnString> {
158 let key_raw = key.to_cstr();
159 let result = unsafe { BNReadDatabaseGlobal(self.handle.as_ptr(), key_raw.as_ptr()) };
160 unsafe { NonNull::new(result).map(|_| BnString::from_raw(result)) }
161 }
162
163 pub fn write_global(&self, key: &str, value: &str) -> bool {
165 let key_raw = key.to_cstr();
166 let value_raw = value.to_cstr();
167 unsafe { BNWriteDatabaseGlobal(self.handle.as_ptr(), key_raw.as_ptr(), value_raw.as_ptr()) }
168 }
169
170 pub fn read_global_data(&self, key: &str) -> Option<DataBuffer> {
172 let key_raw = key.to_cstr();
173 let result = unsafe { BNReadDatabaseGlobalData(self.handle.as_ptr(), key_raw.as_ptr()) };
174 NonNull::new(result).map(|_| DataBuffer::from_raw(result))
175 }
176
177 pub fn write_global_data(&self, key: &str, value: &DataBuffer) -> bool {
179 let key_raw = key.to_cstr();
180 unsafe { BNWriteDatabaseGlobalData(self.handle.as_ptr(), key_raw.as_ptr(), value.as_raw()) }
181 }
182
183 pub fn file(&self) -> Ref<FileMetadata> {
185 let result = unsafe { BNGetDatabaseFile(self.handle.as_ptr()) };
186 assert!(!result.is_null());
187 FileMetadata::ref_from_raw(result)
188 }
189
190 pub fn analysis_cache(&self) -> Ref<KeyValueStore> {
192 let result = unsafe { BNReadDatabaseAnalysisCache(self.handle.as_ptr()) };
193 unsafe { KeyValueStore::ref_from_raw(NonNull::new(result).unwrap()) }
194 }
195
196 pub fn reload_connection(&self) {
197 unsafe { BNDatabaseReloadConnection(self.handle.as_ptr()) }
198 }
199
200 pub fn write_analysis_cache(&self, val: &KeyValueStore) -> Result<(), ()> {
201 if unsafe { BNWriteDatabaseAnalysisCache(self.handle.as_ptr(), val.handle.as_ptr()) } {
202 Ok(())
203 } else {
204 Err(())
205 }
206 }
207
208 pub fn snapshot_has_data(&self, id: SnapshotId) -> bool {
209 unsafe { BNSnapshotHasData(self.handle.as_ptr(), id.0) }
210 }
211}
212
213impl Debug for Database {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 f.debug_struct("Database")
216 .field("current_snapshot", &self.current_snapshot())
217 .field("snapshot_count", &self.snapshots().len())
218 .field("globals", &self.globals())
219 .field("analysis_cache", &self.analysis_cache())
220 .finish()
221 }
222}
223
224impl ToOwned for Database {
225 type Owned = Ref<Self>;
226
227 fn to_owned(&self) -> Self::Owned {
228 unsafe { RefCountable::inc_ref(self) }
229 }
230}
231
232unsafe impl RefCountable for Database {
233 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
234 Ref::new(Self {
235 handle: NonNull::new(BNNewDatabaseReference(handle.handle.as_ptr())).unwrap(),
236 })
237 }
238
239 unsafe fn dec_ref(handle: &Self) {
240 BNFreeDatabase(handle.handle.as_ptr());
241 }
242}