warp/warp-runner/src/extractor.rs
Reisz d9aeb582a2 Upgrade to Rust 2021
- Fix lints
- Formatting pass
- Add Cargo.lock

Use anyhow to improve error handling

Fix errors during directory removal in Windows

Update dependencies

Switch linux build to musl

Switch to musl

Set version to 0.4.0

Allow using uid instead of mtime

Remove name duplication with runner map

Fixing typo
2024-05-16 08:33:21 +02:00

121 lines
3.7 KiB
Rust

use anyhow::{anyhow, Context, Result};
use bincode::Options;
use flate2::read::GzDecoder;
use log::trace;
use memmem::{Searcher, TwoWaySearcher};
use std::fs::File;
use std::io;
use std::io::{BufReader, Read, Seek, SeekFrom};
use std::path::Path;
use tar::Archive;
use warp_args::{bincode_options, Args, WARP_ARGS_MAGIC};
struct FileSearcher<'a> {
buf_reader: BufReader<File>,
searcher: TwoWaySearcher<'a>,
offs: usize,
}
impl<'a> FileSearcher<'a> {
fn new(path: &'a Path, magic: &'a [u8]) -> io::Result<FileSearcher<'a>> {
let file = File::open(path)?;
Ok(FileSearcher {
buf_reader: BufReader::new(file),
searcher: TwoWaySearcher::new(magic),
offs: 0,
})
}
}
impl<'a> Iterator for FileSearcher<'a> {
type Item = io::Result<usize>;
fn next(&mut self) -> Option<io::Result<usize>> {
let mut buf = [0; 32 * 1024];
let ret;
match self.buf_reader.seek(SeekFrom::Start(self.offs as u64)) {
Ok(_) => {}
Err(e) => return Some(Err(e)),
}
loop {
match self.buf_reader.read(&mut buf[..]) {
Ok(0) => {
ret = None;
break;
}
Ok(n) => {
match self.searcher.search_in(&buf) {
Some(pos) => {
self.offs += pos;
ret = Some(Ok(self.offs));
self.offs += 1; // one past the match so we can try again if necessary
break;
}
None => self.offs += n,
}
}
Err(e) => {
ret = Some(Err(e));
break;
}
}
}
ret
}
}
const GZIP_MAGIC: &[u8] = b"\x1f\x8b\x08";
pub fn extract_to(src: &Path, dst: &Path) -> Result<()> {
FileSearcher::new(src, GZIP_MAGIC)
.context("failed searching own binary")?
.map(Result::unwrap)
.find(|offs| extract_at_offset(src, *offs, dst).unwrap())
.ok_or_else(|| anyhow!("No tarball found inside binary file {}", src.display()))
.map(|offs| {
trace!(
"tarball found at offset {} was extracted successfully",
offs
);
})
}
fn extract_at_offset(src: &Path, offs: usize, dst: &Path) -> Result<bool> {
let mut f = File::open(src)
.with_context(|| format!("Failed to open file to extract from: {}", src.display()))?;
f.seek(SeekFrom::Start(offs as u64))
.with_context(|| format!("Failed to read file to extract from: {}", src.display()))?;
let gz = GzDecoder::new(f);
let mut tar = Archive::new(gz);
Ok(tar.unpack(dst).is_ok())
}
pub fn get_args(src: &Path) -> Result<Args> {
FileSearcher::new(src, WARP_ARGS_MAGIC)
.context("failed searching own binary")?
.map(Result::unwrap)
.find_map(|offs| {
Some((
offs,
extract_args_at_offset(src, offs + WARP_ARGS_MAGIC.len()).unwrap()?,
))
})
.ok_or_else(|| anyhow!("No arguments found inside binary file {}", src.display()))
.map(|(offs, args)| {
trace!("args found at offset {} was extracted successfully", offs);
args
})
}
fn extract_args_at_offset(src: &Path, offs: usize) -> Result<Option<Args>> {
let mut f = File::open(src)
.with_context(|| format!("Failed to open file to extract from: {}", src.display()))?;
f.seek(SeekFrom::Start(offs as u64))
.with_context(|| format!("Failed to read file to extract from: {}", src.display()))?;
Ok(bincode_options().deserialize_from(f).ok())
}