diff --git a/src/pxar/decoder.rs b/src/pxar/decoder.rs index 351e58ca..46871b5e 100644 --- a/src/pxar/decoder.rs +++ b/src/pxar/decoder.rs @@ -327,65 +327,6 @@ impl Decoder { } } - /// Get attributes for the archive item located at `offset`. - /// - /// Returns the entry, attributes and the payload size for the item. - /// For regular archive itmes a `PXAR_FILENAME` or a `PXAR_ENTRY` header is - /// expected at `offset`. - /// For directories, `offset` might also (but not necessarily) point at the - /// directories `PXAR_GOODBYE_TAIL_MARKER`. This is not mandatory and it can - /// also directly point to its `PXAR_FILENAME` or `PXAR_ENTRY`, thereby - /// avoiding an additional seek. - pub fn attributes(&mut self, offset: u64) -> Result<(OsString, PxarEntry, PxarAttributes, u64), Error> { - self.seek(SeekFrom::Start(offset))?; - - let mut marker: u64 = self.inner.read_item()?; - if marker == PXAR_GOODBYE_TAIL_MARKER { - let dir_offset: u64 = self.inner.read_item()?; - let gb_size: u64 = self.inner.read_item()?; - let distance = i64::try_from(dir_offset + gb_size)?; - self.seek(SeekFrom::Current(0 - distance))?; - marker = self.inner.read_item()?; - } - - let filename = if marker == PXAR_FILENAME { - let size: u64 = self.inner.read_item()?; - let filename = self.inner.read_filename(size)?; - marker = self.inner.read_item()?; - filename - } else { - OsString::new() - }; - - if marker == PXAR_FORMAT_HARDLINK { - let size: u64 = self.inner.read_item()?; - let (_, diff) = self.inner.read_hardlink(size)?; - // Make sure to return the original filename, - // not the one read from the hardlink. - let (_, entry, xattr, file_size) = self.attributes(offset - diff)?; - return Ok((filename, entry, xattr, file_size)); - } - - if marker != PXAR_ENTRY { - bail!("Expected PXAR_ENTRY, found 0x{:x?}", marker); - } - let _size: u64 = self.inner.read_item()?; - let entry: PxarEntry = self.inner.read_item()?; - let (header, xattr) = self.inner.read_attributes()?; - let file_size = match header.htype { - PXAR_PAYLOAD => header.size - HEADER_SIZE, - _ => 0, - }; - - Ok((filename, entry, xattr, file_size)) - } - - /// Opens the file by validating the given `offset` and returning its attrs, - /// xattrs and size. - pub fn open(&mut self, offset: u64) -> Result<(OsString, PxarEntry, PxarAttributes, u64), Error> { - self.attributes(offset) - } - /// Read the payload of the file given by `offset`. /// /// This will read the file by first seeking to `offset` within the archive, diff --git a/src/pxar/fuse.rs b/src/pxar/fuse.rs index 5fdf7b27..31e67eb3 100644 --- a/src/pxar/fuse.rs +++ b/src/pxar/fuse.rs @@ -17,7 +17,7 @@ use libc::{c_char, c_int, c_void, size_t}; use super::binary_search_tree::search_binary_tree_by; use super::decoder::{Decoder, DirectoryEntry}; -use super::format_definition::{PxarEntry, PxarGoodbyeItem}; +use super::format_definition::PxarEntry; /// Node ID of the root i-node /// @@ -30,8 +30,6 @@ use super::format_definition::{PxarEntry, PxarGoodbyeItem}; /// required. const FUSE_ROOT_ID: u64 = 1; -const GOODBYE_ITEM_SIZE: u64 = std::mem::size_of::() as u64; - type Offset = u64; /// FFI types for easier readability type Request = *mut c_void; @@ -84,6 +82,8 @@ struct Context { /// DirectoryEntry via the Decoder as well as the parent, in order /// to be able to include the parent directory on readdirplus calls. start_end_parent: HashMap, + /// Hold the DirectoryEntry for the current inode + entry: DirectoryEntry, } impl Context { @@ -225,9 +225,10 @@ impl Session { /// default signal handlers. /// Options have to be provided as comma separated OsStr, e.g. /// ("ro,default_permissions"). - pub fn new(decoder: Decoder, options: &OsStr, verbose: bool) -> Result { + pub fn new(mut decoder: Decoder, options: &OsStr, verbose: bool) -> Result { let args = Self::setup_args(options, verbose)?; let oprs = Self::setup_callbacks(); + let entry = decoder.root()?; let mut map = HashMap::new(); // Insert entry for the root directory, with itself as parent. map.insert(0, (decoder.root_end_offset(), 0)); @@ -236,6 +237,7 @@ impl Session { decoder, ino_offset: 0, start_end_parent: map, + entry, }; let session_ctx = Box::new(Mutex::new(ctx)); @@ -365,10 +367,18 @@ impl Session { let result = boxed_ctx .lock() .map(|mut ctx| { - ctx.ino_offset = match inode { - FUSE_ROOT_ID => 0, - _ => inode, + let (ino_offset, entry) = match inode { + FUSE_ROOT_ID => (0, ctx.decoder.root().map_err(|_| libc::EIO)?), + _ => { + let (end, _) = *ctx.start_end_parent.get(&inode).unwrap(); + let entry = ctx.decoder + .read_directory_entry(inode, end) + .map_err(|_| libc::EIO)?; + (inode, entry) + } }; + ctx.entry = entry; + ctx.ino_offset = ino_offset; code(&mut ctx) }) .unwrap_or(Err(libc::EIO)); @@ -422,11 +432,7 @@ impl Session { extern "C" fn getattr(req: Request, inode: u64, _fileinfo: MutPtr) { Self::run_in_context(req, inode, |ctx| { - let (_, entry, _, payload_size) = ctx - .decoder - .attributes(ctx.ino_offset) - .map_err(|_| libc::EIO)?; - let attr = stat(inode, &entry, payload_size)?; + let attr = stat(inode, &ctx.entry.entry, ctx.entry.size)?; let _res = unsafe { // Since fs is read-only, the timeout can be max. let timeout = std::f64::MAX; @@ -451,8 +457,7 @@ impl Session { } extern "C" fn open(req: Request, inode: u64, fileinfo: MutPtr) { - Self::run_in_context(req, inode, |ctx| { - ctx.decoder.open(ctx.ino_offset).map_err(|_| libc::ENOENT)?; + Self::run_in_context(req, inode, |_ctx| { let _ret = unsafe { fuse_reply_open(req, fileinfo) }; Ok(()) @@ -532,21 +537,11 @@ impl Session { // Add current directory entry "." if offset <= n_entries { - let entry = if ctx.ino_offset == 0 { - ctx.decoder - .root() - .map_err(|_| libc::EIO)? - } else { - ctx.decoder - .read_directory_entry(ctx.ino_offset, end) - .map_err(|_| libc::EIO)? - }; - // No need to calculate i-node for current dir, since it is given as parameter let name = CString::new(".").unwrap(); let attr = EntryParam { inode: inode, generation: 1, - attr: stat(inode, &entry.entry, entry.size).map_err(|_| libc::EIO)?, + attr: stat(inode, &ctx.entry.entry, ctx.entry.size).map_err(|_| libc::EIO)?, attr_timeout: std::f64::MAX, entry_timeout: std::f64::MAX, }; @@ -605,19 +600,15 @@ impl Session { let name = unsafe { CStr::from_ptr(name) }; Self::run_in_context(req, inode, |ctx| { - let (_, _, xattrs, _) = ctx - .decoder - .attributes(ctx.ino_offset) - .map_err(|_| libc::EIO)?; // security.capability is stored separately, check it first if name.to_bytes() == b"security.capability" { - match xattrs.fcaps { + match &mut ctx.entry.xattr.fcaps { None => return Err(libc::ENODATA), - Some(mut fcaps) => return Self::xattr_reply_value(req, &mut fcaps.data, size), + Some(fcaps) => return Self::xattr_reply_value(req, &mut fcaps.data, size), } } - for mut xattr in xattrs.xattrs { + for xattr in &mut ctx.entry.xattr.xattrs { if name.to_bytes() == xattr.name.as_slice() { return Self::xattr_reply_value(req, &mut xattr.value, size); } @@ -630,15 +621,11 @@ impl Session { /// Get a list of the extended attribute of `inode`. extern "C" fn listxattr(req: Request, inode: u64, size: size_t) { Self::run_in_context(req, inode, |ctx| { - let (_, _, xattrs, _) = ctx - .decoder - .attributes(ctx.ino_offset) - .map_err(|_| libc::EIO)?; let mut buffer = Vec::new(); - if xattrs.fcaps.is_some() { + if ctx.entry.xattr.fcaps.is_some() { buffer.extend_from_slice(b"security.capability\0"); } - for mut xattr in xattrs.xattrs { + for xattr in &mut ctx.entry.xattr.xattrs { buffer.append(&mut xattr.name); buffer.push(b'\0'); } @@ -679,33 +666,6 @@ impl Drop for Session { } } -/// Return the correct offset for the item based on its `PxarEntry` mode -/// -/// For directories, the offset for the corresponding `GOODBYE_TAIL_MARKER` -/// is returned. -/// If it is not a directory, the start offset is returned. -fn find_offset(entry: &PxarEntry, start: u64, end: u64) -> u64 { - if (entry.mode as u32 & libc::S_IFMT) == libc::S_IFDIR { - end - GOODBYE_ITEM_SIZE - } else { - start - } -} - -/// Calculate the i-node based on the given `offset` -/// -/// This maps the `offset` to the correct i-node, which is simply the offset. -/// The root directory is an exception, as it has per definition `FUSE_ROOT_ID`. -/// `root_end` is the end offset of the root directory (archive end). -fn calculate_inode(offset: u64, root_end: u64) -> u64 { - // check for root offset which has to be mapped to `FUSE_ROOT_ID` - if offset == root_end - GOODBYE_ITEM_SIZE { - FUSE_ROOT_ID - } else { - offset - } -} - /// FUSE entry for fuse_reply_entry in lookup callback #[repr(C)] struct EntryParam {