pxar::fuse: Refactor lookup in order to cache accessed entries.

Context::find_goodbye_entry() is removed and incorporated into the lookup
callback in order to take advantage of the entry_cache and since it is only used
inside this callback.
All entries read on lookup are cached.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Christian Ebner 2020-02-03 13:34:49 +01:00 committed by Wolfgang Bumiller
parent 63698e720c
commit 3e69abef02

View File

@ -135,59 +135,6 @@ impl Context {
) { ) {
( &mut self.decoder, &mut self.start_end_parent, &mut self.gbt_cache, &mut self.entry_cache ) ( &mut self.decoder, &mut self.start_end_parent, &mut self.gbt_cache, &mut self.entry_cache )
} }
/// Lookup the goodbye item identified by `filename` and its corresponding `hash`
///
/// Updates the goodbye table cache to contain the table for the directory given
/// by the i-node in the provided `Context`.
/// Search the first matching `hash` in the goodbye table, allowing for a fast
/// comparison with the items.
/// As there could be a hash collision, the found items filename is then compared
/// by seek to the corresponding item in the archive and reading its attributes
/// (which the lookup callback needs to do anyway).
/// If the filename does not match, the function is called recursively with the
/// rest of the goodbye table to lookup the next match.
/// The matching items archive offset, entry and payload size are returned.
/// If there is no entry with matching `filename` and `hash` a `libc::ENOENT` is
/// returned.
fn find_goodbye_entry(
&mut self,
filename: &CStr,
hash: u64,
) -> Result<(u64, DirectoryEntry), i32> {
let ino_offset = self.ino_offset;
let (decoder, map, gbt_cache, _) = self.as_mut_refs();
let gbt = gbt_cache.access(ino_offset, &mut GbtCacher { decoder, map })
.map_err(|_| libc::EIO)?
.ok_or_else(|| libc::ENOENT)?;
let mut start_idx = 0;
let mut skip_multiple = 0;
loop {
// Search for the next goodbye entry with matching hash.
let idx = search_binary_tree_by(
start_idx,
gbt.len(),
skip_multiple,
|idx| hash.cmp(&gbt[idx].0.hash),
).ok_or(libc::ENOENT)?;
let (_item, start, end) = &gbt[idx];
map.insert(*start, (*end, ino_offset));
let entry = decoder.read_directory_entry(*start, *end)
.map_err(|_| libc::EIO)?;
// Possible hash collision, need to check if the found entry is indeed
// the filename to lookup.
if entry.filename.as_bytes() == filename.to_bytes() {
return Ok((*start, entry));
}
// Hash collision, check the next entry in the goodbye table by starting
// from given index but skipping one more match (so hash at index itself).
start_idx = idx;
skip_multiple = 1;
}
}
} }
/// `Session` stores a pointer to the session context and is used to mount the /// `Session` stores a pointer to the session context and is used to mount the
@ -490,20 +437,47 @@ impl Session {
let filename = unsafe { CStr::from_ptr(name) }; let filename = unsafe { CStr::from_ptr(name) };
let hash = super::format_definition::compute_goodbye_hash(filename.to_bytes()); let hash = super::format_definition::compute_goodbye_hash(filename.to_bytes());
Self::run_in_context(req, parent, |ctx| { Self::run_with_context_refs(req, parent, |decoder, map, gbt_cache, entry_cache, ino_offset| {
let (offset, entry) = ctx.find_goodbye_entry(&filename, hash)?; let gbt = gbt_cache.access(ino_offset, &mut GbtCacher { decoder, map })
.map_err(|_| libc::EIO)?
.ok_or_else(|| libc::EIO)?;
let mut start_idx = 0;
let mut skip_multiple = 0;
loop {
// Search for the next goodbye entry with matching hash.
let idx = search_binary_tree_by(
start_idx,
gbt.len(),
skip_multiple,
|idx| hash.cmp(&gbt[idx].0.hash),
).ok_or_else(|| libc::ENOENT)?;
let e = EntryParam { let (_item, start, end) = &gbt[idx];
inode: offset, map.insert(*start, (*end, ino_offset));
generation: 1,
attr: stat(offset, &entry)?,
attr_timeout: std::f64::MAX,
entry_timeout: std::f64::MAX,
};
let _res = unsafe { fuse_reply_entry(req, Some(&e)) }; let entry = entry_cache.access(*start, &mut EntryCacher { decoder, map })
.map_err(|_| libc::EIO)?
.ok_or_else(|| libc::ENOENT)?;
Ok(()) // Possible hash collision, need to check if the found entry is indeed
// the filename to lookup.
if entry.filename.as_bytes() == filename.to_bytes() {
let e = EntryParam {
inode: *start,
generation: 1,
attr: stat(*start, &entry)?,
attr_timeout: std::f64::MAX,
entry_timeout: std::f64::MAX,
};
let _res = unsafe { fuse_reply_entry(req, Some(&e)) };
return Ok(())
}
// Hash collision, check the next entry in the goodbye table by starting
// from given index but skipping one more match (so hash at index itself).
start_idx = idx;
skip_multiple = 1;
}
}); });
} }