binaryninja/
database.rs

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    /// Get a snapshot by its id, or None if no snapshot with that id exists
34    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    /// Get a list of all snapshots in the database
40    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    /// Get the current snapshot
48    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    /// Equivalent to [`Self::set_current_snapshot_id`].
54    pub fn set_current_snapshot(&self, value: &Snapshot) {
55        self.set_current_snapshot_id(value.id())
56    }
57
58    /// Sets the current snapshot to the [`SnapshotId`].
59    ///
60    /// **No** validation is done to ensure that the id is valid.
61    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                // SAFETY: SnapshotId is just i64
102                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    /// Trim a snapshot's contents in the database by id, but leave the parent/child
117    /// hierarchy intact. Future references to this snapshot will return False for has_contents
118    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    /// Remove a snapshot in the database by id, deleting its contents and references.
127    /// Attempting to remove a snapshot with children will raise an exception.
128    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    /// Get a list of keys for all globals in the database
141    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    /// Get a dictionary of all globals
149    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    /// Get a specific global by key
157    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    /// Write a global into the database
164    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    /// Get a specific global by key, as a binary buffer
171    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    /// Write a binary buffer into a global in the database
178    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    /// Get the owning FileMetadata
184    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    /// Get the backing analysis cache kvs
191    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}