read op can't reply with fuse_reply_iov
rust-fuse forces the read op implementation to use ReplyData type as the reply object which is equivalent to fuse_reply_data or fuse_reply_buf because it returns a single buffer.
But, returning by scatter-gather is better for performance. If I understand correctly, that's because read op allows the user to reply with fuse_reply_iov.
This isn't a bug but I think rust-fuse has a reason to rethink its design.
/**
* Read data
*
* Read should send exactly the number of bytes requested except
* on EOF or error, otherwise the rest of the data will be
* substituted with zeroes. An exception to this is when the file
* has been opened in 'direct_io' mode, in which case the return
* value of the read system call will reflect the return value of
* this operation.
*
* fi->fh will contain the value set by the open method, or will
* be undefined if the open method didn't set any value.
*
* Valid replies:
* fuse_reply_buf
* fuse_reply_iov
* fuse_reply_data
* fuse_reply_err
*
* @param req request handle
* @param ino the inode number
* @param size number of bytes to read
* @param off offset to read from
* @param fi file information
*/
void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info *fi);
You're right, scatter-gather-I/O is more performant if data isn't stored in a single linear buffer. Internally, all reply types already use &[&[u8]] (which is similar to an iovec) to pass around data for replying. If we could enhance ReplyData's interface to work with multiple slices, it would allow a more performant reply of data blocks.
The easiest way would be to simply add a method like
/// Reply to a request with data from the given slices
pub fn data_slices (mut self, data: &[&[u8]]) {
self.reply.send(0, data);
}
Another, more Rust-like approach would be to pass an iterator or a reader that ReplyData uses to gather data and use it for the reply. But maybe this would be more appropriate for a higher level abstraction (where pathnames are used instead of inode numbers like mentioned in README.md)
Sorry to start from my misunderstanding of the protocol. fuse 2.9.3 (protocol 7.19) has reply four methods for read but fuse 2.7.2 (protocol 7.8) has only two. So rust-fuse's implementation is conformant to protocol 7.8.
/**
* Read data
*
* Read should send exactly the number of bytes requested except
* on EOF or error, otherwise the rest of the data will be
* substituted with zeroes. An exception to this is when the file
* has been opened in 'direct_io' mode, in which case the return
* value of the read system call will reflect the return value of
* this operation.
*
* fi->fh will contain the value set by the open method, or will
* be undefined if the open method didn't set any value.
*
* Valid replies:
* fuse_reply_buf
* fuse_reply_err
*
* @param req request handle
* @param ino the inode number
* @param size number of bytes to read
* @param off offset to read from
* @param fi file information
*/
void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info *fi);
So, extending the design so that read op can reply with iov is kind of back-porting.
By the way, do you know what version fuse started to support iov reply for read op? And, why fuse_reply_iov isn't used at all in fuse 2.7.2 (this is really weird)?
The reply methods actually don't support different protocol versions, they just provide different ways to specify the data that should be sent with a reply (i.e. reply methods are part of the library, not the protocol). Afaik fuse used fuse_reply_iov internally for a long time already. In theory you should be able to use fuse_reply_iov with protocol 7.8 with no problems (if the older libfuse exports this function).
So no matter what protocol version, it would be nice to have a reply method in rust-fuse that sends data by scatter-gather, like the data_slices method I suggested above (we're bound to talk a specific protocol version, but we define our own (Rust-like) interface and do not depend on wether fuse_reply_iov exists or not)