Skip to main content

rair_io/plugins/
defaultplugin.rs

1/*
2 * defaultplugin.rs: RIO plugin that opens raw binary files.
3 * Copyright (C) 2019  Oddcoder
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17use memmap::*;
18use plugin::*;
19use std::fs::OpenOptions;
20use std::io;
21use std::ops::Deref;
22use std::path::Path;
23use utils::*;
24enum FileInternals {
25    Map(Mmap),
26    MutMap(MmapMut),
27}
28
29impl FileInternals {
30    fn len(&self) -> usize {
31        match self {
32            FileInternals::Map(m) => return m.len(),
33            FileInternals::MutMap(m) => return m.len(),
34        }
35    }
36    fn as_mut(&mut self) -> Option<&mut MmapMut> {
37        if let FileInternals::MutMap(mutmap) = self {
38            return Some(mutmap);
39        } else {
40            return None;
41        }
42    }
43}
44const METADATA: RIOPluginMetadata = RIOPluginMetadata {
45    name: "FilePlugin",
46    desc: "This IO plugin is used to open normal files.",
47    author: "Oddcoder",
48    license: "LGPL",
49    version: "0.0.1",
50};
51impl Deref for FileInternals {
52    type Target = [u8];
53    fn deref(&self) -> &[u8] {
54        match self {
55            FileInternals::Map(m) => &**m,
56            FileInternals::MutMap(m) => &**m,
57        }
58    }
59}
60impl RIOPluginOperations for FileInternals {
61    fn read(&mut self, raddr: usize, buffer: &mut [u8]) -> Result<(), IoError> {
62        if self.len() < raddr + buffer.len() {
63            return Err(IoError::Parse(io::Error::new(io::ErrorKind::UnexpectedEof, "BufferOverflow")));
64        }
65        buffer.copy_from_slice(&self[raddr..raddr + buffer.len()]);
66        return Ok(());
67    }
68
69    fn write(&mut self, raddr: usize, buf: &[u8]) -> Result<(), IoError> {
70        if let Some(mutmap) = self.as_mut() {
71            if raddr + buf.len() > mutmap.len() {
72                return Err(IoError::Parse(io::Error::new(io::ErrorKind::UnexpectedEof, "BufferOverflow")));
73            }
74            mutmap[raddr..raddr + buf.len()].copy_from_slice(buf);
75            return Ok(());
76        } else {
77            return Err(IoError::Parse(io::Error::new(io::ErrorKind::PermissionDenied, "File Not Writable")));
78        }
79    }
80}
81
82struct FilePlugin {}
83
84impl FilePlugin {
85    fn uri_to_path(uri: &str) -> &Path {
86        let path = uri.trim_start_matches("file://");
87        return Path::new(path);
88    }
89}
90
91impl RIOPlugin for FilePlugin {
92    fn get_metadata(&self) -> &'static RIOPluginMetadata {
93        return &METADATA;
94    }
95
96    fn open(&mut self, uri: &str, flags: IoMode) -> Result<RIOPluginDesc, IoError> {
97        let file: FileInternals;
98        if !flags.contains(IoMode::READ) && flags.contains(IoMode::WRITE) {
99            return Err(IoError::Parse(io::Error::new(io::ErrorKind::PermissionDenied, "Can't Open File for writing without reading")));
100        }
101        // we can't have write with cow bcause this mean we had writer without read or read with cow lol
102        if flags.contains(IoMode::READ) && flags.contains(IoMode::COW) {
103            return Err(IoError::Parse(io::Error::new(
104                io::ErrorKind::PermissionDenied,
105                "Can't Open File with permission as Read and Copy-On-Write",
106            )));
107        }
108        if flags.contains(IoMode::COW) {
109            let f = OpenOptions::new().read(true).open(FilePlugin::uri_to_path(uri))?;
110            file = FileInternals::MutMap(unsafe { MmapOptions::new().map_copy(&f)? });
111        } else if flags.contains(IoMode::WRITE) {
112            let f = OpenOptions::new().read(true).write(true).open(FilePlugin::uri_to_path(uri))?;
113            file = FileInternals::MutMap(unsafe { MmapOptions::new().map_mut(&f)? });
114        } else {
115            let f = OpenOptions::new().read(true).open(FilePlugin::uri_to_path(uri))?;
116            file = FileInternals::Map(unsafe { MmapOptions::new().map(&f)? });
117        }
118        let desc = RIOPluginDesc {
119            name: uri.to_owned(),
120            perm: flags,
121            raddr: 0,
122            size: (file.len() as u64),
123            plugin_operations: Box::new(file),
124        };
125        return Ok(desc);
126    }
127
128    // either file:// or just no "://" to start with
129    fn accept_uri(&self, uri: &str) -> bool {
130        let split: Vec<&str> = uri.split("://").collect();
131        if split.len() == 1 {
132            return true;
133        }
134        if split[0] == "file" {
135            return true;
136        }
137        return false;
138    }
139}
140
141pub fn plugin() -> Box<dyn RIOPlugin> {
142    return Box::new(FilePlugin {});
143}
144
145#[cfg(test)]
146mod default_plugin_tests {
147    use super::*;
148    use test_file::*;
149    #[test]
150    fn test_plugin() {
151        let plugin = plugin();
152        let meta = plugin.get_metadata();
153        assert_eq!(plugin.accept_uri("/bin/ls"), true);
154        assert_eq!(plugin.accept_uri("file:///bin/ls"), true);
155        assert_eq!(plugin.accept_uri("ihex:///bin/ls"), false);
156        assert_eq!(meta.name, METADATA.name);
157        assert_eq!(meta.desc, METADATA.desc);
158        assert_eq!(meta.author, METADATA.author);
159        assert_eq!(meta.license, METADATA.license);
160        assert_eq!(meta.version, METADATA.version);
161    }
162
163    fn test_open_errors_cb(paths: &[&Path]) {
164        let mut plugin = plugin();
165        let custom_path = String::from("file://") + &paths[0].to_string_lossy();
166        plugin.open(&custom_path, IoMode::COW).unwrap();
167        plugin.open(&paths[1].to_string_lossy(), IoMode::READ).unwrap();
168        plugin.open(&paths[2].to_string_lossy(), IoMode::READ | IoMode::WRITE).unwrap();
169        let mut e = plugin.open(&paths[3].to_string_lossy(), IoMode::WRITE);
170        match e {
171            Err(IoError::Parse(io_err)) => assert_eq!(io_err.kind(), io::ErrorKind::PermissionDenied),
172            _ => assert!(false, "Permission Denied Error should have been generated"),
173        };
174
175        e = plugin.open(&paths[3].to_string_lossy(), IoMode::READ | IoMode::COW);
176        match e {
177            Err(IoError::Parse(io_err)) => assert_eq!(io_err.kind(), io::ErrorKind::PermissionDenied),
178            _ => assert!(false, "Permission Denied Error should have been generated"),
179        };
180
181        e = plugin.open(&paths[3].to_string_lossy(), IoMode::READ | IoMode::WRITE | IoMode::COW);
182        match e {
183            Err(IoError::Parse(io_err)) => assert_eq!(io_err.kind(), io::ErrorKind::PermissionDenied),
184            _ => assert!(false, "Permission Denied Error should have been generated"),
185        };
186    }
187    #[test]
188    fn test_open_errors() {
189        operate_on_files(&test_open_errors_cb, &[DATA, DATA, DATA, DATA]);
190    }
191    fn test_read_cb(path: &Path) {
192        let mut plugin = plugin();
193        let mut desc = plugin.open(&path.to_string_lossy(), IoMode::READ).unwrap();
194        let mut buffer: &mut [u8] = &mut [0; 8];
195        // read at the begining
196        desc.plugin_operations.read(desc.raddr as usize, &mut buffer).unwrap();
197        assert_eq!(buffer, [0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x08, 0x0d]);
198        // read at the middle
199        desc.plugin_operations.read((desc.raddr + 0x10) as usize, &mut buffer).unwrap();
200        assert_eq!(buffer, [0xdb, 0x3d, 0x18, 0x55, 0x6d, 0xc2, 0x2f, 0xf1]);
201        // read at the end
202        desc.plugin_operations.read((desc.raddr + 97) as usize, &mut buffer).unwrap();
203        assert_eq!(buffer, [0x41, 0xc1, 0x02, 0xc3, 0xc5, 0x88, 0x4d, 0xd5]);
204    }
205    #[test]
206    fn test_read() {
207        operate_on_file(&test_read_cb, DATA)
208    }
209
210    fn test_read_errors_cb(path: &Path) {
211        let mut plugin = plugin();
212        let mut desc = plugin.open(&path.to_string_lossy(), IoMode::READ).unwrap();
213        let mut buffer: &mut [u8] = &mut [0; 8];
214        // read past the end
215        let mut e = desc.plugin_operations.read((desc.raddr + desc.size) as usize, &mut buffer);
216        match e {
217            Err(IoError::Parse(io_err)) => assert_eq!(io_err.kind(), io::ErrorKind::UnexpectedEof),
218            _ => assert!(true, "UnexpectedEof Error should have been generated"),
219        };
220        // read at the middle past the the end
221        e = desc.plugin_operations.read((desc.raddr + desc.size - 5) as usize, &mut buffer);
222        match e {
223            Err(IoError::Parse(io_err)) => assert_eq!(io_err.kind(), io::ErrorKind::UnexpectedEof),
224            _ => assert!(true, "UnexpectedEof Error should have been generated"),
225        };
226
227        // read at the start past the end
228        let mut v: Vec<u8> = vec![0; (desc.size + 8) as usize];
229        buffer = &mut v;
230        e = desc.plugin_operations.read(desc.raddr as usize, &mut buffer);
231        match e {
232            Err(IoError::Parse(io_err)) => assert_eq!(io_err.kind(), io::ErrorKind::UnexpectedEof),
233            _ => assert!(true, "UnexpectedEof Error should have been generated"),
234        };
235    }
236    #[test]
237    fn test_read_errors() {
238        operate_on_file(&test_read_errors_cb, DATA);
239    }
240
241    fn test_write_cb(path: &Path) {
242        let mut plugin = plugin();
243        let mut desc = plugin.open(&path.to_string_lossy(), IoMode::READ | IoMode::WRITE).unwrap();
244        let mut buffer: &mut [u8] = &mut [0; 8];
245        // write at the begining
246        desc.plugin_operations.write(desc.raddr as usize, &buffer).unwrap();
247        desc.plugin_operations.read(desc.raddr as usize, &mut buffer).unwrap();
248        assert_eq!(buffer, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
249        // write at the middle
250        desc.plugin_operations.write((desc.raddr + 0x10) as usize, &buffer).unwrap();
251        desc.plugin_operations.read((desc.raddr + 0x10) as usize, &mut buffer).unwrap();
252        assert_eq!(buffer, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
253        // write at the end
254        desc.plugin_operations.write((desc.raddr + 97) as usize, &buffer).unwrap();
255        desc.plugin_operations.read((desc.raddr + 97) as usize, &mut buffer).unwrap();
256        assert_eq!(buffer, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
257    }
258
259    #[test]
260    fn test_write() {
261        operate_on_file(&test_write_cb, DATA);
262    }
263
264    fn test_write_errors_cb(path: &Path) {
265        let mut plugin = plugin();
266        let mut desc = plugin.open(&path.to_string_lossy(), IoMode::READ | IoMode::WRITE).unwrap();
267        let mut buffer: &[u8] = &[0; 8];
268        // write past the end
269        let mut e = desc.plugin_operations.write((desc.raddr + desc.size) as usize, &buffer);
270        match e {
271            Err(IoError::Parse(io_err)) => assert_eq!(io_err.kind(), io::ErrorKind::UnexpectedEof),
272            _ => assert!(true, "UnexpectedEof Error should have been generated"),
273        };
274        // middle at the middle past the the end
275        e = desc.plugin_operations.write((desc.raddr + desc.size - 5) as usize, &buffer);
276        match e {
277            Err(IoError::Parse(io_err)) => assert_eq!(io_err.kind(), io::ErrorKind::UnexpectedEof),
278            _ => assert!(true, "UnexpectedEof Error should have been generated"),
279        };
280        // read at the start past the end
281        let v: Vec<u8> = vec![0; (desc.size + 8) as usize];
282        buffer = &v;
283        e = desc.plugin_operations.write(desc.raddr as usize, &mut buffer);
284        match e {
285            Err(IoError::Parse(io_err)) => assert_eq!(io_err.kind(), io::ErrorKind::UnexpectedEof),
286            _ => assert!(true, "UnexpectedEof Error should have been generated"),
287        };
288    }
289    #[test]
290    fn test_write_errors() {
291        operate_on_file(&test_write_errors_cb, DATA);
292    }
293}