binaryninja/
file_accessor.rs

1// Copyright 2021-2025 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                log::debug!("Failed to seek to offset {:x}", offset);
50                0
51            } else {
52                f.read(dest).unwrap_or(0)
53            }
54        }
55
56        extern "C" fn cb_write<A: Accessor>(
57            ctxt: *mut c_void,
58            offset: u64,
59            src: *const c_void,
60            len: usize,
61        ) -> usize {
62            let f = unsafe { &mut *(ctxt as *mut A) };
63            let src = unsafe { slice::from_raw_parts(src as *const u8, len) };
64
65            if f.seek(SeekFrom::Start(offset)).is_err() {
66                0
67            } else {
68                f.write(src).unwrap_or(0)
69            }
70        }
71
72        let boxed_accessor = Box::new(accessor);
73        let leaked_accessor = Box::leak(boxed_accessor);
74
75        Self {
76            raw: BNFileAccessor {
77                context: leaked_accessor as *mut A as *mut _,
78                getLength: Some(cb_get_length::<A>),
79                read: Some(cb_read::<A>),
80                write: Some(cb_write::<A>),
81            },
82            accessor: PhantomData,
83        }
84    }
85
86    pub fn read(&self, addr: u64, len: usize) -> Result<Vec<u8>, ErrorKind> {
87        let cb_read = self.raw.read.unwrap();
88        let mut buf = vec![0; len];
89        let read_len = unsafe { cb_read(self.raw.context, buf.as_mut_ptr() as *mut _, addr, len) };
90        if read_len != len {
91            return Err(ErrorKind::UnexpectedEof);
92        }
93        Ok(buf)
94    }
95
96    pub fn write(&self, addr: u64, data: &[u8]) -> usize {
97        let cb_write = self.raw.write.unwrap();
98        unsafe {
99            cb_write(
100                self.raw.context,
101                addr,
102                data.as_ptr() as *const _,
103                data.len(),
104            )
105        }
106    }
107
108    pub fn length(&self) -> u64 {
109        let cb_get_length = self.raw.getLength.unwrap();
110        unsafe { cb_get_length(self.raw.context) }
111    }
112}
113
114impl<A: Accessor> Drop for FileAccessor<A> {
115    fn drop(&mut self) {
116        unsafe {
117            let _ = Box::from_raw(self.raw.context as *mut A);
118        }
119    }
120}