blkar_lib/
show_core.rs

1use std::sync::{Arc, Mutex};
2use std::fmt;
3use file_utils;
4use misc_utils;
5use misc_utils::RequiredLenAndSeekTo;
6
7use cli_utils::report_ref_block_info;
8use cli_utils::setup_ctrlc_handler;
9
10use std::io::SeekFrom;
11
12use json_printer::BracketType;
13
14use progress_report::*;
15
16use file_reader::{FileReader,
17                  FileReaderParam};
18
19use multihash::*;
20
21use general_error::Error;
22
23use sbx_block::Block;
24use sbx_specs::SBX_LARGEST_BLOCK_SIZE;
25use sbx_specs::ver_to_usize;
26use sbx_specs::ver_uses_rs;
27
28use time_utils;
29use block_utils;
30
31use block_utils::RefBlockChoice;
32use sbx_block::BlockType;
33
34use json_printer::JSONPrinter;
35
36#[derive(Clone, Debug)]
37pub struct Stats {
38    pub bytes_processed : u64,
39    pub total_bytes     : u64,
40    meta_block_count    : u64,
41    start_time          : f64,
42    end_time            : f64,
43    json_printer        : Arc<JSONPrinter>,
44}
45
46impl Stats {
47    pub fn new(file_size    : u64,
48               json_printer : &Arc<JSONPrinter>) -> Stats {
49        Stats {
50            bytes_processed   : 0,
51            total_bytes       : file_size,
52            meta_block_count  : 0,
53            start_time        : 0.,
54            end_time          : 0.,
55            json_printer      : Arc::clone(json_printer),
56        }
57    }
58}
59
60impl ProgressReport for Stats {
61    fn start_time_mut(&mut self) -> &mut f64 { &mut self.start_time }
62
63    fn end_time_mut(&mut self)   -> &mut f64 { &mut self.end_time }
64
65    fn units_so_far(&self)       -> u64      { self.bytes_processed }
66
67    fn total_units(&self)        -> u64      { self.total_bytes }
68}
69
70impl fmt::Display for Stats {
71    fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
72        if self.meta_block_count == 0 {
73            write_if!(not_json => f, self.json_printer => "No metadata blocks found";)
74        } else {
75            Ok(())
76        }
77    }
78}
79
80#[derive(Clone, Debug)]
81pub struct Param {
82    show_all            : bool,
83    guess_burst         : bool,
84    force_misalign      : bool,
85    json_printer        : Arc<JSONPrinter>,
86    from_pos            : Option<u64>,
87    to_pos              : Option<u64>,
88    in_file             : String,
89    pr_verbosity_level  : PRVerbosityLevel
90}
91
92impl Param {
93    pub fn new(show_all            : bool,
94               guess_burst         : bool,
95               force_misalign      : bool,
96               json_printer        : &Arc<JSONPrinter>,
97               from_pos            : Option<u64>,
98               to_pos              : Option<u64>,
99               in_file             : &str,
100               pr_verbosity_level  : PRVerbosityLevel) -> Param {
101        Param {
102            show_all,
103            guess_burst,
104            force_misalign,
105            json_printer : Arc::clone(json_printer),
106            from_pos,
107            to_pos,
108            in_file : String::from(in_file),
109            pr_verbosity_level,
110        }
111    }
112}
113
114pub fn show_file(param : &Param)
115                 -> Result<Stats, Error> {
116    let ctrlc_stop_flag = setup_ctrlc_handler(param.json_printer.json_enabled());
117
118    let json_printer = &param.json_printer;
119
120    if param.guess_burst {
121        print_if!(not_json => json_printer => "Guessing burst error resistance level";);
122        print_if!(not_json => json_printer => "";);
123
124        let (ref_block_pos, ref_block) =
125            match block_utils::get_ref_block(&param.in_file,
126                                             RefBlockChoice::MustBe(BlockType::Meta),
127                                             param.pr_verbosity_level,
128                                             param.json_printer.json_enabled(),
129                                             &ctrlc_stop_flag)? {
130                None => { return Err(Error::with_message("Failed to find reference block")); },
131                Some(x) => x,
132            };
133
134        report_ref_block_info(json_printer, ref_block_pos, &ref_block);
135
136        print_if!(not_json => json_printer => "";);
137
138        if ver_uses_rs(ref_block.get_version()) {
139            match block_utils::guess_burst_err_resistance_level(&param.in_file,
140                                                                ref_block_pos,
141                                                                &ref_block) {
142                Err(e)      => { return Err(Error::with_message(&format!("Error encountered when guessing : {}", e))) },
143                Ok(None)    => print_if!(not_json => json_printer => "Failed to guess level";),
144                Ok(Some(x)) => print_maybe_json!(json_printer, "Best guess for burst error resistance level : {}", x => skip_quotes),
145            }
146        } else {
147            print_if!(not_json => json_printer => "Reference block version does not use Reed-Solomon erasure code";);
148            print_field_if_json!(json_printer, "Best guess for burst error resistance level : null" => skip_quotes);
149        }
150
151        print_if!(not_json => json_printer => "";);
152        print_if!(not_json => json_printer => "========================================";);
153        print_if!(not_json => json_printer => "";);
154    }
155
156    let file_size = file_utils::get_file_size(&param.in_file)?;
157
158    let stats = Arc::new(Mutex::new(Stats::new(file_size,
159                                               &param.json_printer)));
160
161    let reporter = ProgressReporter::new(&stats,
162                                         "Metadata block scanning progress",
163                                         "bytes",
164                                         param.pr_verbosity_level,
165                                         param.json_printer.json_enabled());
166
167    let mut block = Block::dummy();
168    let mut buffer : [u8; SBX_LARGEST_BLOCK_SIZE] =
169        [0; SBX_LARGEST_BLOCK_SIZE];
170
171    let mut reader = FileReader::new(&param.in_file,
172                                     FileReaderParam { write    : false,
173                                                       buffered : true   })?;
174
175    // calulate length to read and position to seek to
176    let RequiredLenAndSeekTo { required_len, seek_to } =
177        misc_utils::calc_required_len_and_seek_to_from_byte_range_inc(param.from_pos,
178                                                                      param.to_pos,
179                                                                      param.force_misalign,
180                                                                      stats.lock().unwrap().bytes_processed,
181                                                                      file_size);
182
183    // seek to calculated position
184    reader.seek(SeekFrom::Start(seek_to))?;
185
186    reporter.start();
187
188    let mut meta_block_count : u64 = 0;
189
190    let mut block_pos       : u64;
191    let mut bytes_processed : u64 = 0;
192
193    json_printer.print_open_bracket(Some("blocks"), BracketType::Square);
194
195    loop {
196        break_if_atomic_bool!(ctrlc_stop_flag);
197
198        break_if_reached_required_len!(bytes_processed,
199                                       required_len);
200
201        let lazy_read_res = block_utils::read_block_lazily(&mut block,
202                                                           &mut buffer,
203                                                           &mut reader)?;
204
205        block_pos        = bytes_processed;
206        bytes_processed += lazy_read_res.len_read as u64;
207
208        stats.lock().unwrap().bytes_processed = bytes_processed;
209
210        break_if_eof_seen!(lazy_read_res);
211
212        if !lazy_read_res.usable { continue; }
213
214        if block.is_meta() {
215            reporter.pause();
216
217            json_printer.print_open_bracket(None, BracketType::Curly);
218
219            if param.show_all {
220                if meta_block_count > 0 {
221                    print_if!(not_json => json_printer => "";);
222                }
223                print_maybe_json!(json_printer,       "Metadata block number : {}", meta_block_count => skip_quotes);
224                print_if!(not_json => json_printer => "========================================";);
225            } else {
226                print_field_if_json!(json_printer,    "Metadata block number : {}", meta_block_count => skip_quotes);
227            }
228
229            print_if!(not_json => json_printer =>     "Found at byte          : {} (0x{:X})",
230                      block_pos + seek_to,
231                      block_pos + seek_to;);
232            print_field_if_json!(json_printer,        "Found at byte          : {}",
233                                 block_pos + seek_to => skip_quotes);
234
235            print_if!(not_json => json_printer =>     "";);
236
237            print_maybe_json!(json_printer,           "File UID               : {}",
238                              misc_utils::bytes_to_upper_hex_string(&block.get_uid()));
239            print_maybe_json!(json_printer,           "File name              : {}",
240                              block.get_FNM().unwrap().unwrap_or("N/A".to_string()));
241            print_maybe_json!(json_printer,           "SBX container name     : {}",
242                              block.get_SNM().unwrap().unwrap_or("N/A".to_string()));
243            print_maybe_json!(json_printer,           "SBX container version  : {}",
244                              if ver_uses_rs(block.get_version()) && !json_printer.json_enabled() {
245                                  format!("{} (0x{:X})",
246                                          ver_to_usize(block.get_version()),
247                                          ver_to_usize(block.get_version()))
248                              } else {
249                                  ver_to_usize(block.get_version()).to_string()
250                              });
251            print_maybe_json!(json_printer,           "RS data shard count    : {}",
252                              if ver_uses_rs(block.get_version()) {
253                                  match block.get_RSD().unwrap() {
254                                      None    => null_if_json_else!(json_printer, "N/A").to_string(),
255                                      Some(x) => x.to_string(),
256                                  }
257                              } else {
258                                  null_if_json_else!(json_printer, "version does not use RS").to_string()
259                              }                                                    => skip_quotes);
260            print_maybe_json!(json_printer,           "RS parity shard count  : {}",
261                              if ver_uses_rs(block.get_version()) {
262                                  match block.get_RSP().unwrap() {
263                                      None    => null_if_json_else!(json_printer, "N/A").to_string(),
264                                      Some(x) => x.to_string(),
265                                  }
266                              } else {
267                                  null_if_json_else!(json_printer, "version does not use RS").to_string()
268                              }                                                    => skip_quotes);
269            print_maybe_json!(json_printer,           "File size              : {}", match block.get_FSZ().unwrap() {
270                None    => null_if_json_else!(json_printer, "N/A").to_string(),
271                Some(x) => x.to_string()
272            }                                                                      => skip_quotes);
273            print_maybe_json!(json_printer,           "File modification time : {}", match block.get_FDT().unwrap() {
274                None    => null_if_json_else!(json_printer, "N/A").to_string(),
275                Some(x) => match (time_utils::i64_secs_to_date_time_string(x, time_utils::TimeMode::UTC),
276                                  time_utils::i64_secs_to_date_time_string(x, time_utils::TimeMode::Local)) {
277                    (Some(u), Some(l)) => format!("{} (UTC)  {} (Local)", u, l),
278                    _                  => "Invalid recorded date time".to_string(),
279                }
280            });
281            print_maybe_json!(json_printer,           "SBX encoding time      : {}", match block.get_SDT().unwrap() {
282                None    => null_if_json_else!(json_printer, "N/A").to_string(),
283                Some(x) => match (time_utils::i64_secs_to_date_time_string(x, time_utils::TimeMode::UTC),
284                                  time_utils::i64_secs_to_date_time_string(x, time_utils::TimeMode::Local)) {
285                    (Some(u), Some(l)) => format!("{} (UTC)  {} (Local)", u, l),
286                    _                  => "Invalid recorded date time".to_string(),
287                }
288            });
289            print_maybe_json!(json_printer,           "Hash                   : {}", match block.get_HSH().unwrap() {
290                None    => null_if_json_else!(json_printer, "N/A").to_string(),
291                Some(h) => format!("{} - {}",
292                                   hash_type_to_string(h.0),
293                                   misc_utils::bytes_to_lower_hex_string(&h.1))
294            });
295
296            meta_block_count += 1;
297
298            reporter.resume();
299
300            json_printer.print_close_bracket();
301
302            if !param.show_all { break; }
303        }
304    }
305
306    json_printer.print_close_bracket();
307
308    reporter.stop();
309
310    stats.lock().unwrap().meta_block_count = meta_block_count;
311
312    let stats = stats.lock().unwrap().clone();
313
314    Ok(stats)
315}