aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorTofik Sonono <tofik.sonono@intel.com>2023-01-10 10:04:35 +0000
committerGitHub <noreply@github.com>2023-01-10 10:04:35 +0000
commit50c74e645928affa1af6e9a5a6ea6a3b9d3c52dc (patch)
tree58f7deff5dfca5ba8ec3a757ddcb04c535eea48e /lib
parentc0a344e3797844896d04efc4b565a2627067b67f (diff)
downloadlibfuse-50c74e645928affa1af6e9a5a6ea6a3b9d3c52dc.tar.gz
Support application-defined I/O functions for FUSE fd
The io for FUSE requests and responses can now be further customized by allowing to write custom functions for reading/writing the responses. This includes overriding the splice io. The reason for this addition is that having a custom file descriptor is not sufficient to allow custom io. Different types of file descriptor require different mechanisms of io interaction. For example, some file descriptor communication has boundaries (SOCK_DGRAM, EOF, etc...), while other types of fd:s might be unbounded (SOCK_STREAMS, ...). For unbounded communication, you have to read the header of the FUSE request first, and then read the remaining packet data. Furthermore, the one read call does not necessarily return all the data expected, requiring further calls in a loop.
Diffstat (limited to 'lib')
-rw-r--r--lib/fuse_i.h1
-rw-r--r--lib/fuse_lowlevel.c84
-rw-r--r--lib/fuse_versionscript1
3 files changed, 77 insertions, 9 deletions
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index 42f0e5f..74cfe36 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -45,6 +45,7 @@ struct fuse_session {
char *mountpoint;
volatile int exited;
int fd;
+ struct fuse_custom_io *io;
struct mount_opts *mo;
int debug;
int deny_others;
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 7d76309..8fe47db 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -186,8 +186,15 @@ static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
}
}
- ssize_t res = writev(ch ? ch->fd : se->fd,
- iov, count);
+ ssize_t res;
+ if (se->io != NULL)
+ /* se->io->writev is never NULL if se->io is not NULL as
+ specified by fuse_session_custom_io()*/
+ res = se->io->writev(ch ? ch->fd : se->fd, iov, count,
+ se->userdata);
+ else
+ res = writev(ch ? ch->fd : se->fd, iov, count);
+
int err = errno;
if (res == -1) {
@@ -817,8 +824,14 @@ static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
(se->conn.want & FUSE_CAP_SPLICE_MOVE))
splice_flags |= SPLICE_F_MOVE;
- res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd,
- NULL, out->len, splice_flags);
+ if (se->io != NULL && se->io->splice_send != NULL) {
+ res = se->io->splice_send(llp->pipe[0], NULL,
+ ch ? ch->fd : se->fd, NULL, out->len,
+ splice_flags, se->userdata);
+ } else {
+ res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, NULL,
+ out->len, splice_flags);
+ }
if (res == -1) {
res = -errno;
perror("fuse: splice from pipe");
@@ -2000,9 +2013,13 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
if (se->conn.proto_minor >= 14) {
#ifdef HAVE_SPLICE
#ifdef HAVE_VMSPLICE
- se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
+ if ((se->io == NULL) || (se->io->splice_send != NULL)) {
+ se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
+ }
#endif
- se->conn.capable |= FUSE_CAP_SPLICE_READ;
+ if ((se->io == NULL) || (se->io->splice_receive != NULL)) {
+ se->conn.capable |= FUSE_CAP_SPLICE_READ;
+ }
#endif
}
if (se->conn.proto_minor >= 18)
@@ -2755,6 +2772,8 @@ void fuse_session_destroy(struct fuse_session *se)
free(se->cuse_data);
if (se->fd != -1)
close(se->fd);
+ if (se->io != NULL)
+ free(se->io);
destroy_mount_opts(se->mo);
free(se);
}
@@ -2804,8 +2823,14 @@ int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf *buf,
goto fallback;
}
- res = splice(ch ? ch->fd : se->fd,
- NULL, llp->pipe[1], NULL, bufsize, 0);
+ if (se->io != NULL && se->io->splice_receive != NULL) {
+ res = se->io->splice_receive(ch ? ch->fd : se->fd, NULL,
+ llp->pipe[1], NULL, bufsize, 0,
+ se->userdata);
+ } else {
+ res = splice(ch ? ch->fd : se->fd, NULL, llp->pipe[1], NULL,
+ bufsize, 0);
+ }
err = errno;
if (fuse_session_exited(se))
@@ -2891,7 +2916,14 @@ fallback:
}
restart:
- res = read(ch ? ch->fd : se->fd, buf->mem, se->bufsize);
+ if (se->io != NULL) {
+ /* se->io->read is never NULL if se->io is not NULL as
+ specified by fuse_session_custom_io()*/
+ res = se->io->read(ch ? ch->fd : se->fd, buf->mem, se->bufsize,
+ se->userdata);
+ } else {
+ res = read(ch ? ch->fd : se->fd, buf->mem, se->bufsize);
+ }
err = errno;
if (fuse_session_exited(se))
@@ -3021,6 +3053,40 @@ out1:
return NULL;
}
+int fuse_session_custom_io(struct fuse_session *se, const struct fuse_custom_io *io,
+ int fd)
+{
+ if (fd < 0) {
+ fuse_log(FUSE_LOG_ERR, "Invalid file descriptor value %d passed to "
+ "fuse_session_custom_io()\n", fd);
+ return -EBADF;
+ }
+ if (io == NULL) {
+ fuse_log(FUSE_LOG_ERR, "No custom IO passed to "
+ "fuse_session_custom_io()\n");
+ return -EINVAL;
+ } else if (io->read == NULL || io->writev == NULL) {
+ /* If the user provides their own file descriptor, we can't
+ guarantee that the default behavior of the io operations made
+ in libfuse will function properly. Therefore, we enforce the
+ user to implement these io operations when using custom io. */
+ fuse_log(FUSE_LOG_ERR, "io passed to fuse_session_custom_io() must "
+ "implement both io->read() and io->writev\n");
+ return -EINVAL;
+ }
+
+ se->io = malloc(sizeof(struct fuse_custom_io));
+ if (se->io == NULL) {
+ fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for custom io. "
+ "Error: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ se->fd = fd;
+ *se->io = *io;
+ return 0;
+}
+
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
{
int fd;
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 114f592..55ab9e5 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -39,6 +39,7 @@ FUSE_3.0 {
fuse_session_new;
fuse_main_real;
fuse_mount;
+ fuse_session_custom_io;
fuse_session_mount;
fuse_new;
fuse_opt_insert_arg;