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