]> git.proxmox.com Git - pxar.git/commitdiff
major updates to allow following hardlinks efficiently
authorWolfgang Bumiller <w.bumiller@proxmox.com>
Fri, 5 Jun 2020 14:01:07 +0000 (16:01 +0200)
committerWolfgang Bumiller <w.bumiller@proxmox.com>
Fri, 5 Jun 2020 14:22:20 +0000 (16:22 +0200)
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
src/accessor/aio.rs
src/accessor/mod.rs
src/accessor/sync.rs
src/decoder/mod.rs

index 6f4309158a31af7814a90e271fdc264ad40fe7b2..779373f654ef3f0021ff364152642ac4b6909e18 100644 (file)
@@ -14,7 +14,7 @@ use std::task::{Context, Poll};
 
 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;
 
@@ -120,9 +120,12 @@ impl<T: Clone + ReadAt> Accessor<T> {
     }
 
     /// 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?,
         })
     }
 
@@ -135,9 +138,9 @@ impl<T: Clone + ReadAt> Accessor<T> {
     }
 
     /// 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?,
         })
     }
 }
@@ -228,8 +231,8 @@ impl<T: Clone + ReadAt> FileEntry<T> {
 
     /// 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()
     }
 }
 
@@ -297,8 +300,8 @@ impl<'a, T: Clone + ReadAt> DirEntry<'a, T> {
 
     /// 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()
     }
 }
 
index 28a4ab9970bdefb7ceb67c8cdf5cbe26ef0d4feb..a8cb5fbc5c0c2a7e8c9ac7bc725253c957701951 100644 (file)
@@ -28,6 +28,22 @@ pub use sync::{Accessor, DirEntry, Directory, FileEntry, ReadDir};
 
 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(
@@ -177,6 +193,27 @@ async fn get_decoder<T: ReadAt>(
     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(
@@ -202,9 +239,13 @@ impl<T: Clone + ReadAt> AccessorImpl<T> {
     /// 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
@@ -212,7 +253,7 @@ impl<T: Clone + ReadAt> AccessorImpl<T> {
         Ok(FileEntryImpl {
             input: self.input.clone(),
             entry,
-            entry_range: range,
+            entry_range_info: entry_range_info.clone(),
             caches: Arc::clone(&self.caches),
         })
     }
@@ -225,13 +266,30 @@ impl<T: Clone + ReadAt> AccessorImpl<T> {
     /// 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
@@ -244,12 +302,15 @@ impl<T: Clone + ReadAt> AccessorImpl<T> {
                 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),
                 })
             }
@@ -427,7 +488,10 @@ impl<T: Clone + ReadAt> DirectoryImpl<T> {
         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),
         })
     }
@@ -530,7 +594,10 @@ impl<T: Clone + ReadAt> DirectoryImpl<T> {
         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),
         })
     }
@@ -576,7 +643,7 @@ impl<T: Clone + ReadAt> DirectoryImpl<T> {
 pub(crate) struct FileEntryImpl<T: Clone + ReadAt> {
     input: T,
     entry: Entry,
-    entry_range: Range<u64>,
+    entry_range_info: EntryRangeInfo,
     caches: Arc<Caches>,
 }
 
@@ -588,7 +655,7 @@ impl<T: Clone + ReadAt> FileEntryImpl<T> {
 
         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),
         )
@@ -628,8 +695,8 @@ impl<T: Clone + ReadAt> FileEntryImpl<T> {
 
     /// 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
     }
 }
 
@@ -678,7 +745,7 @@ impl<'a, T: Clone + ReadAt> ReadDirImpl<'a, T> {
 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>,
 }
 
@@ -690,21 +757,21 @@ impl<'a, T: Clone + ReadAt> DirEntryImpl<'a, T> {
     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
     }
 }
 
index 1eabe6f717f3f148ca749d55267d5dc3d1c599c4..265e50090fb5a7a14994b755d97ae299723ea21f 100644 (file)
@@ -10,7 +10,7 @@ use std::task::{Context, Poll};
 
 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;
 
@@ -115,9 +115,12 @@ impl<T: Clone + ReadAt> Accessor<T> {
     }
 
     /// 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))?,
         })
     }
 
@@ -130,9 +133,9 @@ impl<T: Clone + ReadAt> Accessor<T> {
     }
 
     /// 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))?,
         })
     }
 }
@@ -276,8 +279,8 @@ impl<T: Clone + ReadAt> FileEntry<T> {
 
     /// 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()
     }
 }
 
@@ -348,8 +351,8 @@ impl<'a, T: Clone + ReadAt> DirEntry<'a, T> {
 
     /// 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()
     }
 }
 
index 3988046ebad605c164766b8bda32517207b53df8..92211cce641849202d3b93417f941a3792982e8f 100644 (file)
@@ -151,10 +151,10 @@ async fn seq_read_entry<T: SeqRead + ?Sized, E: Endian>(input: &mut T) -> io::Re
 /// 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,
 }
@@ -180,7 +180,7 @@ enum State {
 /// 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,
 
@@ -391,12 +391,14 @@ impl<I: SeqRead> DecoderImpl<I> {
             .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,
@@ -408,7 +410,7 @@ impl<I: SeqRead> DecoderImpl<I> {
     }
 
     /// 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?;