5
0
mirror of git://git.proxmox.com/git/pxar.git synced 2025-03-14 04:59:01 +03:00

implement lookup over multiple components

add a pxarcmd test example
make 'failure' an optional dependency as we only use it in
examples right now.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2020-02-19 16:03:42 +01:00
parent 98b894a968
commit dc4a2854c0
3 changed files with 105 additions and 7 deletions

View File

@ -12,9 +12,9 @@ required-features = [ "async-example" ]
[dependencies]
bitflags = "1.2.1"
endian_trait = { version = "0.6", features = ["arrays"] }
failure = "0.1"
siphasher = "0.3"
failure = { version = "0.1", optional = true }
futures = { version = "0.3.1", optional = true }
tokio = { version = "0.2.10", optional = true, default-features = false }
@ -23,6 +23,7 @@ default = [ "futures-io", "tokio-io" ]
futures-io = [ "futures" ]
tokio-io = [ "tokio" ]
async-example = [
"failure",
"futures-io",
"tokio-io",
"tokio/fs",

42
examples/pxarcmd.rs Normal file
View File

@ -0,0 +1,42 @@
use std::os::unix::ffi::OsStrExt;
use std::sync::Arc;
use failure::{bail, format_err, Error};
use pxar::accessor::Accessor;
fn main() -> Result<(), Error> {
let mut args = std::env::args_os().skip(1);
let cmd = args
.next()
.ok_or_else(|| format_err!("expected a command (ls or cat)"))?;
let cmd = cmd
.to_str()
.ok_or_else(|| format_err!("expected a valid command string (utf-8)"))?;
match cmd {
"ls" | "cat" => (),
_ => bail!("valid commands are: cat, ls"),
}
let file = args
.next()
.ok_or_else(|| format_err!("expected a file name"))?;
let mut accessor = Accessor::open(file)?;
let mut dir = accessor.open_root_ref()?;
for file in args {
let entry = dir
.lookup(&file)?
.ok_or_else(|| format_err!("no such file in archive: {:?}", file))?;
if file.as_bytes().ends_with(b"/") {
for file in entry.enter_directory()?.read_dir() {
println!("{:?}", file?.file_name());
}
} else {
println!("{:?}", entry.metadata());
}
}
Ok(())
}

View File

@ -1,8 +1,8 @@
//! Random access for PXAR files.
use std::ffi::OsString;
use std::ffi::{OsStr, OsString};
use std::io;
use std::mem::{size_of, size_of_val, MaybeUninit};
use std::mem::{self, size_of, size_of_val, MaybeUninit};
use std::ops::Range;
use std::os::unix::ffi::{OsStrExt, OsStringExt};
use std::path::{Path, PathBuf};
@ -193,6 +193,11 @@ impl<T: Clone + ReadAt> DirectoryImpl<T> {
self.entry_ofs + self.size
}
#[inline]
fn entry_range(&self) -> Range<u64> {
self.entry_ofs..self.end_offset()
}
#[inline]
fn table_size(&self) -> u64 {
(self.end_offset() - self.goodbye_ofs) - (size_of::<format::Header>() as u64)
@ -227,9 +232,7 @@ impl<T: Clone + ReadAt> DirectoryImpl<T> {
/// Get a decoder for the directory contents.
pub(crate) async fn decode_full(&self) -> io::Result<DecoderImpl<SeqReadAtAdapter<T>>> {
let (dir, decoder) = self
.decode_one_entry(self.entry_ofs..(self.entry_ofs + self.size), None)
.await?;
let (dir, decoder) = self.decode_one_entry(self.entry_range(), None).await?;
if !dir.is_dir() {
io_bail!("directory does not seem to be a directory");
}
@ -268,9 +271,61 @@ impl<T: Clone + ReadAt> DirectoryImpl<T> {
format::search_binary_tree_array_by(&self.table, |i| hash.cmp(&i.hash))
}
async fn lookup_self(&self) -> io::Result<FileEntryImpl<T>> {
let (entry, decoder) = self.decode_one_entry(self.entry_range(), None).await?;
Ok(FileEntryImpl {
input: self.input.clone(),
entry,
decoder: Some(decoder),
end_offset: self.end_offset(),
})
}
/// Lookup a directory entry.
pub async fn lookup(&self, path: &Path) -> io::Result<Option<FileEntryImpl<T>>> {
let hash = format::hash_filename(path.as_os_str().as_bytes());
let mut cur: Option<FileEntryImpl<T>> = None;
let mut first = true;
for component in path.components() {
use std::path::Component;
let first = mem::replace(&mut first, false);
let component = match component {
Component::Normal(path) => path,
Component::ParentDir => io_bail!("cannot enter parent directory in archive"),
Component::RootDir | Component::CurDir if first => {
cur = Some(self.lookup_self().await?);
continue;
}
Component::CurDir => continue,
_ => io_bail!("invalid component in path"),
};
let next = match cur {
Some(entry) => {
entry
.enter_directory()
.await?
.lookup_component(component)
.await?
}
None => self.lookup_component(component).await?,
};
if next.is_none() {
return Ok(None);
}
cur = next;
}
Ok(cur)
}
/// Lookup a single directory entry component (does not handle multiple components in path)
pub async fn lookup_component(&self, path: &OsStr) -> io::Result<Option<FileEntryImpl<T>>> {
let hash = format::hash_filename(path.as_bytes());
let index = match self.lookup_hash_position(hash) {
Some(index) => index,
None => return Ok(None),