Skip to main content

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