binaryninja/database/
snapshot.rs

1use crate::data_buffer::DataBuffer;
2use crate::database::kvs::KeyValueStore;
3use crate::database::undo::UndoEntry;
4use crate::database::Database;
5use crate::progress::ProgressCallback;
6use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
7use crate::string::{BnString, IntoCStr};
8use binaryninjacore_sys::{
9    BNCollaborationFreeSnapshotIdList, BNFreeSnapshot, BNFreeSnapshotList, BNGetSnapshotChildren,
10    BNGetSnapshotDatabase, BNGetSnapshotFileContents, BNGetSnapshotFileContentsHash,
11    BNGetSnapshotFirstParent, BNGetSnapshotId, BNGetSnapshotName, BNGetSnapshotParents,
12    BNGetSnapshotUndoData, BNGetSnapshotUndoEntries, BNGetSnapshotUndoEntriesWithProgress,
13    BNIsSnapshotAutoSave, BNNewSnapshotReference, BNReadSnapshotData,
14    BNReadSnapshotDataWithProgress, BNSetSnapshotName, BNSnapshot, BNSnapshotHasAncestor,
15    BNSnapshotHasContents, BNSnapshotHasUndo, BNSnapshotStoreData,
16};
17use std::ffi::c_void;
18use std::fmt;
19use std::fmt::{Debug, Display, Formatter};
20use std::ptr::NonNull;
21
22pub struct Snapshot {
23    pub(crate) handle: NonNull<BNSnapshot>,
24}
25
26impl Snapshot {
27    pub(crate) unsafe fn from_raw(handle: NonNull<BNSnapshot>) -> Self {
28        Self { handle }
29    }
30
31    pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNSnapshot>) -> Ref<Self> {
32        Ref::new(Self { handle })
33    }
34
35    /// Get the owning database
36    pub fn database(&self) -> Database {
37        unsafe {
38            Database::from_raw(NonNull::new(BNGetSnapshotDatabase(self.handle.as_ptr())).unwrap())
39        }
40    }
41
42    /// Get the numerical id
43    pub fn id(&self) -> SnapshotId {
44        SnapshotId(unsafe { BNGetSnapshotId(self.handle.as_ptr()) })
45    }
46
47    /// Get the displayed snapshot name
48    pub fn name(&self) -> String {
49        unsafe { BnString::into_string(BNGetSnapshotName(self.handle.as_ptr())) }
50    }
51
52    /// Set the displayed snapshot name
53    pub fn set_name(&self, value: &str) {
54        let value_raw = value.to_cstr();
55        let value_ptr = value_raw.as_ptr();
56        unsafe { BNSetSnapshotName(self.handle.as_ptr(), value_ptr) }
57    }
58
59    /// If the snapshot was the result of an auto-save
60    pub fn is_auto_save(&self) -> bool {
61        unsafe { BNIsSnapshotAutoSave(self.handle.as_ptr()) }
62    }
63
64    /// If the snapshot has contents, and has not been trimmed
65    pub fn has_contents(&self) -> bool {
66        unsafe { BNSnapshotHasContents(self.handle.as_ptr()) }
67    }
68
69    /// If the snapshot has undo data
70    pub fn has_undo(&self) -> bool {
71        unsafe { BNSnapshotHasUndo(self.handle.as_ptr()) }
72    }
73
74    /// Get the first parent of the snapshot, or None if it has no parents
75    pub fn first_parent(&self) -> Option<Snapshot> {
76        let result = unsafe { BNGetSnapshotFirstParent(self.handle.as_ptr()) };
77        NonNull::new(result).map(|s| unsafe { Snapshot::from_raw(s) })
78    }
79
80    /// Get a list of all parent snapshots of the snapshot
81    pub fn parents(&self) -> Array<Snapshot> {
82        let mut count = 0;
83        let result = unsafe { BNGetSnapshotParents(self.handle.as_ptr(), &mut count) };
84        assert!(!result.is_null());
85        unsafe { Array::new(result, count, ()) }
86    }
87
88    /// Get a list of all child snapshots of the snapshot
89    pub fn children(&self) -> Array<Snapshot> {
90        let mut count = 0;
91        let result = unsafe { BNGetSnapshotChildren(self.handle.as_ptr(), &mut count) };
92        assert!(!result.is_null());
93        unsafe { Array::new(result, count, ()) }
94    }
95
96    /// Get a buffer of the raw data at the time of the snapshot
97    pub fn file_contents(&self) -> Option<DataBuffer> {
98        self.has_contents().then(|| unsafe {
99            let result = BNGetSnapshotFileContents(self.handle.as_ptr());
100            assert!(!result.is_null());
101            DataBuffer::from_raw(result)
102        })
103    }
104
105    /// Get a hash of the data at the time of the snapshot
106    pub fn file_contents_hash(&self) -> Option<DataBuffer> {
107        self.has_contents().then(|| unsafe {
108            let result = BNGetSnapshotFileContentsHash(self.handle.as_ptr());
109            assert!(!result.is_null());
110            DataBuffer::from_raw(result)
111        })
112    }
113
114    /// Get a list of undo entries at the time of the snapshot
115    pub fn undo_entries(&self) -> Array<UndoEntry> {
116        assert!(self.has_undo());
117        let mut count = 0;
118        let result = unsafe { BNGetSnapshotUndoEntries(self.handle.as_ptr(), &mut count) };
119        assert!(!result.is_null());
120        unsafe { Array::new(result, count, ()) }
121    }
122
123    pub fn undo_entries_with_progress<P: ProgressCallback>(
124        &self,
125        mut progress: P,
126    ) -> Array<UndoEntry> {
127        assert!(self.has_undo());
128        let mut count = 0;
129
130        let result = unsafe {
131            BNGetSnapshotUndoEntriesWithProgress(
132                self.handle.as_ptr(),
133                &mut progress as *mut P as *mut c_void,
134                Some(P::cb_progress_callback),
135                &mut count,
136            )
137        };
138
139        assert!(!result.is_null());
140        unsafe { Array::new(result, count, ()) }
141    }
142
143    /// Get the backing kvs data with snapshot fields
144    pub fn read_data(&self) -> Ref<KeyValueStore> {
145        let result = unsafe { BNReadSnapshotData(self.handle.as_ptr()) };
146        unsafe { KeyValueStore::ref_from_raw(NonNull::new(result).unwrap()) }
147    }
148
149    pub fn read_data_with_progress<P: ProgressCallback>(
150        &self,
151        mut progress: P,
152    ) -> Ref<KeyValueStore> {
153        let result = unsafe {
154            BNReadSnapshotDataWithProgress(
155                self.handle.as_ptr(),
156                &mut progress as *mut P as *mut c_void,
157                Some(P::cb_progress_callback),
158            )
159        };
160
161        unsafe { KeyValueStore::ref_from_raw(NonNull::new(result).unwrap()) }
162    }
163
164    pub fn undo_data(&self) -> DataBuffer {
165        let result = unsafe { BNGetSnapshotUndoData(self.handle.as_ptr()) };
166        assert!(!result.is_null());
167        DataBuffer::from_raw(result)
168    }
169
170    pub fn store_data(&self, data: &KeyValueStore) -> bool {
171        unsafe {
172            BNSnapshotStoreData(
173                self.handle.as_ptr(),
174                data.handle.as_ptr(),
175                std::ptr::null_mut(),
176                None,
177            )
178        }
179    }
180
181    pub fn store_data_with_progress<P: ProgressCallback>(
182        &self,
183        data: &KeyValueStore,
184        mut progress: P,
185    ) -> bool {
186        unsafe {
187            BNSnapshotStoreData(
188                self.handle.as_ptr(),
189                data.handle.as_ptr(),
190                &mut progress as *mut P as *mut c_void,
191                Some(P::cb_progress_callback),
192            )
193        }
194    }
195
196    /// Determine if this snapshot has another as an ancestor
197    pub fn has_ancestor(self, other: &Snapshot) -> bool {
198        unsafe { BNSnapshotHasAncestor(self.handle.as_ptr(), other.handle.as_ptr()) }
199    }
200}
201
202impl Debug for Snapshot {
203    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204        f.debug_struct("Snapshot")
205            .field("id", &self.id())
206            .field("name", &self.name())
207            .field("is_auto_save", &self.is_auto_save())
208            .field("has_contents", &self.has_contents())
209            .field("has_undo", &self.has_undo())
210            // TODO: This might be too much.
211            .field("children", &self.children().to_vec())
212            .finish()
213    }
214}
215
216impl ToOwned for Snapshot {
217    type Owned = Ref<Self>;
218
219    fn to_owned(&self) -> Self::Owned {
220        unsafe { RefCountable::inc_ref(self) }
221    }
222}
223
224unsafe impl RefCountable for Snapshot {
225    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
226        Ref::new(Self {
227            handle: NonNull::new(BNNewSnapshotReference(handle.handle.as_ptr())).unwrap(),
228        })
229    }
230
231    unsafe fn dec_ref(handle: &Self) {
232        BNFreeSnapshot(handle.handle.as_ptr());
233    }
234}
235
236impl CoreArrayProvider for Snapshot {
237    type Raw = *mut BNSnapshot;
238    type Context = ();
239    type Wrapped<'a> = Guard<'a, Snapshot>;
240}
241
242unsafe impl CoreArrayProviderInner for Snapshot {
243    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
244        BNFreeSnapshotList(raw, count);
245    }
246
247    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
248        let raw_ptr = NonNull::new(*raw).unwrap();
249        Guard::new(Self::from_raw(raw_ptr), context)
250    }
251}
252
253#[repr(transparent)]
254#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
255pub struct SnapshotId(pub i64);
256
257impl Display for SnapshotId {
258    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
259        f.write_fmt(format_args!("{}", self.0))
260    }
261}
262
263impl CoreArrayProvider for SnapshotId {
264    type Raw = i64;
265    type Context = ();
266    type Wrapped<'a> = SnapshotId;
267}
268
269unsafe impl CoreArrayProviderInner for SnapshotId {
270    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
271        BNCollaborationFreeSnapshotIdList(raw, count)
272    }
273
274    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
275        SnapshotId(*raw)
276    }
277}