binaryninja/
file_metadata.rs1use crate::binary_view::BinaryView;
16use crate::database::Database;
17use crate::rc::*;
18use crate::string::*;
19use binaryninjacore_sys::*;
20use binaryninjacore_sys::{BNCreateDatabaseWithProgress, BNOpenExistingDatabaseWithProgress};
21use std::ffi::c_void;
22use std::fmt::{Debug, Display, Formatter};
23use std::path::Path;
24
25use crate::progress::ProgressCallback;
26use crate::project::file::ProjectFile;
27use std::ptr::{self, NonNull};
28
29new_id_type!(SessionId, usize);
30
31#[derive(PartialEq, Eq, Hash)]
32pub struct FileMetadata {
33 pub(crate) handle: *mut BNFileMetadata,
34}
35
36impl FileMetadata {
37 pub(crate) fn from_raw(handle: *mut BNFileMetadata) -> Self {
38 Self { handle }
39 }
40
41 pub(crate) fn ref_from_raw(handle: *mut BNFileMetadata) -> Ref<Self> {
42 unsafe { Ref::new(Self { handle }) }
43 }
44
45 pub fn new() -> Ref<Self> {
46 Self::ref_from_raw(unsafe { BNCreateFileMetadata() })
47 }
48
49 pub fn with_filename(name: &str) -> Ref<Self> {
50 let ret = FileMetadata::new();
51 ret.set_filename(name);
52 ret
53 }
54
55 pub fn close(&self) {
56 unsafe {
57 BNCloseFile(self.handle);
58 }
59 }
60
61 pub fn session_id(&self) -> SessionId {
62 let raw = unsafe { BNFileMetadataGetSessionId(self.handle) };
63 SessionId(raw)
64 }
65
66 pub fn filename(&self) -> String {
67 unsafe {
68 let raw = BNGetFilename(self.handle);
69 BnString::into_string(raw)
70 }
71 }
72
73 pub fn set_filename(&self, name: &str) {
74 let name = name.to_cstr();
75
76 unsafe {
77 BNSetFilename(self.handle, name.as_ptr());
78 }
79 }
80
81 pub fn modified(&self) -> bool {
82 unsafe { BNIsFileModified(self.handle) }
83 }
84
85 pub fn mark_modified(&self) {
86 unsafe {
87 BNMarkFileModified(self.handle);
88 }
89 }
90
91 pub fn mark_saved(&self) {
92 unsafe {
93 BNMarkFileSaved(self.handle);
94 }
95 }
96
97 pub fn is_analysis_changed(&self) -> bool {
98 unsafe { BNIsAnalysisChanged(self.handle) }
99 }
100
101 pub fn is_database_backed(&self) -> bool {
102 self.is_database_backed_for_view_type("")
103 }
104
105 pub fn is_database_backed_for_view_type(&self, view_type: &str) -> bool {
106 let view_type = view_type.to_cstr();
107
108 unsafe { BNIsBackedByDatabase(self.handle, view_type.as_ref().as_ptr() as *const _) }
109 }
110
111 pub fn run_undoable_transaction<F: FnOnce() -> Result<T, E>, T, E>(
122 &self,
123 func: F,
124 ) -> Result<T, E> {
125 let undo = self.begin_undo_actions(false);
126 let result = func();
127 match result {
128 Ok(t) => {
129 self.commit_undo_actions(&undo);
130 Ok(t)
131 }
132 Err(e) => {
133 self.revert_undo_actions(&undo);
134 Err(e)
135 }
136 }
137 }
138
139 pub fn begin_undo_actions(&self, anonymous_allowed: bool) -> String {
147 unsafe { BnString::into_string(BNBeginUndoActions(self.handle, anonymous_allowed)) }
148 }
149
150 pub fn commit_undo_actions(&self, id: &str) {
158 let id = id.to_cstr();
159 unsafe {
160 BNCommitUndoActions(self.handle, id.as_ref().as_ptr() as *const _);
161 }
162 }
163
164 pub fn revert_undo_actions(&self, id: &str) {
172 let id = id.to_cstr();
173 unsafe {
174 BNRevertUndoActions(self.handle, id.as_ref().as_ptr() as *const _);
175 }
176 }
177
178 pub fn forget_undo_actions(&self, id: &str) {
186 let id = id.to_cstr();
187 unsafe {
188 BNForgetUndoActions(self.handle, id.as_ref().as_ptr() as *const _);
189 }
190 }
191
192 pub fn undo(&self) {
193 unsafe {
194 BNUndo(self.handle);
195 }
196 }
197
198 pub fn redo(&self) {
199 unsafe {
200 BNRedo(self.handle);
201 }
202 }
203
204 pub fn current_view(&self) -> String {
205 unsafe { BnString::into_string(BNGetCurrentView(self.handle)) }
206 }
207
208 pub fn current_offset(&self) -> u64 {
209 unsafe { BNGetCurrentOffset(self.handle) }
210 }
211
212 pub fn navigate_to(&self, view: &str, offset: u64) -> Result<(), ()> {
222 let view = view.to_cstr();
223
224 unsafe {
225 if BNNavigate(self.handle, view.as_ref().as_ptr() as *const _, offset) {
226 Ok(())
227 } else {
228 Err(())
229 }
230 }
231 }
232
233 pub fn view_of_type(&self, view: &str) -> Option<Ref<BinaryView>> {
243 let view = view.to_cstr();
244
245 unsafe {
246 let raw_view_ptr = BNGetFileViewOfType(self.handle, view.as_ref().as_ptr() as *const _);
247 match raw_view_ptr.is_null() {
248 false => Some(BinaryView::ref_from_raw(raw_view_ptr)),
249 true => None,
250 }
251 }
252 }
253
254 pub fn view_types(&self) -> Array<BnString> {
255 let mut count = 0;
256 unsafe {
257 let types = BNGetExistingViews(self.handle, &mut count);
258 Array::new(types, count, ())
259 }
260 }
261
262 pub fn project_file(&self) -> Option<Ref<ProjectFile>> {
264 unsafe {
265 let res = NonNull::new(BNGetProjectFile(self.handle))?;
266 Some(ProjectFile::ref_from_raw(res))
267 }
268 }
269
270 pub fn create_database(&self, file_path: impl AsRef<Path>) -> bool {
271 let Some(raw_view) = self.view_of_type("Raw") else {
273 return false;
274 };
275
276 let file_path = file_path.as_ref().to_cstr();
277 unsafe {
278 BNCreateDatabase(
279 raw_view.handle,
280 file_path.as_ptr() as *mut _,
281 ptr::null_mut(),
282 )
283 }
284 }
285
286 pub fn create_database_with_progress<P: ProgressCallback>(
288 &self,
289 file_path: impl AsRef<Path>,
290 mut progress: P,
291 ) -> bool {
292 let Some(raw_view) = self.view_of_type("Raw") else {
294 return false;
295 };
296 let file_path = file_path.as_ref().to_cstr();
297 unsafe {
298 BNCreateDatabaseWithProgress(
299 raw_view.handle,
300 file_path.as_ptr() as *mut _,
301 &mut progress as *mut P as *mut c_void,
302 Some(P::cb_progress_callback),
303 ptr::null_mut(),
304 )
305 }
306 }
307
308 pub fn save_auto_snapshot(&self) -> bool {
309 let Some(raw_view) = self.view_of_type("Raw") else {
311 return false;
312 };
313
314 unsafe { BNSaveAutoSnapshot(raw_view.handle, ptr::null_mut() as *mut _) }
315 }
316
317 pub fn open_database_for_configuration(&self, file: &Path) -> Result<Ref<BinaryView>, ()> {
318 let file = file.to_cstr();
319 unsafe {
320 let bv =
321 BNOpenDatabaseForConfiguration(self.handle, file.as_ref().as_ptr() as *const _);
322
323 if bv.is_null() {
324 Err(())
325 } else {
326 Ok(BinaryView::ref_from_raw(bv))
327 }
328 }
329 }
330
331 pub fn open_database(&self, file: &Path) -> Result<Ref<BinaryView>, ()> {
332 let file = file.to_cstr();
333 let view = unsafe { BNOpenExistingDatabase(self.handle, file.as_ptr()) };
334
335 if view.is_null() {
336 Err(())
337 } else {
338 Ok(unsafe { BinaryView::ref_from_raw(view) })
339 }
340 }
341
342 pub fn open_database_with_progress<P: ProgressCallback>(
343 &self,
344 file: &Path,
345 mut progress: P,
346 ) -> Result<Ref<BinaryView>, ()> {
347 let file = file.to_cstr();
348
349 let view = unsafe {
350 BNOpenExistingDatabaseWithProgress(
351 self.handle,
352 file.as_ptr(),
353 &mut progress as *mut P as *mut c_void,
354 Some(P::cb_progress_callback),
355 )
356 };
357
358 if view.is_null() {
359 Err(())
360 } else {
361 Ok(unsafe { BinaryView::ref_from_raw(view) })
362 }
363 }
364
365 pub fn database(&self) -> Option<Ref<Database>> {
367 let result = unsafe { BNGetFileMetadataDatabase(self.handle) };
368 NonNull::new(result).map(|handle| unsafe { Database::ref_from_raw(handle) })
369 }
370}
371
372impl Debug for FileMetadata {
373 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
374 f.debug_struct("FileMetadata")
375 .field("filename", &self.filename())
376 .field("session_id", &self.session_id())
377 .field("modified", &self.modified())
378 .field("is_analysis_changed", &self.is_analysis_changed())
379 .field("current_view_type", &self.current_view())
380 .field("current_offset", &self.current_offset())
381 .field("view_types", &self.view_types().to_vec())
382 .finish()
383 }
384}
385
386impl Display for FileMetadata {
387 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
388 f.write_str(&self.filename())
389 }
390}
391
392unsafe impl Send for FileMetadata {}
393unsafe impl Sync for FileMetadata {}
394
395impl ToOwned for FileMetadata {
396 type Owned = Ref<Self>;
397
398 fn to_owned(&self) -> Self::Owned {
399 unsafe { RefCountable::inc_ref(self) }
400 }
401}
402
403unsafe impl RefCountable for FileMetadata {
404 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
405 Ref::new(Self {
406 handle: BNNewFileReference(handle.handle),
407 })
408 }
409
410 unsafe fn dec_ref(handle: &Self) {
411 BNFreeFileMetadata(handle.handle);
412 }
413}