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:
parent
98b894a968
commit
dc4a2854c0
@ -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
42
examples/pxarcmd.rs
Normal 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(())
|
||||
}
|
@ -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),
|
||||
|
Loading…
x
Reference in New Issue
Block a user