Skip to main content

binaryninja/
file_accessor.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
15use binaryninjacore_sys::BNFileAccessor;
16use std::io::{ErrorKind, Read, Seek, SeekFrom, Write};
17use std::marker::PhantomData;
18use std::slice;
19
20pub trait Accessor: Read + Write + Seek + Sized {}
21
22impl<T: Read + Write + Seek + Sized> Accessor for T {}
23
24pub struct FileAccessor<A: Accessor> {
25    pub(crate) raw: BNFileAccessor,
26    accessor: PhantomData<A>,
27}
28
29impl<A: Accessor> FileAccessor<A> {
30    pub fn new(accessor: A) -> Self {
31        use std::os::raw::c_void;
32
33        extern "C" fn cb_get_length<A: Accessor>(ctxt: *mut c_void) -> u64 {
34            let f = unsafe { &mut *(ctxt as *mut A) };
35
36            f.seek(SeekFrom::End(0)).unwrap_or(0)
37        }
38
39        extern "C" fn cb_read<A: Accessor>(
40            ctxt: *mut c_void,
41            dest: *mut c_void,
42            offset: u64,
43            len: usize,
44        ) -> usize {
45            let f = unsafe { &mut *(ctxt as *mut A) };
46            let dest = unsafe { slice::from_raw_parts_mut(dest as *mut u8, len) };
47
48            if f.seek(SeekFrom::Start(offset)).is_err() {
49                0
50            } else {
51                f.read(dest).unwrap_or(0)
52            }
53        }
54
55        extern "C" fn cb_write<A: Accessor>(
56            ctxt: *mut c_void,
57            offset: u64,
58            src: *const c_void,
59            len: usize,
60        ) -> usize {
61            let f = unsafe { &mut *(ctxt as *mut A) };
62            let src = unsafe { slice::from_raw_parts(src as *const u8, len) };
63
64            if f.seek(SeekFrom::Start(offset)).is_err() {
65                0
66            } else {
67                f.write(src).unwrap_or(0)
68            }
69        }
70
71        let boxed_accessor = Box::new(accessor);
72        let leaked_accessor = Box::leak(boxed_accessor);
73
74        Self {
75            raw: BNFileAccessor {
76                context: leaked_accessor as *mut A as *mut _,
77                getLength: Some(cb_get_length::<A>),
78                read: Some(cb_read::<A>),
79                write: Some(cb_write::<A>),
80            },
81            accessor: PhantomData,
82        }
83    }
84
85    pub fn read(&self, addr: u64, len: usize) -> Result<Vec<u8>, ErrorKind> {
86        let cb_read = self.raw.read.unwrap();
87        let mut buf = vec![0; len];
88        let read_len = unsafe { cb_read(self.raw.context, buf.as_mut_ptr() as *mut _, addr, len) };
89        if read_len != len {
90            return Err(ErrorKind::UnexpectedEof);
91        }
92        Ok(buf)
93    }
94
95    pub fn write(&self, addr: u64, data: &[u8]) -> usize {
96        let cb_write = self.raw.write.unwrap();
97        unsafe {
98            cb_write(
99                self.raw.context,
100                addr,
101                data.as_ptr() as *const _,
102                data.len(),
103            )
104        }
105    }
106
107    pub fn length(&self) -> u64 {
108        let cb_get_length = self.raw.getLength.unwrap();
109        unsafe { cb_get_length(self.raw.context) }
110    }
111}
112
113impl<A: Accessor> Drop for FileAccessor<A> {
114    fn drop(&mut self) {
115        unsafe {
116            let _ = Box::from_raw(self.raw.context as *mut A);
117        }
118    }
119}
120
121/// A non-owning wrapper around a file accessor.
122///
123/// This is passed to callbacks, such as [`crate::binary_view::BinaryViewBase::save`], where the
124/// core retains ownership of the underlying accessor.
125///
126/// Use [`FileAccessor`] instead when you are the owner of the underlying accessor.
127pub struct BorrowedFileAccessor<'a> {
128    pub(crate) raw: &'a mut BNFileAccessor,
129}
130
131impl<'a> BorrowedFileAccessor<'a> {
132    pub(crate) unsafe fn from_raw(raw: *mut BNFileAccessor) -> Self {
133        debug_assert!(!raw.is_null());
134        Self {
135            raw: unsafe { &mut *raw },
136        }
137    }
138
139    pub fn read(&self, addr: u64, len: usize) -> Result<Vec<u8>, ErrorKind> {
140        let cb_read = self.raw.read.unwrap();
141        let mut buf = vec![0; len];
142        let read_len = unsafe { cb_read(self.raw.context, buf.as_mut_ptr() as *mut _, addr, len) };
143        if read_len != len {
144            return Err(ErrorKind::UnexpectedEof);
145        }
146        Ok(buf)
147    }
148
149    pub fn write(&self, addr: u64, data: &[u8]) -> usize {
150        let cb_write = self.raw.write.unwrap();
151        unsafe {
152            cb_write(
153                self.raw.context,
154                addr,
155                data.as_ptr() as *const _,
156                data.len(),
157            )
158        }
159    }
160
161    pub fn length(&self) -> u64 {
162        let cb_get_length = self.raw.getLength.unwrap();
163        unsafe { cb_get_length(self.raw.context) }
164    }
165}
166
167/// Used to provide access to the underlying file accessor.
168mod private {
169    use binaryninjacore_sys::BNFileAccessor;
170
171    pub trait Sealed {
172        fn raw_mut(&mut self) -> &mut BNFileAccessor;
173    }
174}
175
176/// A file accessor that can be passed to core functions like [`crate::binary_view::BinaryView::save_to_accessor`].
177///
178/// This is implemented by both [`FileAccessor`] and [`BorrowedFileAccessor`].
179#[allow(private_bounds)]
180pub trait FileAccessorHandle: private::Sealed {}
181
182impl<T: private::Sealed + ?Sized> FileAccessorHandle for T {}
183
184impl<A: Accessor> private::Sealed for FileAccessor<A> {
185    fn raw_mut(&mut self) -> &mut BNFileAccessor {
186        &mut self.raw
187    }
188}
189
190impl private::Sealed for BorrowedFileAccessor<'_> {
191    fn raw_mut(&mut self) -> &mut BNFileAccessor {
192        self.raw
193    }
194}
195
196pub(crate) fn raw_mut<T: FileAccessorHandle + ?Sized>(accessor: &mut T) -> &mut BNFileAccessor {
197    private::Sealed::raw_mut(accessor)
198}