1use std::ffi::{OsStr, OsString};
48use std::fs::File;
49use std::io::{self, Read, Write};
50use std::path::{Path, PathBuf};
51use std::process::ExitCode;
52
53mod bits;
54mod messages;
55
56use bits::{BitIn, BitOut};
57use messages as m;
58
59const MAX_FILENAME: usize = 2000;
61const MAX_HANDLED_BLOCKS: i64 = 50_000;
63const BLOCK_MAGIC: u64 = 0x3141_5926_5359;
65const END_MAGIC: u64 = 0x1772_4538_5090;
67const SPLIT: u8 = if cfg!(windows) { b'\\' } else { b'/' };
69
70pub fn run(argv0: OsString, args: Vec<OsString>) -> ExitCode {
73 super::host::reset_sigpipe();
76 let args: Vec<Vec<u8>> = args.iter().map(|a| bytes(a)).collect();
77 let code = recover(&bytes(&argv0), &args, &mut RealFs, &mut io::stderr());
78 ExitCode::from(code)
79}
80
81#[doc(hidden)]
86pub fn run_in(dir: &Path, argv0: &[u8], args: &[OsString]) -> (i32, Vec<u8>) {
87 let args: Vec<Vec<u8>> = args.iter().map(|a| bytes(a)).collect();
88 let mut err = Vec::new();
89 let mut fs = DirFs(dir.to_path_buf());
90 let code = recover(argv0, &args, &mut fs, &mut err);
91 (i32::from(code), err)
92}
93
94#[doc(hidden)]
98pub trait Fs {
99 fn open(&mut self, name: &[u8]) -> io::Result<Box<dyn Read>>;
101 fn create(&mut self, name: &[u8]) -> io::Result<Box<dyn Write>>;
103}
104
105pub(crate) struct RealFs;
107
108impl Fs for RealFs {
109 fn open(&mut self, name: &[u8]) -> io::Result<Box<dyn Read>> {
110 Ok(Box::new(File::open(path(name))?))
111 }
112 fn create(&mut self, name: &[u8]) -> io::Result<Box<dyn Write>> {
113 create(&path(name))
114 }
115}
116
117struct DirFs(PathBuf);
119
120impl Fs for DirFs {
121 fn open(&mut self, name: &[u8]) -> io::Result<Box<dyn Read>> {
122 Ok(Box::new(File::open(self.0.join(path(name)))?))
123 }
124 fn create(&mut self, name: &[u8]) -> io::Result<Box<dyn Write>> {
125 create(&self.0.join(path(name)))
126 }
127}
128
129fn create(p: &Path) -> io::Result<Box<dyn Write>> {
131 let f = File::options()
132 .write(true)
133 .create(true)
134 .truncate(true)
135 .open(p)?;
136 Ok(Box::new(f))
137}
138
139fn bytes(s: &OsStr) -> Vec<u8> {
140 #[cfg(unix)]
141 {
142 use std::os::unix::ffi::OsStrExt;
143 s.as_bytes().to_vec()
144 }
145 #[cfg(not(unix))]
146 {
147 s.to_string_lossy().into_owned().into_bytes()
148 }
149}
150
151fn path(b: &[u8]) -> PathBuf {
152 #[cfg(unix)]
153 {
154 use std::os::unix::ffi::OsStrExt;
155 PathBuf::from(OsStr::from_bytes(b))
156 }
157 #[cfg(not(unix))]
158 {
159 PathBuf::from(String::from_utf8_lossy(b).into_owned())
160 }
161}
162
163fn strerror(e: &io::Error) -> Vec<u8> {
165 let s = e.to_string();
166 match e.raw_os_error() {
167 Some(n) => {
168 let suffix = format!(" (os error {n})");
169 s.strip_suffix(&suffix).unwrap_or(&s).as_bytes().to_vec()
170 }
171 None => s.into_bytes(),
172 }
173}
174
175struct Exit(u8);
177
178struct Recover<'a> {
180 prog: &'a [u8],
181 in_name: &'a [u8],
182 err: &'a mut dyn Write,
183}
184
185impl Recover<'_> {
186 fn say(&mut self, fmt: &str, args: &[&[u8]]) {
187 m::printf(self.err, fmt, args);
188 }
189
190 fn io_error(&mut self, e: &io::Error) -> Exit {
192 let (p, n) = (self.prog, self.in_name);
193 self.say(m::IO_ERROR, &[p, n]);
194 self.say(m::PERROR, &[p, &strerror(e)]);
195 self.say(m::MAY_BE_INCOMPLETE, &[p]);
196 Exit(1)
197 }
198
199 fn bit(&mut self, input: &mut BitIn) -> Result<Option<u32>, Exit> {
200 input.bit().map_err(|e| self.io_error(&e))
201 }
202}
203
204struct Found {
207 blocks: Vec<(u64, u64)>,
208 hi: u32,
209 lo: u32,
210}
211
212fn shift(hi: &mut u32, lo: &mut u32, b: u32) -> bool {
215 *hi = (*hi << 1) | (*lo >> 31);
216 *lo = (*lo << 1) | (b & 1);
217 let w = (u64::from(*hi & 0xffff) << 32) | u64::from(*lo);
218 w == BLOCK_MAGIC || w == END_MAGIC
219}
220
221#[doc(hidden)]
225pub fn recover(prog: &[u8], args: &[Vec<u8>], fs: &mut dyn Fs, err: &mut dyn Write) -> u8 {
226 let prog = &prog[..prog.len().min(MAX_FILENAME - 1)];
228 m::printf(err, m::BANNER, &[]);
229 let [in_name] = args else {
230 m::printf(err, m::USAGE, &[prog, prog]);
231 m::printf(err, m::USAGE_SIZE, &[]);
232 return 1;
233 };
234 if in_name.len() >= MAX_FILENAME - 20 {
235 let n = in_name.len().to_string();
236 m::printf(err, m::NAME_TOO_LONG, &[prog, n.as_bytes()]);
237 return 1;
238 }
239 let mut r = Recover { prog, in_name, err };
240 match r.both_passes(fs) {
241 Ok(()) => 0,
242 Err(Exit(c)) => c,
243 }
244}
245
246impl Recover<'_> {
247 fn both_passes(&mut self, fs: &mut dyn Fs) -> Result<(), Exit> {
248 let (p, n) = (self.prog, self.in_name);
249 let Ok(input) = fs.open(n) else {
250 self.say(m::CANT_READ, &[p, n]);
251 return Err(Exit(1));
252 };
253 self.say(m::SEARCHING, &[p]);
254 let found = self.find_blocks(&mut BitIn::new(input))?;
255 if found.blocks.is_empty() {
256 self.say(m::NO_BLOCKS, &[p]);
257 return Err(Exit(1));
258 }
259 self.say(m::SPLITTING, &[p]);
260 let Ok(input) = fs.open(n) else {
261 self.say(m::CANT_OPEN, &[p, n]);
262 return Err(Exit(1));
263 };
264 self.split(&mut BitIn::new(input), found, fs)?;
265 self.say(m::FINISHED, &[p]);
266 Ok(())
267 }
268
269 fn find_blocks(&mut self, input: &mut BitIn) -> Result<Found, Exit> {
274 let (mut hi, mut lo) = (0u32, 0u32);
275 let mut bits_read: u64 = 0;
276 let mut curr_block: i64 = 0;
277 let mut b_start: u64 = 0; let mut blocks: Vec<(u64, u64)> = Vec::new();
279 loop {
280 let b = self.bit(input)?;
281 bits_read = bits_read.wrapping_add(1);
282 let Some(b) = b else {
283 if bits_read >= b_start && bits_read - b_start >= 40 {
284 let b_end = bits_read - 1;
285 if curr_block > 0 {
286 let c = curr_block.to_string();
287 self.say(
288 m::BLOCK_INCOMPLETE,
289 &[c.as_bytes(), num(b_start).as_bytes(), num(b_end).as_bytes()],
290 );
291 }
292 }
293 break;
294 };
295 if shift(&mut hi, &mut lo, b) {
296 let b_end = bits_read.saturating_sub(49);
297 if curr_block > 0 && b_end.wrapping_sub(b_start) >= 130 {
298 let c = (blocks.len() + 1).to_string();
299 self.say(
300 m::BLOCK,
301 &[c.as_bytes(), num(b_start).as_bytes(), num(b_end).as_bytes()],
302 );
303 blocks.push((b_start, b_end));
304 }
305 if curr_block >= MAX_HANDLED_BLOCKS {
306 let (p, n) = (self.prog, self.in_name);
307 let max = MAX_HANDLED_BLOCKS.to_string();
308 self.say(m::TOO_MANY_BLOCKS, &[p, n, max.as_bytes(), p, p]);
309 return Err(Exit(1));
310 }
311 curr_block += 1;
312 b_start = bits_read;
313 }
314 }
315 Ok(Found { blocks, hi, lo })
316 }
317
318 fn split(&mut self, input: &mut BitIn, found: Found, fs: &mut dyn Fs) -> Result<(), Exit> {
324 let Found {
325 blocks,
326 mut hi,
327 mut lo,
328 } = found;
329 let at = |i: usize| blocks.get(i).copied().unwrap_or((0, 0));
330 let mut bits_read: u64 = 0;
331 let mut wr_block: usize = 0;
332 let mut block_crc: u32 = 0;
333 let mut out: Option<BitOut> = None;
334 while let Some(b) = self.bit(input)? {
335 shift(&mut hi, &mut lo, b);
336 let (start, end) = at(wr_block);
337 if bits_read == start.wrapping_add(47) {
338 block_crc = (hi << 16) | (lo >> 16);
339 }
340 if let Some(w) = out.as_mut()
341 && bits_read >= start
342 && bits_read <= end
343 {
344 w.put_bit(b).map_err(|e| self.io_error(&e))?;
345 }
346 bits_read = bits_read.wrapping_add(1);
347 if bits_read == end.wrapping_add(1) {
348 if let Some(mut w) = out.take() {
349 let r: io::Result<()> = (|| {
350 for c in [0x17, 0x72, 0x45, 0x38, 0x50, 0x90] {
351 w.put_u8(c)?;
352 }
353 w.put_u32(block_crc)?;
354 w.close()
355 })();
356 r.map_err(|e| self.io_error(&e))?;
357 }
358 if wr_block >= blocks.len() {
359 break;
360 }
361 wr_block += 1;
362 } else if bits_read == start {
363 let name = out_name(self.in_name, wr_block + 1);
364 let k = (wr_block + 1).to_string();
365 self.say(m::WRITING, &[k.as_bytes(), &name]);
366 let Ok(f) = fs.create(&name) else {
367 let p = self.prog;
368 self.say(m::CANT_WRITE, &[p, &name]);
369 return Err(Exit(1));
370 };
371 let mut w = BitOut::new(f);
372 let r: io::Result<()> = (|| {
373 for c in *b"BZh9" {
374 w.put_u8(c)?;
375 }
376 for c in [0x31, 0x41, 0x59, 0x26, 0x53, 0x59] {
377 w.put_u8(c)?;
378 }
379 Ok(())
380 })();
381 r.map_err(|e| self.io_error(&e))?;
382 out = Some(w);
383 }
384 }
385 Ok(())
386 }
387}
388
389fn num(n: u64) -> String {
391 n.to_string()
392}
393
394fn out_name(in_name: &[u8], k: usize) -> Vec<u8> {
399 let ofs = in_name
400 .iter()
401 .rposition(|&c| c == SPLIT)
402 .map_or(0, |i| i + 1);
403 let mut v = in_name[..ofs].to_vec();
404 v.extend_from_slice(format!("rec{k:5}").replace(' ', "0").as_bytes());
405 v.extend_from_slice(&in_name[ofs..]);
406 if !(v.len() > 4 && v.ends_with(b".bz2")) {
407 v.extend_from_slice(b".bz2");
408 }
409 v
410}
411
412#[cfg(test)]
413mod tests {
414 use super::*;
415 use std::cell::{Cell, RefCell};
416 use std::collections::BTreeMap;
417 use std::rc::Rc;
418
419 #[derive(Default)]
421 struct MemFs {
422 files: BTreeMap<Vec<u8>, Rc<RefCell<Vec<u8>>>>,
423 read: Rc<Cell<u64>>,
424 opens: usize,
425 }
426
427 struct Counting {
428 data: Rc<RefCell<Vec<u8>>>,
429 pos: usize,
430 read: Rc<Cell<u64>>,
431 }
432 impl Read for Counting {
433 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
434 let d = self.data.borrow();
435 let n = buf.len().min(d.len() - self.pos);
436 buf[..n].copy_from_slice(&d[self.pos..self.pos + n]);
437 self.pos += n;
438 self.read.set(self.read.get() + n as u64);
439 Ok(n)
440 }
441 }
442
443 struct Shared(Rc<RefCell<Vec<u8>>>);
444 impl Write for Shared {
445 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
446 self.0.borrow_mut().extend_from_slice(b);
447 Ok(b.len())
448 }
449 fn flush(&mut self) -> io::Result<()> {
450 Ok(())
451 }
452 }
453
454 impl Fs for MemFs {
455 fn open(&mut self, name: &[u8]) -> io::Result<Box<dyn Read>> {
456 self.opens += 1;
457 let data = self
458 .files
459 .get(name)
460 .cloned()
461 .ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))?;
462 Ok(Box::new(Counting {
463 data,
464 pos: 0,
465 read: self.read.clone(),
466 }))
467 }
468 fn create(&mut self, name: &[u8]) -> io::Result<Box<dyn Write>> {
469 let v = Rc::new(RefCell::new(Vec::new()));
470 self.files.insert(name.to_vec(), v.clone());
471 Ok(Box::new(Shared(v)))
472 }
473 }
474
475 fn synthetic(n: usize, body: usize) -> Vec<u8> {
478 let mut v = b"BZh9".to_vec();
479 for i in 0..n {
480 v.extend_from_slice(&[0x31, 0x41, 0x59, 0x26, 0x53, 0x59]);
481 v.extend_from_slice(&(i as u32).wrapping_mul(0x9e37_79b9).to_be_bytes());
482 v.extend((0..body).map(|k| (k * 7 + i) as u8 | 0x80));
483 }
484 v.extend_from_slice(&[0x17, 0x72, 0x45, 0x38, 0x50, 0x90, 1, 2, 3, 4]);
485 v
486 }
487
488 fn run_mem(data: Vec<u8>) -> (u8, MemFs, String) {
489 let mut fs = MemFs::default();
490 fs.files
491 .insert(b"d.bz2".to_vec(), Rc::new(RefCell::new(data)));
492 let mut err = Vec::new();
493 let code = recover(b"bzip2recover", &[b"d.bz2".to_vec()], &mut fs, &mut err);
494 (code, fs, String::from_utf8(err).unwrap())
495 }
496
497 #[test]
500 fn the_input_is_read_at_most_twice() {
501 for (blocks, body) in [(1, 40), (7, 3000), (300, 25), (2000, 13)] {
502 let data = synthetic(blocks, body);
503 let len = data.len() as u64;
504 let (code, fs, err) = run_mem(data);
505 assert_eq!(code, 0, "{err}");
506 assert_eq!(fs.opens, 2);
507 assert_eq!(fs.read.get(), 2 * len, "{blocks} blocks of {body}");
508 }
509 for data in [Vec::new(), b"not bzip2 at all".repeat(1000)] {
511 let len = data.len() as u64;
512 let (code, fs, _) = run_mem(data);
513 assert_eq!(code, 1);
514 assert_eq!(fs.read.get(), len);
515 }
516 }
517
518 #[test]
519 fn each_block_becomes_a_stream_with_its_crc_as_the_combined_crc() {
520 let data = synthetic(2, 30);
521 let (code, fs, err) = run_mem(data);
522 assert_eq!(code, 0, "{err}");
523 let rec1 = fs.files[&b"rec00001d.bz2".to_vec()].borrow().clone();
524 let mut want = b"BZh9".to_vec();
525 want.extend_from_slice(&[0x31, 0x41, 0x59, 0x26, 0x53, 0x59]);
526 let crc = 0u32.to_be_bytes();
527 want.extend_from_slice(&crc);
528 want.extend((0..30).map(|k| (k * 7) as u8 | 0x80));
529 want.extend_from_slice(&[0x17, 0x72, 0x45, 0x38, 0x50, 0x90]);
530 want.extend_from_slice(&crc);
531 assert_eq!(rec1, want);
533 let mut line = Vec::new();
534 m::printf(&mut line, m::WRITING, &[b"2", b"rec00002d.bz2"]);
535 assert!(err.contains(std::str::from_utf8(&line).unwrap()));
536 assert!(err.ends_with("bzip2recover: finished\n"));
537 }
538
539 #[test]
540 fn output_names_split_at_the_last_slash_and_keep_one_bz2() {
541 let n = |s: &str, k| String::from_utf8(out_name(s.as_bytes(), k)).unwrap();
542 assert_eq!(n("d.bz2", 1), "rec00001d.bz2");
543 assert_eq!(n("a/b/d.bz2", 12), "a/b/rec00012d.bz2");
544 assert_eq!(n("d.dat", 50000), "rec50000d.dat.bz2");
545 assert_eq!(n("my file", 3), "rec00003my file.bz2");
546 assert_eq!(n(".bz2", 1), "rec00001.bz2");
547 assert_eq!(n("dir/", 1), "dir/rec00001.bz2");
548 }
549
550 #[test]
551 fn usage_and_length_limits() {
552 let mut fs = MemFs::default();
553 let mut err = Vec::new();
554 assert_eq!(recover(b"/x/bzip2recover", &[], &mut fs, &mut err), 1);
555 assert_eq!(
556 String::from_utf8(err).unwrap(),
557 "bzip2recover 1.0.8: extracts blocks from damaged .bz2 files.\n\
558 /x/bzip2recover: usage is `/x/bzip2recover damaged_file_name'.\n\
559 \trestrictions on size of recovered file: None\n"
560 );
561 for (len, refused) in [(1979, false), (1980, true)] {
562 let mut err = Vec::new();
563 assert_eq!(recover(b"p", &[vec![b'a'; len]], &mut fs, &mut err), 1);
564 let err = String::from_utf8(err).unwrap();
565 assert_eq!(
566 err.contains(&format!("suspiciously (>= {len} chars) long. Bye!")),
567 refused,
568 "{err}"
569 );
570 }
571 let mut err = Vec::new();
573 recover(&[b'p'; 2500], &[], &mut fs, &mut err);
574 let first = err.split(|&c| c == b'\n').nth(1).unwrap();
575 assert!(first.starts_with(&[b'p'; 1999]) && first[1999] == b':');
576 }
577}