summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2026-02-11 17:56:26 -0800
committerMason Reed <35282038+emesare@users.noreply.github.com>2026-02-23 00:09:44 -0800
commitc1dbea197a6ba7e3d008e01c8169f5c3702151ea (patch)
treec4dc5e203e7c04bd7ba1b105bd065073f05774a5
parentd61826e8fbe3580c762f7f317f69201bce3710ad (diff)
[Rust] Refactor `FileMetadata` file information
- Rename and retype `FileMetadata::filename` and make the assignment required to happen at time of construction. - Add `FileMetadata::display_name` which is only to be used for presentation purposes. - Add `FileMetadata::virtual_path` for containers. - Rename `FileMetadata::modified` to `FileMetadata::is_modified` to be more consistent across codebase. - Add some missing documentation. - Add `BinaryView::from_metadata` with accompanying documentation.
-rw-r--r--plugins/dwarf/dwarf_import/src/helpers.rs22
-rw-r--r--plugins/idb_import/src/lib.rs11
-rw-r--r--plugins/pdb-ng/src/lib.rs2
-rw-r--r--plugins/warp/src/cache.rs2
-rw-r--r--plugins/warp/src/plugin/create.rs6
-rw-r--r--rust/examples/decompile.rs2
-rw-r--r--rust/examples/disassemble.rs2
-rw-r--r--rust/examples/high_level_il.rs2
-rw-r--r--rust/examples/medium_level_il.rs2
-rw-r--r--rust/src/binary_view.rs19
-rw-r--r--rust/src/file_metadata.rs128
11 files changed, 158 insertions, 40 deletions
diff --git a/plugins/dwarf/dwarf_import/src/helpers.rs b/plugins/dwarf/dwarf_import/src/helpers.rs
index c7855b25..541fdc42 100644
--- a/plugins/dwarf/dwarf_import/src/helpers.rs
+++ b/plugins/dwarf/dwarf_import/src/helpers.rs
@@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use std::ffi::OsStr;
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
use std::{str::FromStr, sync::mpsc};
use crate::{DebugInfoBuilderContext, ReaderType};
@@ -590,10 +589,8 @@ pub(crate) fn find_sibling_debug_file(view: &BinaryView) -> Option<String> {
return None;
}
- let full_file_path = view.file().filename().to_string();
-
- let debug_file = PathBuf::from(format!("{}.debug", full_file_path));
- let dsym_folder = PathBuf::from(format!("{}.dSYM", full_file_path));
+ let debug_file = view.file().file_path().with_extension("debug");
+ let dsym_folder = view.file().file_path().with_extension("dSYM");
// Find sibling debug file
if debug_file.exists() && debug_file.is_file() {
@@ -624,13 +621,12 @@ pub(crate) fn find_sibling_debug_file(view: &BinaryView) -> Option<String> {
// Look for dSYM
// TODO: look for dSYM in project
if dsym_folder.exists() && dsym_folder.is_dir() {
- let filename = Path::new(&full_file_path)
- .file_name()
- .unwrap_or(OsStr::new(""));
-
- let dsym_file = dsym_folder.join("Contents/Resources/DWARF/").join(filename); // TODO: should this just pull any file out? Can there be multiple files?
- if dsym_file.exists() {
- return Some(dsym_file.to_string_lossy().to_string());
+ if let Some(filename) = view.file().file_path().file_name() {
+ // TODO: should this just pull any file out? Can there be multiple files?
+ let dsym_file = dsym_folder.join("Contents/Resources/DWARF/").join(filename);
+ if dsym_file.exists() {
+ return Some(dsym_file.to_string_lossy().to_string());
+ }
}
}
diff --git a/plugins/idb_import/src/lib.rs b/plugins/idb_import/src/lib.rs
index f285f554..0137f198 100644
--- a/plugins/idb_import/src/lib.rs
+++ b/plugins/idb_import/src/lib.rs
@@ -27,8 +27,10 @@ impl CustomDebugInfoParser for IDBDebugInfoParser {
project_file.name().as_str().ends_with(".i64")
|| project_file.name().as_str().ends_with(".idb")
} else {
- view.file().filename().as_str().ends_with(".i64")
- || view.file().filename().as_str().ends_with(".idb")
+ view.file()
+ .file_path()
+ .extension()
+ .map_or(false, |ext| ext == "i64" || ext == "idb")
}
}
@@ -55,7 +57,10 @@ impl CustomDebugInfoParser for TILDebugInfoParser {
if let Some(project_file) = view.file().project_file() {
project_file.name().as_str().ends_with(".til")
} else {
- view.file().filename().as_str().ends_with(".til")
+ view.file()
+ .file_path()
+ .extension()
+ .map_or(false, |ext| ext == "til")
}
}
diff --git a/plugins/pdb-ng/src/lib.rs b/plugins/pdb-ng/src/lib.rs
index 86ae1cdd..61ff54b2 100644
--- a/plugins/pdb-ng/src/lib.rs
+++ b/plugins/pdb-ng/src/lib.rs
@@ -617,7 +617,7 @@ impl CustomDebugInfoParser for PDBParser {
}
// Try in the same directory as the file
- let mut potential_path = PathBuf::from(view.file().filename().to_string());
+ let mut potential_path = view.file().file_path();
potential_path.pop();
potential_path.push(&info.file_name);
if potential_path.exists() {
diff --git a/plugins/warp/src/cache.rs b/plugins/warp/src/cache.rs
index 41aab423..3df2dbf8 100644
--- a/plugins/warp/src/cache.rs
+++ b/plugins/warp/src/cache.rs
@@ -79,6 +79,6 @@ pub struct CacheDestructor;
impl ObjectDestructor for CacheDestructor {
fn destruct_view(&self, view: &BinaryView) {
clear_type_ref_cache(view);
- tracing::debug!("Removed WARP caches for {:?}", view.file().filename());
+ tracing::debug!("Removed WARP caches for {}", view.file());
}
}
diff --git a/plugins/warp/src/plugin/create.rs b/plugins/warp/src/plugin/create.rs
index 0c6a2fd5..2a748819 100644
--- a/plugins/warp/src/plugin/create.rs
+++ b/plugins/warp/src/plugin/create.rs
@@ -23,11 +23,7 @@ impl SaveFileField {
let default_name = match file.project_file() {
None => {
// Not in a project, use the file name directly.
- file.filename()
- .split('/')
- .last()
- .unwrap_or("file")
- .to_string()
+ file.display_name()
}
Some(project_file) => project_file.name(),
};
diff --git a/rust/examples/decompile.rs b/rust/examples/decompile.rs
index 6ba9cbf4..155de81c 100644
--- a/rust/examples/decompile.rs
+++ b/rust/examples/decompile.rs
@@ -42,7 +42,7 @@ pub fn main() {
.load(&filename)
.expect("Couldn't open file!");
- tracing::info!("Filename: `{}`", bv.file().filename());
+ tracing::info!("File: `{}`", bv.file());
tracing::info!("File size: `{:#x}`", bv.len());
tracing::info!("Function count: {}", bv.functions().len());
diff --git a/rust/examples/disassemble.rs b/rust/examples/disassemble.rs
index ce3ff765..df33aade 100644
--- a/rust/examples/disassemble.rs
+++ b/rust/examples/disassemble.rs
@@ -39,7 +39,7 @@ pub fn main() {
.load(&filename)
.expect("Couldn't open file!");
- tracing::info!("Filename: `{}`", bv.file().filename());
+ tracing::info!("File: `{}`", bv.file());
tracing::info!("File size: `{:#x}`", bv.len());
tracing::info!("Function count: {}", bv.functions().len());
diff --git a/rust/examples/high_level_il.rs b/rust/examples/high_level_il.rs
index a099437a..57b735e9 100644
--- a/rust/examples/high_level_il.rs
+++ b/rust/examples/high_level_il.rs
@@ -14,7 +14,7 @@ fn main() {
.load("/bin/cat")
.expect("Couldn't open `/bin/cat`");
- tracing::info!("Filename: `{}`", bv.file().filename());
+ tracing::info!("File: `{}`", bv.file());
tracing::info!("File size: `{:#x}`", bv.len());
tracing::info!("Function count: {}", bv.functions().len());
diff --git a/rust/examples/medium_level_il.rs b/rust/examples/medium_level_il.rs
index c9412d08..e2c0edee 100644
--- a/rust/examples/medium_level_il.rs
+++ b/rust/examples/medium_level_il.rs
@@ -14,7 +14,7 @@ fn main() {
.load("/bin/cat")
.expect("Couldn't open `/bin/cat`");
- tracing::info!("Filename: `{}`", bv.file().filename());
+ tracing::info!("File: `{}`", bv.file());
tracing::info!("File size: `{:#x}`", bv.len());
tracing::info!("Function count: {}", bv.functions().len());
diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs
index 478b8265..4e35dc8c 100644
--- a/rust/src/binary_view.rs
+++ b/rust/src/binary_view.rs
@@ -2476,8 +2476,14 @@ impl BinaryView {
Ref::new(Self { handle })
}
- pub fn from_path(meta: &mut FileMetadata, file_path: impl AsRef<Path>) -> Result<Ref<Self>> {
- let file = file_path.as_ref().to_cstr();
+ /// Construct the raw binary view from the given metadata. Before calling this make sure you have
+ /// a valid file path set for the [`FileMetadata`]. It is required that the [`FileMetadata::file_path`]
+ /// exist on the local filesystem.
+ pub fn from_metadata(meta: &FileMetadata) -> Result<Ref<Self>> {
+ if !meta.file_path().exists() {
+ return Err(());
+ }
+ let file = meta.file_path().to_cstr();
let handle =
unsafe { BNCreateBinaryDataViewFromFilename(meta.handle, file.as_ptr() as *mut _) };
@@ -2488,6 +2494,15 @@ impl BinaryView {
unsafe { Ok(Ref::new(Self { handle })) }
}
+ /// Construct the raw binary view from the given `file_path` and metadata.
+ ///
+ /// This will implicitly set the metadata file path and then construct the view. If the metadata
+ /// already has the desired file path, use [`BinaryView::from_metadata`] instead.
+ pub fn from_path(meta: &FileMetadata, file_path: impl AsRef<Path>) -> Result<Ref<Self>> {
+ meta.set_file_path(file_path.as_ref());
+ Self::from_metadata(meta)
+ }
+
pub fn from_accessor<A: Accessor>(
meta: &FileMetadata,
file: &mut FileAccessor<A>,
diff --git a/rust/src/file_metadata.rs b/rust/src/file_metadata.rs
index 27ceb4af..f6427750 100644
--- a/rust/src/file_metadata.rs
+++ b/rust/src/file_metadata.rs
@@ -20,7 +20,7 @@ use binaryninjacore_sys::*;
use binaryninjacore_sys::{BNCreateDatabaseWithProgress, BNOpenExistingDatabaseWithProgress};
use std::ffi::c_void;
use std::fmt::{Debug, Display, Formatter};
-use std::path::Path;
+use std::path::{Path, PathBuf};
use crate::progress::ProgressCallback;
use crate::project::file::ProjectFile;
@@ -46,12 +46,15 @@ impl FileMetadata {
Self::ref_from_raw(unsafe { BNCreateFileMetadata() })
}
- pub fn with_filename(name: &str) -> Ref<Self> {
+ /// Build a [`FileMetadata`] with the given `path`, this is uncommon as you are likely to want to
+ /// open a [`BinaryView`]
+ pub fn with_file_path(path: &Path) -> Ref<Self> {
let ret = FileMetadata::new();
- ret.set_filename(name);
+ ret.set_file_path(path);
ret
}
+ /// Closes the [`FileMetadata`] allowing any [`BinaryView`] parented to it to be freed.
pub fn close(&self) {
unsafe {
BNCloseFile(self.handle);
@@ -63,22 +66,124 @@ impl FileMetadata {
SessionId(raw)
}
- pub fn filename(&self) -> String {
+ /// The path to the [`FileMetadata`] on disk.
+ ///
+ /// This will not point to the original file on disk, in the event that the file was saved
+ /// as a BNDB. When a BNDB is opened, the FileMetadata will contain the file path to the database.
+ ///
+ /// If you need the original binary file path, use [`FileMetadata::original_file_path`] instead.
+ ///
+ /// If you just want a name to present to the user, use [`FileMetadata::display_name`].
+ pub fn file_path(&self) -> PathBuf {
unsafe {
let raw = BNGetFilename(self.handle);
- BnString::into_string(raw)
+ PathBuf::from(BnString::into_string(raw))
}
}
- pub fn set_filename(&self, name: &str) {
+ // TODO: To prevent issues we will not allow users to set the file path as it really should be
+ // TODO: derived at construction and not modified later.
+ /// Set the files path on disk.
+ ///
+ /// This should always be a valid path.
+ pub(crate) fn set_file_path(&self, name: &Path) {
let name = name.to_cstr();
-
unsafe {
BNSetFilename(self.handle, name.as_ptr());
}
}
- pub fn modified(&self) -> bool {
+ /// The display name of the file. Useful for presenting to the user. Can differ from the original
+ /// name of the file and can be overridden with [`FileMetadata::set_display_name`].
+ pub fn display_name(&self) -> String {
+ let raw_name = unsafe {
+ let raw = BNGetDisplayName(self.handle);
+ BnString::into_string(raw)
+ };
+ // Sometimes this display name may return a full path, which is not the intended purpose.
+ raw_name
+ .split('/')
+ .next_back()
+ .unwrap_or(&raw_name)
+ .to_string()
+ }
+
+ /// Set the display name of the file.
+ ///
+ /// This can be anything and will not be used for any purpose other than presentation.
+ pub fn set_display_name(&self, name: &str) {
+ let name = name.to_cstr();
+ unsafe {
+ BNSetDisplayName(self.handle, name.as_ptr());
+ }
+ }
+
+ /// The path to the original file on disk, if any.
+ ///
+ /// It may not be present if the BNDB was saved without it or cleared via [`FileMetadata::clear_original_file_path`].
+ ///
+ /// Only prefer this over [`FileMetadata::file_path`] if you require the original binary location.
+ pub fn original_file_path(&self) -> Option<PathBuf> {
+ let raw_name = unsafe {
+ let raw = BNGetOriginalFilename(self.handle);
+ PathBuf::from(BnString::into_string(raw))
+ };
+ // If the original file path is empty, or the original file path is pointing to the same file
+ // as the database itself, we know the original file path does not exist.
+ if raw_name.as_os_str().is_empty()
+ || self.is_database_backed() && raw_name == self.file_path()
+ {
+ None
+ } else {
+ Some(raw_name)
+ }
+ }
+
+ /// Set the original file path inside the database. Useful if it has since been cleared from the
+ /// database, or you have moved the original file.
+ pub fn set_original_file_path(&self, path: &Path) {
+ let name = path.to_cstr();
+ unsafe {
+ BNSetOriginalFilename(self.handle, name.as_ptr());
+ }
+ }
+
+ /// Clear the original file path inside the database. This is useful since the original file path
+ /// may be sensitive information you wish to not share with others.
+ pub fn clear_original_file_path(&self) {
+ unsafe {
+ BNSetOriginalFilename(self.handle, std::ptr::null());
+ }
+ }
+
+ /// The non-filesystem path that describes how this file was derived from the container
+ /// transform system, detailing the sequence of transform steps and selection names.
+ ///
+ /// NOTE: Returns `None` if this [`FileMetadata`] was not processed by the transform system and
+ /// does not differ from that of the "physical" file path reported by [`FileMetadata::file_path`].
+ pub fn virtual_path(&self) -> Option<String> {
+ unsafe {
+ let raw = BNGetVirtualPath(self.handle);
+ let path = BnString::into_string(raw);
+ // For whatever reason the core may report there being a virtual path as the file path.
+ // In the case where that occurs, we wish not to report there being one to the user.
+ match path.is_empty() || path == self.file_path() {
+ true => None,
+ false => Some(path),
+ }
+ }
+ }
+
+ /// Sets the non-filesystem path that describes how this file was derived from the container
+ /// transform system.
+ pub fn set_virtual_path(&self, path: &str) {
+ let path = path.to_cstr();
+ unsafe {
+ BNSetVirtualPath(self.handle, path.as_ptr());
+ }
+ }
+
+ pub fn is_modified(&self) -> bool {
unsafe { BNIsFileModified(self.handle) }
}
@@ -372,9 +477,10 @@ impl FileMetadata {
impl Debug for FileMetadata {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FileMetadata")
- .field("filename", &self.filename())
+ .field("file_path", &self.file_path())
+ .field("display_name", &self.display_name())
.field("session_id", &self.session_id())
- .field("modified", &self.modified())
+ .field("is_modified", &self.is_modified())
.field("is_analysis_changed", &self.is_analysis_changed())
.field("current_view_type", &self.current_view())
.field("current_offset", &self.current_offset())
@@ -385,7 +491,7 @@ impl Debug for FileMetadata {
impl Display for FileMetadata {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- f.write_str(&self.filename())
+ f.write_str(&self.display_name())
}
}