pxar::fuse: remove readdir implementation
By not implementing readdir but only readdirplus, the FUSE_CAP_READDIRPLUS flag is set while the FUSE_CAP_READDIRPLUS_AUTO flag is not set. Thereby the kernel will issue only readdirplus calls. Documentation at: https://libfuse.github.io/doxygen/fuse-3_88_80_2include_2fuse__common_8h.html#a9b90333ad08d0e1c2ed0134d9305ee87 As the expensive part for accessing and reading the attributes is seeking and decoding each directory entry, it is usefull to force readdirplus calls. By this a struct `EntryParam` is returned for each entry, therebye avoiding a subsequent lookup call. Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
This commit is contained in:
parent
3be839c61c
commit
ef2d7f2f96
109
src/pxar/fuse.rs
109
src/pxar/fuse.rs
|
@ -60,7 +60,6 @@ extern "C" {
|
||||||
fn fuse_reply_xattr(req: Request, size: size_t) -> c_int;
|
fn fuse_reply_xattr(req: Request, size: size_t) -> c_int;
|
||||||
fn fuse_reply_readlink(req: Request, link: StrPtr) -> c_int;
|
fn fuse_reply_readlink(req: Request, link: StrPtr) -> c_int;
|
||||||
fn fuse_req_userdata(req: Request) -> MutPtr;
|
fn fuse_req_userdata(req: Request) -> MutPtr;
|
||||||
fn fuse_add_direntry(req: Request, buf: MutStrPtr, bufsize: size_t, name: StrPtr, stbuf: Option<&libc::stat>, off: c_int) -> c_int;
|
|
||||||
fn fuse_add_direntry_plus(req: Request, buf: MutStrPtr, bufsize: size_t, name: StrPtr, stbuf: Option<&EntryParam>, off: c_int) -> c_int;
|
fn fuse_add_direntry_plus(req: Request, buf: MutStrPtr, bufsize: size_t, name: StrPtr, stbuf: Option<&EntryParam>, off: c_int) -> c_int;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +81,7 @@ struct Context {
|
||||||
/// offsets.
|
/// offsets.
|
||||||
///
|
///
|
||||||
/// In order to include the parent directory entry '..' in the response for
|
/// In order to include the parent directory entry '..' in the response for
|
||||||
/// readdir callback, this mapping is needed.
|
/// readdirplus callback, this mapping is needed.
|
||||||
/// Calling the lookup callback will insert the offsets into the HashMap.
|
/// Calling the lookup callback will insert the offsets into the HashMap.
|
||||||
child_parent: HashMap<u64, u64>,
|
child_parent: HashMap<u64, u64>,
|
||||||
}
|
}
|
||||||
|
@ -293,7 +292,6 @@ impl Session {
|
||||||
oprs.open = Some(Self::open);
|
oprs.open = Some(Self::open);
|
||||||
oprs.read = Some(Self::read);
|
oprs.read = Some(Self::read);
|
||||||
oprs.opendir = Some(Self::opendir);
|
oprs.opendir = Some(Self::opendir);
|
||||||
oprs.readdir = Some(Self::readdir);
|
|
||||||
oprs.releasedir = Some(Self::releasedir);
|
oprs.releasedir = Some(Self::releasedir);
|
||||||
oprs.getxattr = Some(Self::getxattr);
|
oprs.getxattr = Some(Self::getxattr);
|
||||||
oprs.listxattr = Some(Self::listxattr);
|
oprs.listxattr = Some(Self::listxattr);
|
||||||
|
@ -494,84 +492,6 @@ impl Session {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read and return the entries of the directory referenced by i-node.
|
|
||||||
///
|
|
||||||
/// Replies to the request with the entries fitting into a buffer of length
|
|
||||||
/// `size`, as requested by the caller.
|
|
||||||
/// `offset` identifies the start index of entries to return. This is used on
|
|
||||||
/// repeated calls, occurring if not all entries fitted into the buffer.
|
|
||||||
extern "C" fn readdir(req: Request, inode: u64, size: size_t, offset: c_int, _fileinfo: MutPtr) {
|
|
||||||
let offset = offset as usize;
|
|
||||||
|
|
||||||
Self::run_in_context(req, inode, |ctx| {
|
|
||||||
let gbt = ctx.decoder
|
|
||||||
.goodbye_table(None, ctx.ino_offset + GOODBYE_ITEM_SIZE)
|
|
||||||
.map_err(|_| libc::EIO)?;
|
|
||||||
let n_entries = gbt.len();
|
|
||||||
let mut buf = ReplyBuf::new(req, size, offset);
|
|
||||||
|
|
||||||
if offset < n_entries {
|
|
||||||
for e in gbt[offset..gbt.len()].iter() {
|
|
||||||
let (filename, entry, _, payload_size) =
|
|
||||||
ctx.decoder.attributes(e.1).map_err(|_| libc::EIO)?;
|
|
||||||
let name = CString::new(filename.as_bytes()).map_err(|_| libc::EIO)?;
|
|
||||||
let item_offset = find_offset(&entry, e.1, e.2);
|
|
||||||
ctx.child_parent.insert(item_offset, ctx.ino_offset);
|
|
||||||
let item_inode = calculate_inode(item_offset, ctx.decoder.root_end_offset());
|
|
||||||
let attr = stat(item_inode, &entry, payload_size).map_err(|_| libc::EIO)?;
|
|
||||||
match buf.fill(&name, &attr, |req, buf, bufsize, name, stbuf, off| {
|
|
||||||
unsafe { fuse_add_direntry(req, buf, bufsize, name, stbuf, off) }
|
|
||||||
}) {
|
|
||||||
Ok(ReplyBufState::Okay) => {}
|
|
||||||
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
|
||||||
Err(_) => return Err(libc::EIO),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add current directory entry "."
|
|
||||||
if offset <= n_entries {
|
|
||||||
let (_, entry, _, payload_size) = ctx
|
|
||||||
.decoder
|
|
||||||
.attributes(ctx.ino_offset)
|
|
||||||
.map_err(|_| libc::EIO)?;
|
|
||||||
// No need to calculate i-node for current dir, since it is given as parameter
|
|
||||||
let attr = stat(inode, &entry, payload_size).map_err(|_| libc::EIO)?;
|
|
||||||
let name = CString::new(".").unwrap();
|
|
||||||
match buf.fill(&name, &attr, |req, buf, bufsize, name, stbuf, off| {
|
|
||||||
unsafe { fuse_add_direntry(req, buf, bufsize, name, stbuf, off) }
|
|
||||||
}) {
|
|
||||||
Ok(ReplyBufState::Okay) => {}
|
|
||||||
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
|
||||||
Err(_) => return Err(libc::EIO),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add parent directory entry ".."
|
|
||||||
if offset <= n_entries + 1 {
|
|
||||||
let parent_off = if inode == FUSE_ROOT_ID {
|
|
||||||
ctx.decoder.root_end_offset() - GOODBYE_ITEM_SIZE
|
|
||||||
} else {
|
|
||||||
*ctx.child_parent.get(&ctx.ino_offset).ok_or_else(|| libc::EIO)?
|
|
||||||
};
|
|
||||||
let (_, entry, _, payload_size) =
|
|
||||||
ctx.decoder.attributes(parent_off).map_err(|_| libc::EIO)?;
|
|
||||||
let item_inode = calculate_inode(parent_off, ctx.decoder.root_end_offset());
|
|
||||||
let attr = stat(item_inode, &entry, payload_size).map_err(|_| libc::EIO)?;
|
|
||||||
let name = CString::new("..").unwrap();
|
|
||||||
match buf.fill(&name, &attr, |req, buf, bufsize, name, stbuf, off| {
|
|
||||||
unsafe { fuse_add_direntry(req, buf, bufsize, name, stbuf, off) }
|
|
||||||
}) {
|
|
||||||
Ok(ReplyBufState::Okay) => {}
|
|
||||||
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
|
||||||
Err(_) => return Err(libc::EIO),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.reply_filled()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read and return the entries of the directory referenced by i-node.
|
/// Read and return the entries of the directory referenced by i-node.
|
||||||
///
|
///
|
||||||
/// Replies to the request with the entries fitting into a buffer of length
|
/// Replies to the request with the entries fitting into a buffer of length
|
||||||
|
@ -603,9 +523,7 @@ impl Session {
|
||||||
attr_timeout: std::f64::MAX,
|
attr_timeout: std::f64::MAX,
|
||||||
entry_timeout: std::f64::MAX,
|
entry_timeout: std::f64::MAX,
|
||||||
};
|
};
|
||||||
match buf.fill(&name, &attr, |req, buf, bufsize, name, stbuf, off| {
|
match buf.fill(&name, &attr) {
|
||||||
unsafe { fuse_add_direntry_plus(req, buf, bufsize, name, stbuf, off) }
|
|
||||||
}) {
|
|
||||||
Ok(ReplyBufState::Okay) => {}
|
Ok(ReplyBufState::Okay) => {}
|
||||||
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
||||||
Err(_) => return Err(libc::EIO),
|
Err(_) => return Err(libc::EIO),
|
||||||
|
@ -628,9 +546,7 @@ impl Session {
|
||||||
attr_timeout: std::f64::MAX,
|
attr_timeout: std::f64::MAX,
|
||||||
entry_timeout: std::f64::MAX,
|
entry_timeout: std::f64::MAX,
|
||||||
};
|
};
|
||||||
match buf.fill(&name, &attr, |req, buf, bufsize, name, stbuf, off| {
|
match buf.fill(&name, &attr) {
|
||||||
unsafe { fuse_add_direntry_plus(req, buf, bufsize, name, stbuf, off) }
|
|
||||||
}) {
|
|
||||||
Ok(ReplyBufState::Okay) => {}
|
Ok(ReplyBufState::Okay) => {}
|
||||||
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
||||||
Err(_) => return Err(libc::EIO),
|
Err(_) => return Err(libc::EIO),
|
||||||
|
@ -655,9 +571,7 @@ impl Session {
|
||||||
attr_timeout: std::f64::MAX,
|
attr_timeout: std::f64::MAX,
|
||||||
entry_timeout: std::f64::MAX,
|
entry_timeout: std::f64::MAX,
|
||||||
};
|
};
|
||||||
match buf.fill(&name, &attr, |req, buf, bufsize, name, stbuf, off| {
|
match buf.fill(&name, &attr) {
|
||||||
unsafe { fuse_add_direntry_plus(req, buf, bufsize, name, stbuf, off) }
|
|
||||||
}) {
|
|
||||||
Ok(ReplyBufState::Okay) => {}
|
Ok(ReplyBufState::Okay) => {}
|
||||||
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
||||||
Err(_) => return Err(libc::EIO),
|
Err(_) => return Err(libc::EIO),
|
||||||
|
@ -825,7 +739,7 @@ enum ReplyBufState {
|
||||||
Overfull,
|
Overfull,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to correctly fill and reply the buffer for the readdir callback
|
/// Used to correctly fill and reply the buffer for the readdirplus callback
|
||||||
struct ReplyBuf {
|
struct ReplyBuf {
|
||||||
/// internal buffer holding the binary data
|
/// internal buffer holding the binary data
|
||||||
buffer: Vec<u8>,
|
buffer: Vec<u8>,
|
||||||
|
@ -833,8 +747,8 @@ struct ReplyBuf {
|
||||||
filled: usize,
|
filled: usize,
|
||||||
/// fuse request the buffer is used to reply to
|
/// fuse request the buffer is used to reply to
|
||||||
req: Request,
|
req: Request,
|
||||||
/// index of the next item, telling from were to start on the next readdir callback in
|
/// index of the next item, telling from were to start on the next readdirplus
|
||||||
/// case not everything fitted in the buffer on the first reply.
|
/// callback in case not everything fitted in the buffer on the first reply.
|
||||||
next: usize,
|
next: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -860,18 +774,15 @@ impl ReplyBuf {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fill the buffer for the fuse reply with the next dir entry by invoking the
|
/// Fill the buffer for the fuse reply with the next dir entry by invoking the
|
||||||
/// provided filler function, which is fuse_add_direntry for the readdir callback
|
/// fuse_add_direntry_plus helper function for the readdirplus callback.
|
||||||
/// or fuse_add_direntry_plus in case of the readdirplus callback.
|
|
||||||
/// The attr type T is has to be `libc::stat` or `EntryParam` accordingly.
|
/// The attr type T is has to be `libc::stat` or `EntryParam` accordingly.
|
||||||
fn fill<T, F>(&mut self, name: &CString, attr: &T, filler_fn: F) -> Result<ReplyBufState, Error>
|
fn fill(&mut self, name: &CString, attr: &EntryParam) -> Result<ReplyBufState, Error> {
|
||||||
where F: FnOnce(Request, MutStrPtr, size_t, StrPtr, Option<&T>, c_int) -> c_int
|
|
||||||
{
|
|
||||||
self.next += 1;
|
self.next += 1;
|
||||||
let size = self.buffer.len();
|
let size = self.buffer.len();
|
||||||
let bytes = unsafe {
|
let bytes = unsafe {
|
||||||
let bptr = self.buffer.as_mut_ptr() as *mut c_char;
|
let bptr = self.buffer.as_mut_ptr() as *mut c_char;
|
||||||
let nptr = name.as_ptr();
|
let nptr = name.as_ptr();
|
||||||
filler_fn(
|
fuse_add_direntry_plus(
|
||||||
self.req,
|
self.req,
|
||||||
bptr.offset(self.filled as isize),
|
bptr.offset(self.filled as isize),
|
||||||
size - self.filled,
|
size - self.filled,
|
||||||
|
|
Loading…
Reference in New Issue