diff options
Diffstat (limited to 'rust/src/file_metadata.rs')
| -rw-r--r-- | rust/src/file_metadata.rs | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/rust/src/file_metadata.rs b/rust/src/file_metadata.rs new file mode 100644 index 00000000..fbbadcda --- /dev/null +++ b/rust/src/file_metadata.rs @@ -0,0 +1,355 @@ +// Copyright 2021-2024 Vector 35 Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::binary_view::BinaryView; +use crate::database::Database; +use crate::rc::*; +use crate::string::*; +use binaryninjacore_sys::{ + BNBeginUndoActions, BNCloseFile, BNCommitUndoActions, BNCreateDatabase, BNCreateFileMetadata, + BNFileMetadata, BNFileMetadataGetSessionId, BNFreeFileMetadata, BNGetCurrentOffset, + BNGetCurrentView, BNGetExistingViews, BNGetFileMetadataDatabase, BNGetFileViewOfType, + BNGetFilename, BNGetProjectFile, BNIsAnalysisChanged, BNIsBackedByDatabase, BNIsFileModified, + BNMarkFileModified, BNMarkFileSaved, BNNavigate, BNNewFileReference, + BNOpenDatabaseForConfiguration, BNOpenExistingDatabase, BNRedo, BNRevertUndoActions, + BNSaveAutoSnapshot, BNSetFilename, BNUndo, +}; +use binaryninjacore_sys::{BNCreateDatabaseWithProgress, BNOpenExistingDatabaseWithProgress}; +use std::ffi::c_void; +use std::fmt::Debug; +use std::path::Path; + +use crate::progress::ProgressCallback; +use crate::project::file::ProjectFile; +use std::ptr::{self, NonNull}; + +#[derive(PartialEq, Eq, Hash)] +pub struct FileMetadata { + pub(crate) handle: *mut BNFileMetadata, +} + +impl FileMetadata { + pub(crate) fn from_raw(handle: *mut BNFileMetadata) -> Self { + Self { handle } + } + + pub(crate) fn ref_from_raw(handle: *mut BNFileMetadata) -> Ref<Self> { + unsafe { Ref::new(Self { handle }) } + } + + pub fn new() -> Ref<Self> { + Self::ref_from_raw(unsafe { BNCreateFileMetadata() }) + } + + pub fn with_filename<S: BnStrCompatible>(name: S) -> Ref<Self> { + let ret = FileMetadata::new(); + ret.set_filename(name); + ret + } + + pub fn close(&self) { + unsafe { + BNCloseFile(self.handle); + } + } + + pub fn session_id(&self) -> usize { + unsafe { BNFileMetadataGetSessionId(self.handle) } + } + + pub fn filename(&self) -> BnString { + unsafe { + let raw = BNGetFilename(self.handle); + BnString::from_raw(raw) + } + } + + pub fn set_filename<S: BnStrCompatible>(&self, name: S) { + let name = name.into_bytes_with_nul(); + + unsafe { + BNSetFilename(self.handle, name.as_ref().as_ptr() as *mut _); + } + } + + pub fn modified(&self) -> bool { + unsafe { BNIsFileModified(self.handle) } + } + + pub fn mark_modified(&self) { + unsafe { + BNMarkFileModified(self.handle); + } + } + + pub fn mark_saved(&self) { + unsafe { + BNMarkFileSaved(self.handle); + } + } + + pub fn is_analysis_changed(&self) -> bool { + unsafe { BNIsAnalysisChanged(self.handle) } + } + + pub fn is_database_backed(&self) -> bool { + self.is_database_backed_for_view_type("") + } + + pub fn is_database_backed_for_view_type<S: BnStrCompatible>(&self, view_type: S) -> bool { + let view_type = view_type.into_bytes_with_nul(); + + unsafe { BNIsBackedByDatabase(self.handle, view_type.as_ref().as_ptr() as *const _) } + } + + pub fn run_undoable_transaction<F: FnOnce() -> Result<T, E>, T, E>( + &self, + func: F, + ) -> Result<T, E> { + let undo = self.begin_undo_actions(false); + let result = func(); + match result { + Ok(t) => { + self.commit_undo_actions(undo); + Ok(t) + } + Err(e) => { + self.revert_undo_actions(undo); + Err(e) + } + } + } + + pub fn begin_undo_actions(&self, anonymous_allowed: bool) -> BnString { + unsafe { BnString::from_raw(BNBeginUndoActions(self.handle, anonymous_allowed)) } + } + + pub fn commit_undo_actions<S: BnStrCompatible>(&self, id: S) { + let id = id.into_bytes_with_nul(); + unsafe { + BNCommitUndoActions(self.handle, id.as_ref().as_ptr() as *const _); + } + } + + pub fn revert_undo_actions<S: BnStrCompatible>(&self, id: S) { + let id = id.into_bytes_with_nul(); + unsafe { + BNRevertUndoActions(self.handle, id.as_ref().as_ptr() as *const _); + } + } + + pub fn undo(&self) { + unsafe { + BNUndo(self.handle); + } + } + + pub fn redo(&self) { + unsafe { + BNRedo(self.handle); + } + } + + pub fn current_view(&self) -> BnString { + unsafe { BnString::from_raw(BNGetCurrentView(self.handle)) } + } + + pub fn current_offset(&self) -> u64 { + unsafe { BNGetCurrentOffset(self.handle) } + } + + pub fn navigate_to<S: BnStrCompatible>(&self, view: S, offset: u64) -> Result<(), ()> { + let view = view.into_bytes_with_nul(); + + unsafe { + if BNNavigate(self.handle, view.as_ref().as_ptr() as *const _, offset) { + Ok(()) + } else { + Err(()) + } + } + } + + pub fn view_of_type<S: BnStrCompatible>(&self, view: S) -> Option<Ref<BinaryView>> { + let view = view.into_bytes_with_nul(); + + unsafe { + let raw_view_ptr = BNGetFileViewOfType(self.handle, view.as_ref().as_ptr() as *const _); + match raw_view_ptr.is_null() { + false => Some(BinaryView::ref_from_raw(raw_view_ptr)), + true => None, + } + } + } + + pub fn view_types(&self) -> Array<BnString> { + let mut count = 0; + unsafe { + let types = BNGetExistingViews(self.handle, &mut count); + Array::new(types, count, ()) + } + } + + /// Get the [`ProjectFile`] for the [`FileMetadata`]. + pub fn project_file(&self) -> Option<Ref<ProjectFile>> { + unsafe { + let res = NonNull::new(BNGetProjectFile(self.handle))?; + Some(ProjectFile::ref_from_raw(res)) + } + } + + pub fn create_database(&self, file_path: impl AsRef<Path>) -> bool { + // Databases are created with the root view (Raw). + let Some(raw_view) = self.view_of_type("Raw") else { + return false; + }; + + let file_path = file_path.as_ref().into_bytes_with_nul(); + unsafe { + BNCreateDatabase( + raw_view.handle, + file_path.as_ptr() as *mut _, + ptr::null_mut(), + ) + } + } + + // TODO: Pass settings? + pub fn create_database_with_progress<S: BnStrCompatible, P: ProgressCallback>( + &self, + file_path: impl AsRef<Path>, + mut progress: P, + ) -> bool { + // Databases are created with the root view (Raw). + let Some(raw_view) = self.view_of_type("Raw") else { + return false; + }; + let file_path = file_path.as_ref().into_bytes_with_nul(); + unsafe { + BNCreateDatabaseWithProgress( + raw_view.handle, + file_path.as_ptr() as *mut _, + &mut progress as *mut P as *mut c_void, + Some(P::cb_progress_callback), + ptr::null_mut(), + ) + } + } + + pub fn save_auto_snapshot(&self) -> bool { + // Snapshots are saved with the root view (Raw). + let Some(raw_view) = self.view_of_type("Raw") else { + return false; + }; + + unsafe { BNSaveAutoSnapshot(raw_view.handle, ptr::null_mut() as *mut _) } + } + + pub fn open_database_for_configuration<S: BnStrCompatible>( + &self, + filename: S, + ) -> Result<Ref<BinaryView>, ()> { + let filename = filename.into_bytes_with_nul(); + unsafe { + let bv = + BNOpenDatabaseForConfiguration(self.handle, filename.as_ref().as_ptr() as *const _); + + if bv.is_null() { + Err(()) + } else { + Ok(BinaryView::ref_from_raw(bv)) + } + } + } + + pub fn open_database<S: BnStrCompatible>(&self, filename: S) -> Result<Ref<BinaryView>, ()> { + let filename = filename.into_bytes_with_nul(); + let filename_ptr = filename.as_ref().as_ptr() as *mut _; + + let view = unsafe { BNOpenExistingDatabase(self.handle, filename_ptr) }; + + if view.is_null() { + Err(()) + } else { + Ok(unsafe { BinaryView::ref_from_raw(view) }) + } + } + + pub fn open_database_with_progress<S: BnStrCompatible, P: ProgressCallback>( + &self, + filename: S, + mut progress: P, + ) -> Result<Ref<BinaryView>, ()> { + let filename = filename.into_bytes_with_nul(); + let filename_ptr = filename.as_ref().as_ptr() as *mut _; + + let view = unsafe { + BNOpenExistingDatabaseWithProgress( + self.handle, + filename_ptr, + &mut progress as *mut P as *mut c_void, + Some(P::cb_progress_callback), + ) + }; + + if view.is_null() { + Err(()) + } else { + Ok(unsafe { BinaryView::ref_from_raw(view) }) + } + } + + /// Get the current database + pub fn database(&self) -> Option<Ref<Database>> { + let result = unsafe { BNGetFileMetadataDatabase(self.handle) }; + NonNull::new(result).map(|handle| unsafe { Database::ref_from_raw(handle) }) + } +} + +impl Debug for FileMetadata { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FileMetadata") + .field("filename", &self.filename()) + .field("session_id", &self.session_id()) + .field("modified", &self.modified()) + .field("is_analysis_changed", &self.is_analysis_changed()) + .field("current_view_type", &self.current_view()) + .field("current_offset", &self.current_offset()) + .field("view_types", &self.view_types().to_vec()) + .finish() + } +} + +unsafe impl Send for FileMetadata {} +unsafe impl Sync for FileMetadata {} + +impl ToOwned for FileMetadata { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl RefCountable for FileMetadata { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + handle: BNNewFileReference(handle.handle), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeFileMetadata(handle.handle); + } +} |
