binaryninja/
file_metadata.rs

1// Copyright 2021-2026 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The [`FileMetadata`] struct provides information about a file and owns its available [`BinaryView`]s.
16
17use crate::binary_view::BinaryView;
18use crate::database::Database;
19use crate::rc::*;
20use crate::string::*;
21use binaryninjacore_sys::*;
22use binaryninjacore_sys::{BNCreateDatabaseWithProgress, BNOpenExistingDatabaseWithProgress};
23use std::ffi::c_void;
24use std::fmt::{Debug, Display, Formatter};
25use std::hash::{Hash, Hasher};
26use std::path::{Path, PathBuf};
27
28use crate::progress::{NoProgressCallback, ProgressCallback};
29use crate::project::file::ProjectFile;
30use std::ptr::NonNull;
31
32#[allow(unused_imports)]
33use crate::binary_view::BinaryViewType;
34
35new_id_type!(SessionId, usize);
36
37pub type SaveOption = BNSaveOption;
38
39/// Settings to alter the behavior of creating snapshots saved within a [`Database`].
40pub struct SaveSettings {
41    pub(crate) handle: *mut BNSaveSettings,
42}
43
44impl SaveSettings {
45    pub fn new() -> Ref<Self> {
46        Self::ref_from_raw(unsafe { BNCreateSaveSettings() })
47    }
48
49    fn ref_from_raw(handle: *mut BNSaveSettings) -> Ref<Self> {
50        unsafe { Ref::new(Self { handle }) }
51    }
52
53    /// Sets the specified `option` to `true` and returns a ref counted `SaveSettings` that can
54    /// continued to be chained.
55    pub fn with_option(&self, option: SaveOption) -> Ref<Self> {
56        self.set_option(option, true);
57        self.to_owned()
58    }
59
60    pub fn set_option(&self, option: SaveOption, value: bool) {
61        unsafe { BNSetSaveSettingsOption(self.handle, option, value) }
62    }
63
64    pub fn option(&self, option: SaveOption) -> bool {
65        unsafe { BNIsSaveSettingsOptionSet(self.handle, option) }
66    }
67
68    /// When saving an automatic snapshot via [`FileMetadata::save_auto_snapshot`] this name will be
69    /// used for the newly written snapshot.
70    pub fn snapshot_name(&self) -> String {
71        unsafe { BnString::into_string(BNGetSaveSettingsName(self.handle)) }
72    }
73
74    pub fn set_snapshot_name(&self, name: &str) {
75        let name = name.to_cstr();
76        unsafe { BNSetSaveSettingsName(self.handle, name.as_ptr()) }
77    }
78}
79
80unsafe impl Send for SaveSettings {}
81unsafe impl Sync for SaveSettings {}
82
83impl ToOwned for SaveSettings {
84    type Owned = Ref<Self>;
85
86    fn to_owned(&self) -> Self::Owned {
87        unsafe { RefCountable::inc_ref(self) }
88    }
89}
90
91unsafe impl RefCountable for SaveSettings {
92    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
93        Ref::new(Self {
94            handle: BNNewSaveSettingsReference(handle.handle),
95        })
96    }
97
98    unsafe fn dec_ref(handle: &Self) {
99        BNFreeSaveSettings(handle.handle);
100    }
101}
102
103/// File metadata provides information about a file in the context of Binary Ninja. It contains no
104/// analysis information, only information useful for identifying a file, such as the [`FileMetadata::file_path`].
105///
106/// Another responsibility of the [`FileMetadata`] is to own the available [`BinaryView`]s for the
107/// file, such as the "Raw" view and any other views that may be created for the file.
108///
109/// **Important**: Because [`FileMetadata`] holds a strong reference to the [`BinaryView`]s and those
110/// views hold a strong reference to the file metadata, to end the cyclic reference a call to the
111/// [`FileMetadata::close`] is required.
112pub struct FileMetadata {
113    pub(crate) handle: *mut BNFileMetadata,
114}
115
116impl FileMetadata {
117    pub(crate) fn from_raw(handle: *mut BNFileMetadata) -> Self {
118        Self { handle }
119    }
120
121    pub(crate) fn ref_from_raw(handle: *mut BNFileMetadata) -> Ref<Self> {
122        unsafe { Ref::new(Self { handle }) }
123    }
124
125    /// Create an empty [`FileMetadata`] with no associated file path.
126    ///
127    /// Unless you are creating an ephemeral file with no backing, prefer [`FileMetadata::with_file_path`].
128    pub fn new() -> Ref<Self> {
129        Self::ref_from_raw(unsafe { BNCreateFileMetadata() })
130    }
131
132    /// Build a [`FileMetadata`] with the given `path`.
133    pub fn with_file_path(path: &Path) -> Ref<Self> {
134        let ret = FileMetadata::new();
135        ret.set_file_path(path);
136        ret
137    }
138
139    /// Closes the [`FileMetadata`] allowing any [`BinaryView`] parented to it to be freed.
140    pub fn close(&self) {
141        unsafe {
142            BNCloseFile(self.handle);
143        }
144    }
145
146    /// An id unique to this [`FileMetadata`], mostly used for associating logs with a specific file.
147    pub fn session_id(&self) -> SessionId {
148        let raw = unsafe { BNFileMetadataGetSessionId(self.handle) };
149        SessionId(raw)
150    }
151
152    /// The path to the [`FileMetadata`] on disk.
153    ///
154    /// This will not point to the original file on disk, in the event that the file was saved
155    /// as a BNDB. When a BNDB is opened, the FileMetadata will contain the file path to the database.
156    ///
157    /// If you need the original binary file path, use [`FileMetadata::original_file_path`] instead.
158    ///
159    /// If you just want a name to present to the user, use [`FileMetadata::display_name`].
160    pub fn file_path(&self) -> PathBuf {
161        unsafe {
162            let raw = BNGetFilename(self.handle);
163            PathBuf::from(BnString::into_string(raw))
164        }
165    }
166
167    // TODO: To prevent issues we will not allow users to set the file path as it really should be
168    // TODO: derived at construction and not modified later.
169    /// Set the files path on disk.
170    ///
171    /// This should always be a valid path.
172    pub(crate) fn set_file_path(&self, name: &Path) {
173        let name = name.to_cstr();
174        unsafe {
175            BNSetFilename(self.handle, name.as_ptr());
176        }
177    }
178
179    /// A leaf-shaped human-readable name for UI presentation. Never contains a directory path.
180    /// Resolution order:
181    /// * An explicitly set display name (project-assigned, transform-synthesized for container
182    ///   entries, or set via [`FileMetadata::set_display_name`]).
183    /// * Otherwise the leaf of [`FileMetadata::file_path`].
184    ///
185    /// Use this for tab titles, save-dialog default leaf names, logs, and any UI surface where
186    /// you'd refer to the file by name. Use [`FileMetadata::file_path`] for the physical path
187    /// that can be reopened.
188    pub fn display_name(&self) -> String {
189        let raw_name = unsafe {
190            let raw = BNGetDisplayName(self.handle);
191            BnString::into_string(raw)
192        };
193        // Sometimes this display name may return a full path, which is not the intended purpose.
194        raw_name
195            .split('/')
196            .next_back()
197            .unwrap_or(&raw_name)
198            .to_string()
199    }
200
201    /// Set the display name of the file.
202    ///
203    /// This can be anything and will not be used for any purpose other than presentation.
204    pub fn set_display_name(&self, name: &str) {
205        let name = name.to_cstr();
206        unsafe {
207            BNSetDisplayName(self.handle, name.as_ptr());
208        }
209    }
210
211    /// The path to the original file on disk, if any.
212    ///
213    /// It may not be present if the BNDB was saved without it or cleared via [`FileMetadata::clear_original_file_path`].
214    ///
215    /// If this [`FileMetadata`] is a database within a project, it may not have a "consumable" original
216    /// file path. Instead, this might return the path to the on disk file path of the project file that
217    /// this database was created from, for projects you should query through [`FileMetadata::project_file`].
218    ///
219    /// Only prefer this over [`FileMetadata::file_path`] if you require the original binary location.
220    pub fn original_file_path(&self) -> Option<PathBuf> {
221        let raw_name = unsafe {
222            let raw = BNGetOriginalFilename(self.handle);
223            PathBuf::from(BnString::into_string(raw))
224        };
225        // If the original file path is empty, or the original file path is pointing to the same file
226        // as the database itself, we know the original file path does not exist.
227        if raw_name.as_os_str().is_empty()
228            || self.is_database_backed() && raw_name == self.file_path()
229        {
230            None
231        } else {
232            Some(raw_name)
233        }
234    }
235
236    /// Set the original file path inside the database. Useful if it has since been cleared from the
237    /// database, or you have moved the original file.
238    pub fn set_original_file_path(&self, path: &Path) {
239        let name = path.to_cstr();
240        unsafe {
241            BNSetOriginalFilename(self.handle, name.as_ptr());
242        }
243    }
244
245    /// Clear the original file path inside the database. This is useful since the original file path
246    /// may be sensitive information you wish to not share with others.
247    pub fn clear_original_file_path(&self) {
248        unsafe {
249            BNSetOriginalFilename(self.handle, std::ptr::null());
250        }
251    }
252
253    /// The non-filesystem path describing how this file was derived from the container transform
254    /// system in the current session. There are three meaningful states:
255    /// * `None` - not yet processed by the transform system.
256    /// * `Some(p)` where `p == file_path()` - processed, no transform chain applied (plain file,
257    ///   database, or the container system was disabled via `files.container.mode`).
258    /// * `Some(p)` where `p != file_path()` - derived container entry.
259    ///
260    /// Session-scoped: save-as does not persist the chain. Reopening the saved artifact yields
261    /// whatever chain that session's access path produces.
262    pub fn virtual_path(&self) -> Option<String> {
263        unsafe {
264            let raw = BNGetVirtualPath(self.handle);
265            let path = BnString::into_string(raw);
266            if path.is_empty() {
267                None
268            } else {
269                Some(path)
270            }
271        }
272    }
273
274    /// Sets the non-filesystem path that describes how this file was derived from the container
275    /// transform system.
276    pub fn set_virtual_path(&self, path: &str) {
277        let path = path.to_cstr();
278        unsafe {
279            BNSetVirtualPath(self.handle, path.as_ptr());
280        }
281    }
282
283    /// `true` if this file was produced by the container transform system, `false` for plain files,
284    /// databases, and FileMetadata that has not yet been processed by the transform system.
285    pub fn is_container_entry(&self) -> bool {
286        matches!(self.virtual_path(), Some(p) if p != self.file_path())
287    }
288
289    /// Whether the file is currently flagged as modified.
290    ///
291    /// When this returns `true`, the UI will prompt to save the database on close, as well as display
292    /// a dot in the files tab.
293    pub fn is_modified(&self) -> bool {
294        unsafe { BNIsFileModified(self.handle) }
295    }
296
297    /// Marks the file as modified such that we can prompt to save the database on close.
298    pub fn mark_modified(&self) {
299        unsafe {
300            BNMarkFileModified(self.handle);
301        }
302    }
303
304    /// Marks the file as saved such that [`FileMetadata::is_modified`] and [`FileMetadata::is_analysis_changed`]
305    /// will return `false` and the undo buffer associated with this [`FileMetadata`] will be updated.
306    pub fn mark_saved(&self) {
307        unsafe {
308            BNMarkFileSaved(self.handle);
309        }
310    }
311
312    pub fn is_analysis_changed(&self) -> bool {
313        unsafe { BNIsAnalysisChanged(self.handle) }
314    }
315
316    /// Checks to see if the database exists for the file.
317    pub fn is_database_backed(&self) -> bool {
318        // TODO: This seems to be a useless function. Replace with a call to file.database().is_some()?
319        self.is_database_backed_for_view_type("")
320    }
321
322    /// Checks to see if the file metadata has a [`Database`], and then checks to see if the `view_type`
323    /// is available.
324    ///
325    /// NOTE: Passing an empty string will simply check if the database exists.
326    pub fn is_database_backed_for_view_type(&self, view_type: &str) -> bool {
327        let view_type = view_type.to_cstr();
328        // TODO: This seems to be a useless function. Replace with a call to file.database().is_some()?
329        unsafe { BNIsBackedByDatabase(self.handle, view_type.as_ref().as_ptr() as *const _) }
330    }
331
332    /// Runs a failable function where the failure state will revert any undo actions that occurred
333    /// during the time of the function's execution.
334    ///
335    /// NOTE: This will commit or undo any actions that occurred on **any** thread as this state is not thread local.
336    ///
337    /// NOTE: This is **NOT** thread safe, if you are holding any locks that might be held by both the main thread
338    /// and the thread executing this function, you can deadlock. You should also never call this function
339    /// on multiple threads at a time. See the following issues:
340    ///  - <https://github.com/Vector35/binaryninja-api/issues/6289>
341    ///  - <https://github.com/Vector35/binaryninja-api/issues/6325>
342    pub fn run_undoable_transaction<F: FnOnce() -> Result<T, E>, T, E>(
343        &self,
344        func: F,
345    ) -> Result<T, E> {
346        let undo = self.begin_undo_actions(false);
347        let result = func();
348        match result {
349            Ok(t) => {
350                self.commit_undo_actions(&undo);
351                Ok(t)
352            }
353            Err(e) => {
354                self.revert_undo_actions(&undo);
355                Err(e)
356            }
357        }
358    }
359
360    /// Creates a new undo entry, any undo actions after this will be added to this entry.
361    ///
362    /// NOTE: This is **NOT** thread safe, if you are holding any locks that might be held by both the main thread
363    /// and the thread executing this function, you can deadlock. You should also never call this function
364    /// on multiple threads at a time. See the following issues:
365    ///  - <https://github.com/Vector35/binaryninja-api/issues/6289>
366    ///  - <https://github.com/Vector35/binaryninja-api/issues/6325>
367    pub fn begin_undo_actions(&self, anonymous_allowed: bool) -> String {
368        unsafe { BnString::into_string(BNBeginUndoActions(self.handle, anonymous_allowed)) }
369    }
370
371    /// Commits the undo entry with the id to the undo buffer.
372    ///
373    /// NOTE: This is **NOT** thread safe, if you are holding any locks that might be held by both the main thread
374    /// and the thread executing this function, you can deadlock. You should also never call this function
375    /// on multiple threads at a time. See the following issues:
376    ///  - <https://github.com/Vector35/binaryninja-api/issues/6289>
377    ///  - <https://github.com/Vector35/binaryninja-api/issues/6325>
378    pub fn commit_undo_actions(&self, id: &str) {
379        let id = id.to_cstr();
380        unsafe {
381            BNCommitUndoActions(self.handle, id.as_ref().as_ptr() as *const _);
382        }
383    }
384
385    /// Reverts the undo actions committed in the undo entry.
386    ///
387    /// NOTE: This is **NOT** thread safe, if you are holding any locks that might be held by both the main thread
388    /// and the thread executing this function, you can deadlock. You should also never call this function
389    /// on multiple threads at a time. See the following issues:
390    ///  - <https://github.com/Vector35/binaryninja-api/issues/6289>
391    ///  - <https://github.com/Vector35/binaryninja-api/issues/6325>
392    pub fn revert_undo_actions(&self, id: &str) {
393        let id = id.to_cstr();
394        unsafe {
395            BNRevertUndoActions(self.handle, id.as_ref().as_ptr() as *const _);
396        }
397    }
398
399    /// Forgets the undo actions committed in the undo entry.
400    ///
401    /// NOTE: This is **NOT** thread safe, if you are holding any locks that might be held by both the main thread
402    /// and the thread executing this function, you can deadlock. You should also never call this function
403    /// on multiple threads at a time. See the following issues:
404    ///  - <https://github.com/Vector35/binaryninja-api/issues/6289>
405    ///  - <https://github.com/Vector35/binaryninja-api/issues/6325>
406    pub fn forget_undo_actions(&self, id: &str) {
407        let id = id.to_cstr();
408        unsafe {
409            BNForgetUndoActions(self.handle, id.as_ref().as_ptr() as *const _);
410        }
411    }
412
413    pub fn undo(&self) {
414        unsafe {
415            BNUndo(self.handle);
416        }
417    }
418
419    pub fn redo(&self) {
420        unsafe {
421            BNRedo(self.handle);
422        }
423    }
424
425    /// Retrieve the raw view for the file, this should always be present.
426    ///
427    /// The "Raw" view is a special [`BinaryView`] that holds data required for updating and creating
428    /// [`Database`]s such as the view and load settings.
429    pub fn raw_view(&self) -> Ref<BinaryView> {
430        self.view_of_type("Raw")
431            .expect("Raw view should always be present")
432    }
433
434    /// The current view for the file.
435    ///
436    /// For example, opening a PE file and navigating to the linear view will return "Linear:PE".
437    pub fn current_view(&self) -> String {
438        unsafe { BnString::into_string(BNGetCurrentView(self.handle)) }
439    }
440
441    /// The current offset navigated to within the [`FileMetadata::current_view`].
442    pub fn current_offset(&self) -> u64 {
443        unsafe { BNGetCurrentOffset(self.handle) }
444    }
445
446    /// Navigate to an offset for a specific view.
447    ///
448    /// # Example
449    ///
450    /// ```no_run
451    /// use binaryninja::file_metadata::FileMetadata;
452    /// # let file: FileMetadata = unimplemented!();
453    /// file.navigate_to("Linear:Raw", 0x0).expect("Linear:Raw should always be present");
454    /// ```
455    pub fn navigate_to(&self, view: &str, offset: u64) -> Result<(), ()> {
456        let view = view.to_cstr();
457
458        unsafe {
459            if BNNavigate(self.handle, view.as_ref().as_ptr() as *const _, offset) {
460                Ok(())
461            } else {
462                Err(())
463            }
464        }
465    }
466
467    /// Get the [`BinaryView`] for the view type.
468    ///
469    /// # Example
470    ///
471    /// ```no_run
472    /// use binaryninja::file_metadata::FileMetadata;
473    /// # let file: FileMetadata = unimplemented!();
474    /// file.view_of_type("Raw").expect("Raw type should always be present");
475    /// ```
476    pub fn view_of_type(&self, view: &str) -> Option<Ref<BinaryView>> {
477        let view = view.to_cstr();
478
479        unsafe {
480            let raw_view_ptr = BNGetFileViewOfType(self.handle, view.as_ref().as_ptr() as *const _);
481            match raw_view_ptr.is_null() {
482                false => Some(BinaryView::ref_from_raw(raw_view_ptr)),
483                true => None,
484            }
485        }
486    }
487
488    /// The [`BinaryViewType`]s associated with this file.
489    ///
490    /// For example, opening a PE binary will have the following: "Raw", "PE".
491    ///
492    /// Because the type may not have been registered, and the actual [`BinaryViewType`] is not available,
493    /// we instead return the name of the view type.
494    pub fn view_types(&self) -> Array<BnString> {
495        let mut count = 0;
496        unsafe {
497            let types = BNGetExistingViews(self.handle, &mut count);
498            Array::new(types, count, ())
499        }
500    }
501
502    /// Get the [`ProjectFile`] for the [`FileMetadata`].
503    pub fn project_file(&self) -> Option<Ref<ProjectFile>> {
504        unsafe {
505            let res = NonNull::new(BNGetProjectFile(self.handle))?;
506            Some(ProjectFile::ref_from_raw(res))
507        }
508    }
509
510    /// Create a database for the file and its views at `file_path`.
511    ///
512    /// NOTE: Calling this while analysis is running will flag the next load of the database to
513    /// regenerate the current analysis.
514    pub fn create_database(&self, file_path: impl AsRef<Path>, settings: &SaveSettings) -> bool {
515        self.create_database_with_progress(file_path, settings, NoProgressCallback)
516    }
517
518    /// Create a database for the file and its views at `file_path`, with a progress callback.
519    ///
520    /// NOTE: Calling this while analysis is running will flag the next load of the database to
521    /// regenerate the current analysis.
522    pub fn create_database_with_progress<P: ProgressCallback>(
523        &self,
524        file_path: impl AsRef<Path>,
525        settings: &SaveSettings,
526        mut progress: P,
527    ) -> bool {
528        // Databases are created with the root view (Raw).
529        let raw_view = self.raw_view();
530        let file_path = file_path.as_ref().to_cstr();
531        unsafe {
532            BNCreateDatabaseWithProgress(
533                raw_view.handle,
534                file_path.as_ptr() as *mut _,
535                &mut progress as *mut P as *mut c_void,
536                Some(P::cb_progress_callback),
537                settings.handle,
538            )
539        }
540    }
541
542    /// Save a new snapshot of the current file.
543    ///
544    /// NOTE: Calling this while analysis is running will flag the next load of the database to
545    /// regenerate the current analysis.
546    pub fn save_auto_snapshot(&self) -> bool {
547        // Snapshots are saved with the root view (Raw).
548        let raw_view = self.raw_view();
549        unsafe { BNSaveAutoSnapshot(raw_view.handle, std::ptr::null_mut() as *mut _) }
550    }
551
552    // TODO: Deprecate this function? Does not seem to do anything different than `open_database`.
553    pub fn open_database_for_configuration(&self, file: &Path) -> Result<Ref<BinaryView>, ()> {
554        let file = file.to_cstr();
555        unsafe {
556            let bv =
557                BNOpenDatabaseForConfiguration(self.handle, file.as_ref().as_ptr() as *const _);
558
559            if bv.is_null() {
560                Err(())
561            } else {
562                Ok(BinaryView::ref_from_raw(bv))
563            }
564        }
565    }
566
567    // TODO: How this relates to `BNLoadFilename`?
568    pub fn open_database(&self, file: &Path) -> Result<Ref<BinaryView>, ()> {
569        let file = file.to_cstr();
570        let view = unsafe { BNOpenExistingDatabase(self.handle, file.as_ptr()) };
571
572        if view.is_null() {
573            Err(())
574        } else {
575            Ok(unsafe { BinaryView::ref_from_raw(view) })
576        }
577    }
578
579    // TODO: How this relates to `BNLoadFilename`?
580    pub fn open_database_with_progress<P: ProgressCallback>(
581        &self,
582        file: &Path,
583        mut progress: P,
584    ) -> Result<Ref<BinaryView>, ()> {
585        let file = file.to_cstr();
586
587        let view = unsafe {
588            BNOpenExistingDatabaseWithProgress(
589                self.handle,
590                file.as_ptr(),
591                &mut progress as *mut P as *mut c_void,
592                Some(P::cb_progress_callback),
593            )
594        };
595
596        if view.is_null() {
597            Err(())
598        } else {
599            Ok(unsafe { BinaryView::ref_from_raw(view) })
600        }
601    }
602
603    /// Get the database attached to this file.
604    ///
605    /// Only available if this file is a database, or [`FileMetadata::create_database`] has previously
606    /// been called on this file.
607    pub fn database(&self) -> Option<Ref<Database>> {
608        let result = unsafe { BNGetFileMetadataDatabase(self.handle) };
609        NonNull::new(result).map(|handle| unsafe { Database::ref_from_raw(handle) })
610    }
611}
612
613impl Debug for FileMetadata {
614    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
615        f.debug_struct("FileMetadata")
616            .field("file_path", &self.file_path())
617            .field("display_name", &self.display_name())
618            .field("session_id", &self.session_id())
619            .field("is_modified", &self.is_modified())
620            .field("is_analysis_changed", &self.is_analysis_changed())
621            .field("current_view_type", &self.current_view())
622            .field("current_offset", &self.current_offset())
623            .field("view_types", &self.view_types().to_vec())
624            .finish()
625    }
626}
627
628impl Display for FileMetadata {
629    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
630        f.write_str(&self.display_name())
631    }
632}
633
634impl PartialEq for FileMetadata {
635    fn eq(&self, other: &Self) -> bool {
636        self.session_id() == other.session_id()
637    }
638}
639
640impl Eq for FileMetadata {}
641
642impl Hash for FileMetadata {
643    fn hash<H: Hasher>(&self, state: &mut H) {
644        self.session_id().hash(state);
645    }
646}
647
648unsafe impl Send for FileMetadata {}
649unsafe impl Sync for FileMetadata {}
650
651impl ToOwned for FileMetadata {
652    type Owned = Ref<Self>;
653
654    fn to_owned(&self) -> Self::Owned {
655        unsafe { RefCountable::inc_ref(self) }
656    }
657}
658
659unsafe impl RefCountable for FileMetadata {
660    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
661        Ref::new(Self {
662            handle: BNNewFileReference(handle.handle),
663        })
664    }
665
666    unsafe fn dec_ref(handle: &Self) {
667        BNFreeFileMetadata(handle.handle);
668    }
669}