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}