runiter_wot/operations/
file.rs

1//  Copyright (C) 2017-2018  The Runiter Project Developers.
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU Affero General Public License as
5// published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU Affero General Public License for more details.
12//
13// You should have received a copy of the GNU Affero General Public License
14// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
16//! Provide a trait and implementation to read and write `WebOfTrust` to disk.
17
18use data::NodeId;
19use std::fs;
20use std::fs::File;
21use std::io;
22use std::io::prelude::*;
23
24use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
25
26use data::WebOfTrust;
27
28/// Results of `WebOfTrust` parsing from binary file.
29#[derive(Debug)]
30pub enum WotParseError {
31    /// FailToOpenFile
32    FailToOpenFile(io::Error),
33
34    /// IOError
35    IOError(io::Error),
36}
37
38impl From<io::Error> for WotParseError {
39    fn from(e: io::Error) -> WotParseError {
40        WotParseError::IOError(e)
41    }
42}
43
44/// Results of `WebOfTrust` writing to binary file.
45#[derive(Debug)]
46pub enum WotWriteError {
47    /// WrongWotSize
48    WrongWotSize(),
49
50    /// FailToCreateFile
51    FailToCreateFile(io::Error),
52
53    /// FailToWriteInFile
54    FailToWriteInFile(io::Error),
55}
56
57impl From<io::Error> for WotWriteError {
58    fn from(e: io::Error) -> WotWriteError {
59        WotWriteError::FailToWriteInFile(e)
60    }
61}
62
63/// Provide Read/Write functions for `WebOfTrust` objects.
64pub trait FileFormater {
65    /// Try to read a `WebOfTrust` from a file.
66    fn from_file<T: WebOfTrust>(
67        &self,
68        path: &str,
69        max_links: usize,
70    ) -> Result<(T, Vec<u8>), WotParseError>;
71
72    /// Tru to write a `WebOfTrust` in a file.
73    fn to_file<T: WebOfTrust>(&self, wot: &T, data: &[u8], path: &str)
74        -> Result<(), WotWriteError>;
75}
76
77/// Read and write WebOfTrust in a binary format.
78#[derive(Debug, Clone, Copy)]
79pub struct BinaryFileFormater;
80
81impl FileFormater for BinaryFileFormater {
82    /// Try to read a `WebOfTrust` from a file.
83    fn from_file<T: WebOfTrust>(
84        &self,
85        path: &str,
86        max_links: usize,
87    ) -> Result<(T, Vec<u8>), WotParseError> {
88        let mut wot = T::new(max_links);
89
90        let file_size = fs::metadata(path).expect("fail to read wotb file !").len();
91        let mut file_pointing_to_blockstamp_size: Vec<u8> = vec![0; file_size as usize];
92        match File::open(path) {
93            Ok(mut file) => {
94                file.read_exact(&mut file_pointing_to_blockstamp_size.as_mut_slice())?;
95            }
96            Err(e) => return Err(WotParseError::FailToOpenFile(e)),
97        };
98        // Read up to 4 bytes (blockstamp_size)
99        let mut file_pointing_to_blockstamp = file_pointing_to_blockstamp_size.split_off(4);
100        // Get blockstamp size
101        let mut buf = &file_pointing_to_blockstamp_size[..];
102        let blockstamp_size = buf.read_u32::<BigEndian>().unwrap();
103        // Read up to blockstamp_size bytes (blockstamp)
104        let mut file_pointing_to_nodes_count =
105            file_pointing_to_blockstamp.split_off(blockstamp_size as usize);
106        // Read up to 4 bytes (nodes_count)
107        let mut file_pointing_to_nodes_states = file_pointing_to_nodes_count.split_off(4);
108        // Read nodes_count
109        let mut buf = &file_pointing_to_nodes_count[..];
110        let nodes_count = buf.read_u32::<BigEndian>().unwrap();
111        // Calcule nodes_state size
112        let nodes_states_size = match nodes_count % 8 {
113            0 => nodes_count / 8,
114            _ => (nodes_count / 8) + 1,
115        };
116        // Read up to nodes_states_size bytes (nodes_states)
117        let file_pointing_to_links =
118            file_pointing_to_nodes_states.split_off(nodes_states_size as usize);
119        let count_total_bytes_read = file_pointing_to_links.len()
120            + nodes_states_size as usize
121            + 4
122            + blockstamp_size as usize
123            + 4;
124        if count_total_bytes_read != file_size as usize {
125            panic!("not read all wot file !");
126        }
127        // Apply nodes state
128        let mut count_remaining_nodes = nodes_count;
129        for byte in file_pointing_to_nodes_states {
130            let mut byte_integer = u8::from_be(byte);
131            let mut factor: u8 = 128;
132            for _i in 0..8 {
133                if count_remaining_nodes > 0 {
134                    wot.add_node();
135                    if byte_integer >= factor {
136                        byte_integer -= factor;
137                    } else {
138                        let _test = wot.set_enabled(
139                            NodeId((nodes_count - count_remaining_nodes) as usize),
140                            false,
141                        );
142                    }
143                    count_remaining_nodes -= 1;
144                }
145                factor /= 2;
146            }
147        }
148        // Apply links
149        let mut buffer_3b: Vec<u8> = Vec::with_capacity(3);
150        let mut count_bytes = 0;
151        let mut remaining_links: u8 = 0;
152        let mut target: u32 = 0;
153        for byte in file_pointing_to_links {
154            if remaining_links == 0 {
155                target += 1;
156                remaining_links = u8::from_be(byte);
157                count_bytes = 0;
158            } else {
159                buffer_3b.push(byte);
160                if count_bytes % 3 == 2 {
161                    let mut buf = &buffer_3b.clone()[..];
162                    let source = buf.read_u24::<BigEndian>().expect("fail to parse source");
163                    wot.add_link(NodeId(source as usize), NodeId((target - 1) as usize));
164                    remaining_links -= 1;
165                    buffer_3b.clear();
166                }
167                count_bytes += 1;
168            }
169        }
170        if count_bytes % 3 != 0 {
171            panic!("not read all wot file !");
172        }
173        Ok((wot, file_pointing_to_blockstamp))
174    }
175
176    /// Try to write a `WebOfTrust` in a file.
177    fn to_file<T: WebOfTrust>(
178        &self,
179        wot: &T,
180        data: &[u8],
181        path: &str,
182    ) -> Result<(), WotWriteError> {
183        let mut buffer: Vec<u8> = Vec::new();
184        // Write blockstamp size
185        let blockstamp_size = data.len() as u32;
186        let mut bytes: Vec<u8> = Vec::with_capacity(4);
187        bytes.write_u32::<BigEndian>(blockstamp_size).unwrap();
188        buffer.append(&mut bytes);
189        // Write blockstamp
190        buffer.append(&mut data.to_vec());
191        // Write nodes_count
192        let nodes_count = wot.size() as u32;
193        let mut bytes: Vec<u8> = Vec::with_capacity(4);
194        bytes.write_u32::<BigEndian>(nodes_count).unwrap();
195        buffer.append(&mut bytes);
196        // Write enable state by groups of 8
197        let mut enable_states: u8 = 0;
198        let mut factor: u8 = 128;
199        for n in 0..nodes_count {
200            match wot.is_enabled(NodeId(n as usize)) {
201                Some(enable) => {
202                    if enable {
203                        enable_states += factor;
204                    }
205                }
206                None => {
207                    return Err(WotWriteError::WrongWotSize());
208                }
209            }
210            if n % 8 == 7 {
211                factor = 128;
212                let mut tmp_buf = Vec::with_capacity(1);
213                tmp_buf.write_u8(enable_states).unwrap();
214                buffer.append(&mut tmp_buf);
215                enable_states = 0;
216            } else {
217                factor /= 2;
218            }
219        }
220        // nodes_states padding
221        if nodes_count % 8 != 7 {
222            let mut tmp_buf = Vec::with_capacity(1);
223            tmp_buf.write_u8(enable_states).unwrap();
224            buffer.append(&mut tmp_buf);
225        }
226        // Write links
227        for n in 0..nodes_count {
228            if let Some(sources) = wot.get_links_source(NodeId(n as usize)) {
229                // Write sources_counts
230                let mut bytes = Vec::with_capacity(1);
231                bytes.write_u8(sources.len() as u8).unwrap();
232                buffer.append(&mut bytes);
233                for source in &sources {
234                    // Write source
235                    let mut bytes: Vec<u8> = Vec::with_capacity(3);
236                    bytes.write_u24::<BigEndian>(source.0 as u32).unwrap();
237                    buffer.append(&mut bytes);
238                }
239            };
240        }
241        // Create or open file
242        let mut file = match File::create(path) {
243            Ok(file) => file,
244            Err(e) => return Err(WotWriteError::FailToCreateFile(e)),
245        };
246        // Write buffer in file
247        file.write_all(&buffer)?;
248
249        Ok(())
250    }
251}