use crate::accessor::{self, cache::Cache, ReadAt};
use crate::decoder::aio::Decoder;
-use crate::format::{GoodbyeItem, Hardlink};
+use crate::format::GoodbyeItem;
use crate::poll_fn::poll_fn;
use crate::Entry;
}
/// Allow opening a regular file from a specified range.
- pub async unsafe fn open_file_at_range(&self, range: Range<u64>) -> io::Result<FileEntry<T>> {
+ pub async unsafe fn open_file_at_range(
+ &self,
+ entry_range_info: &accessor::EntryRangeInfo,
+ ) -> io::Result<FileEntry<T>> {
Ok(FileEntry {
- inner: self.inner.open_file_at_range(range).await?,
+ inner: self.inner.open_file_at_range(entry_range_info).await?,
})
}
}
/// Following a hardlink.
- pub async fn follow_hardlink(&self, link: &Hardlink) -> io::Result<FileEntry<T>> {
+ pub async fn follow_hardlink(&self, entry: &FileEntry<T>) -> io::Result<FileEntry<T>> {
Ok(FileEntry {
- inner: self.inner.follow_hardlink(link).await?,
+ inner: self.inner.follow_hardlink(&entry.inner).await?,
})
}
}
/// Exposed for raw by-offset access methods (use with `open_dir_at_end`).
#[inline]
- pub fn entry_range(&self) -> Range<u64> {
- self.inner.entry_range()
+ pub fn entry_range_info(&self) -> &accessor::EntryRangeInfo {
+ self.inner.entry_range_info()
}
}
/// Exposed for raw by-offset access methods.
#[inline]
- pub fn entry_range(&self) -> Range<u64> {
- self.inner.entry_range()
+ pub fn entry_range_info(&self) -> &accessor::EntryRangeInfo {
+ self.inner.entry_range_info()
}
}
use cache::Cache;
+/// Range information used for unsafe raw random access:
+#[derive(Clone, Debug)]
+pub struct EntryRangeInfo {
+ pub filename_header_offset: Option<u64>,
+ pub entry_range: Range<u64>,
+}
+
+impl EntryRangeInfo {
+ pub fn toplevel(entry_range: Range<u64>) -> Self {
+ Self {
+ filename_header_offset: None,
+ entry_range,
+ }
+ }
+}
+
/// Random access read implementation.
pub trait ReadAt {
fn poll_read_at(
Ok(DecoderImpl::new_full(SeqReadAtAdapter::new(input, entry_range), path).await?)
}
+// NOTE: This performs the Decoder::read_next_item() behavior! Keep in mind when changing!
+async fn get_decoder_at_filename<T: ReadAt>(
+ input: T,
+ entry_range: Range<u64>,
+ path: PathBuf,
+) -> io::Result<(DecoderImpl<SeqReadAtAdapter<T>>, u64)> {
+ let mut decoder = get_decoder(input, entry_range, path).await?;
+ decoder.path_lengths.push(0);
+ decoder.read_next_header().await?;
+ if decoder.current_header.htype != format::PXAR_FILENAME {
+ io_bail!("expected filename entry, got {:?}", decoder.current_header.htype);
+ }
+ if decoder.read_current_item().await? != decoder::ItemResult::Entry {
+ // impossible, since we checked the header type above for a "proper" error message
+ io_bail!("unexpected decoder state");
+ }
+ let entry_offset = decoder::seq_read_position(&mut decoder.input).await.transpose()?
+ .ok_or_else(|| io_format_err!("reader provided no offset"))?;
+ Ok((decoder, entry_offset))
+}
+
impl<T: Clone + ReadAt> AccessorImpl<T> {
pub async fn open_root(&self) -> io::Result<DirectoryImpl<T>> {
DirectoryImpl::open_at_end(
/// Allow opening a regular file from a specified range.
pub async unsafe fn open_file_at_range(
&self,
- range: Range<u64>,
+ entry_range_info: &EntryRangeInfo,
) -> io::Result<FileEntryImpl<T>> {
- let mut decoder = get_decoder(self.input.clone(), range.clone(), PathBuf::new()).await?;
+ let mut decoder = get_decoder(
+ self.input.clone(),
+ entry_range_info.entry_range.clone(),
+ PathBuf::new(),
+ ).await?;
let entry = decoder
.next()
.await
Ok(FileEntryImpl {
input: self.input.clone(),
entry,
- entry_range: range,
+ entry_range_info: entry_range_info.clone(),
caches: Arc::clone(&self.caches),
})
}
/// Following a hardlink breaks a couple of conventions we otherwise have, particularly we will
/// never know the actual length of the target entry until we're done decoding it, so this
/// needs to happen at the accessor level, rather than a "sub-entry-reader".
- pub async fn follow_hardlink(&self, link: &format::Hardlink) -> io::Result<FileEntryImpl<T>> {
- let mut decoder = get_decoder(
+ pub async fn follow_hardlink(&self, entry: &FileEntryImpl<T>) -> io::Result<FileEntryImpl<T>> {
+ let link_offset = match entry.entry.kind() {
+ EntryKind::Hardlink(link) => link.offset,
+ _ => io_bail!("cannot resolve a non-hardlink"),
+ };
+
+ let entry_file_offset = entry
+ .entry_range_info
+ .filename_header_offset
+ .ok_or_else(|| io_format_err!("cannot follow hardlink without a file entry header"))?;
+
+ if link_offset > entry_file_offset {
+ io_bail!("invalid offset in hardlink");
+ }
+
+ let link_offset = entry_file_offset - link_offset;
+
+ let (mut decoder, entry_offset) = get_decoder_at_filename(
self.input.clone(),
- link.offset..self.size,
- PathBuf::from(link.as_os_str()),
+ link_offset..self.size,
+ PathBuf::new(),
)
.await?;
+
let entry = decoder
.next()
.await
offset: Some(offset),
size,
} => {
- let meta_size = offset - link.offset;
- let entry_end = link.offset + meta_size + size;
+ let meta_size = offset - link_offset;
+ let entry_end = link_offset + meta_size + size;
Ok(FileEntryImpl {
input: self.input.clone(),
entry,
- entry_range: link.offset..entry_end,
+ entry_range_info: EntryRangeInfo {
+ filename_header_offset: Some(link_offset),
+ entry_range: entry_offset..entry_end,
+ },
caches: Arc::clone(&self.caches),
})
}
Ok(FileEntryImpl {
input: self.input.clone(),
entry,
- entry_range: self.entry_range(),
+ entry_range_info: EntryRangeInfo {
+ filename_header_offset: None,
+ entry_range: self.entry_range(),
+ },
caches: Arc::clone(&self.caches),
})
}
Ok(DirEntryImpl {
dir: self,
file_name,
- entry_range,
+ entry_range_info: EntryRangeInfo {
+ filename_header_offset: Some(file_ofs),
+ entry_range,
+ },
caches: Arc::clone(&self.caches),
})
}
pub(crate) struct FileEntryImpl<T: Clone + ReadAt> {
input: T,
entry: Entry,
- entry_range: Range<u64>,
+ entry_range_info: EntryRangeInfo,
caches: Arc<Caches>,
}
DirectoryImpl::open_at_end(
self.input.clone(),
- self.entry_range.end,
+ self.entry_range_info.entry_range.end,
self.entry.path.clone(),
Arc::clone(&self.caches),
)
/// Exposed for raw by-offset access methods (use with `open_dir_at_end`).
#[inline]
- pub fn entry_range(&self) -> Range<u64> {
- self.entry_range.clone()
+ pub fn entry_range_info(&self) -> &EntryRangeInfo {
+ &self.entry_range_info
}
}
pub(crate) struct DirEntryImpl<'a, T: Clone + ReadAt> {
dir: &'a DirectoryImpl<T>,
file_name: PathBuf,
- entry_range: Range<u64>,
+ entry_range_info: EntryRangeInfo,
caches: Arc<Caches>,
}
async fn decode_entry(&self) -> io::Result<FileEntryImpl<T>> {
let (entry, _decoder) = self
.dir
- .decode_one_entry(self.entry_range.clone(), Some(&self.file_name))
+ .decode_one_entry(self.entry_range_info.entry_range.clone(), Some(&self.file_name))
.await?;
Ok(FileEntryImpl {
input: self.dir.input.clone(),
entry,
- entry_range: self.entry_range(),
+ entry_range_info: self.entry_range_info.clone(),
caches: Arc::clone(&self.caches),
})
}
/// Exposed for raw by-offset access methods.
#[inline]
- pub fn entry_range(&self) -> Range<u64> {
- self.entry_range.clone()
+ pub fn entry_range_info(&self) -> &EntryRangeInfo {
+ &self.entry_range_info
}
}
use crate::accessor::{self, cache::Cache, ReadAt};
use crate::decoder::Decoder;
-use crate::format::{GoodbyeItem, Hardlink};
+use crate::format::GoodbyeItem;
use crate::util::poll_result_once;
use crate::Entry;
}
/// Allow opening a regular file from a specified range.
- pub unsafe fn open_file_at_range(&self, range: Range<u64>) -> io::Result<FileEntry<T>> {
+ pub unsafe fn open_file_at_range(
+ &self,
+ entry_range_info: &accessor::EntryRangeInfo,
+ ) -> io::Result<FileEntry<T>> {
Ok(FileEntry {
- inner: poll_result_once(self.inner.open_file_at_range(range))?,
+ inner: poll_result_once(self.inner.open_file_at_range(entry_range_info))?,
})
}
}
/// Following a hardlink.
- pub fn follow_hardlink(&self, link: &Hardlink) -> io::Result<FileEntry<T>> {
+ pub fn follow_hardlink(&self, entry: &FileEntry<T>) -> io::Result<FileEntry<T>> {
Ok(FileEntry {
- inner: poll_result_once(self.inner.follow_hardlink(link))?,
+ inner: poll_result_once(self.inner.follow_hardlink(&entry.inner))?,
})
}
}
/// Exposed for raw by-offset access methods (use with `open_dir_at_end`).
#[inline]
- pub fn entry_range(&self) -> Range<u64> {
- self.inner.entry_range()
+ pub fn entry_range_info(&self) -> &accessor::EntryRangeInfo {
+ self.inner.entry_range_info()
}
}
/// Exposed for raw by-offset access methods.
#[inline]
- pub fn entry_range(&self) -> Range<u64> {
- self.inner.entry_range()
+ pub fn entry_range_info(&self) -> &accessor::EntryRangeInfo {
+ self.inner.entry_range_info()
}
}
/// We use `async fn` to implement the decoder state machine so that we can easily plug in both
/// synchronous or `async` I/O objects in as input.
pub(crate) struct DecoderImpl<T> {
- input: T,
- current_header: Header,
+ pub(crate) input: T,
+ pub(crate) current_header: Header,
entry: Entry,
- path_lengths: Vec<usize>,
+ pub(crate) path_lengths: Vec<usize>,
state: State,
with_goodbye_tables: bool,
}
/// of the entry we stop.
/// Note that if we're in a directory, we stopped at the beginning of its contents.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-enum ItemResult {
+pub(crate) enum ItemResult {
/// We parsed an "attribute" item and should continue parsing.
Attribute,
.ok_or_else(|| io_format_err!("unexpected EOF"))
}
+ // NOTE: This behavior method is also recreated in the accessor's `get_decoder_at_filename`
+ // function! Keep in mind when changing!
async fn read_next_item(&mut self) -> io::Result<ItemResult> {
self.read_next_header().await?;
self.read_current_item().await
}
- async fn read_next_header(&mut self) -> io::Result<()> {
+ pub(crate) async fn read_next_header(&mut self) -> io::Result<()> {
let dest = unsafe {
std::slice::from_raw_parts_mut(
&mut self.current_header as *mut Header as *mut u8,
}
/// Read the next item, the header is already loaded.
- async fn read_current_item(&mut self) -> io::Result<ItemResult> {
+ pub(crate) async fn read_current_item(&mut self) -> io::Result<ItemResult> {
match self.current_header.htype {
format::PXAR_XATTR => {
let xattr = self.read_xattr().await?;