+2006-03-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add O_ASYNC and O_NONBLOCK support to FUSE device. Patch by
+ Jeff Dike
+
+ * Renamed fuse_chan_receive() to fuse_chan_recv() and changed
+ interface to return -errno in case of error.
+
2006-03-01 Csaba Henk <csaba.henk@creo.hu>
* libfuse: pass device file descriptor to fuse_unmount(), rewrite
/**
* Receive a raw request
*
+ * A return value of -ENODEV means, that the filesystem was unmounted
+ *
* @param ch the channel
* @param buf the buffer to store the request in
* @param size the size of the buffer
- * @return the actual size of the raw request, or -1 on error
+ * @return the actual size of the raw request, or -errno on error
*/
-int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size);
+int fuse_chan_recv(struct fuse_chan *ch, char *buf, size_t size);
/**
* Send a raw reply
const struct fuse_lowlevel_ops *op,
size_t op_size, void *userdata);
+int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size);
+
#endif /* __FreeBSD__ */
list_add_tail(&req->list, &fc->pending);
req->state = FUSE_REQ_PENDING;
wake_up(&fc->waitq);
+ kill_fasync(&fc->fasync, SIGIO, POLL_IN);
}
/*
err = -EPERM;
if (!fc)
goto err_unlock;
+
+ err = -EAGAIN;
+ if((file->f_flags & O_NONBLOCK) && fc->connected &&
+ list_empty(&fc->pending))
+ goto err_unlock;
+
request_wait(fc);
err = -ENODEV;
if (!fc->connected)
end_requests(fc, &fc->pending);
end_requests(fc, &fc->processing);
wake_up_all(&fc->waitq);
+ kill_fasync(&fc->fasync, SIGIO, POLL_IN);
}
spin_unlock(&fuse_lock);
}
end_requests(fc, &fc->processing);
}
spin_unlock(&fuse_lock);
- if (fc)
+ if (fc) {
kobject_put(&fc->kobj);
+ fasync_helper(-1, file, 0, &fc->fasync);
+ fc->fasync = NULL;
+ }
return 0;
}
+static int fuse_dev_fasync(int fd, struct file *file, int on)
+{
+ struct fuse_conn *fc = fuse_get_conn(file);
+
+ if (!fc)
+ return -ENODEV;
+
+ /* No locking - fasync_helper does its own locking */
+ return fasync_helper(fd, file, on, &fc->fasync);
+}
+
struct file_operations fuse_dev_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.writev = fuse_dev_writev,
.poll = fuse_dev_poll,
.release = fuse_dev_release,
+ .fasync = fuse_dev_fasync,
};
static struct miscdevice fuse_miscdevice = {
/** kobject */
struct kobject kobj;
+
+ /** O_ASYNC requests */
+ struct fasync_struct *fasync;
};
static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb)
#else
#include "compat/parser.h"
#endif
+#include <linux/poll.h>
MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
MODULE_DESCRIPTION("Filesystem in Userspace");
spin_unlock(&fuse_lock);
up_write(&fc->sbput_sem);
/* Flush all readers on this fs */
+ kill_fasync(&fc->fasync, SIGIO, POLL_IN);
wake_up_all(&fc->waitq);
#ifdef KERNEL_2_6
kobject_del(&fc->kobj);
fc->bdi.unplug_io_fn = default_unplug_io_fn;
#endif
fc->reqctr = 0;
+ fc->fasync = NULL;
}
return fc;
}
size_t bufsize = fuse_chan_bufsize(ch);
struct fuse_cmd *cmd = fuse_alloc_cmd(bufsize);
if (cmd != NULL) {
- int res = fuse_chan_receive(ch, cmd->buf, bufsize);
+ int res = fuse_chan_recv(ch, cmd->buf, bufsize);
if (res <= 0) {
free_cmd(cmd);
- if (res == -1)
+ if (res < 0 && res != -EINTR && res != -EAGAIN)
fuse_exit(f);
return NULL;
}
static int fuse_kern_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
{
- ssize_t res = read(fuse_chan_fd(ch), buf, size);
- int err = errno;
+ int err;
+ ssize_t res;
struct fuse_session *se = fuse_chan_session(ch);
-
assert(se != NULL);
+
+ restart:
+ res = read(fuse_chan_fd(ch), buf, size);
+ err = errno;
+
if (fuse_session_exited(se))
return 0;
if (res == -1) {
- /* EINTR means, the read() was interrupted, ENOENT means the
- operation was interrupted */
- if (err == EINTR || err == ENOENT)
- return 0;
- /* ENODEV means we got unmounted, so we silently return failure */
- if (err != ENODEV)
+ /* ENOENT means the operation was interrupted, it's safe
+ to restart */
+ if (err == ENOENT)
+ goto restart;
+
+ /* Errors occuring during normal operation: EINTR (read
+ interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
+ umounted) */
+ if (err != EINTR && err != EAGAIN && err != ENODEV)
perror("fuse: reading device");
- return -1;
+ return -err;
}
if ((size_t) res < sizeof(struct fuse_in_header)) {
fprintf(stderr, "short read on fuse device\n");
- return -1;
+ return -EIO;
}
return res;
}
#include <stdio.h>
#include <stdlib.h>
+#include <errno.h>
int fuse_session_loop(struct fuse_session *se)
{
}
while (!fuse_session_exited(se)) {
- res = fuse_chan_receive(ch, buf, bufsize);
- if (!res)
+ res = fuse_chan_recv(ch, buf, bufsize);
+ if (res == -EINTR)
continue;
- if (res == -1)
+ if (res <= 0)
break;
fuse_session_process(se, buf, res, ch);
- res = 0;
}
free(buf);
fuse_session_reset(se);
- return res;
+ return res < 0 ? -1 : 0;
}
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
while (!fuse_session_exited(w->se)) {
- int res = fuse_chan_receive(w->prevch, buf, bufsize);
- if (!res)
+ int res = fuse_chan_recv(w->prevch, buf, bufsize);
+ if (res == -EINTR)
continue;
- if (res == -1) {
- fuse_session_exit(w->se);
- w->error = -1;
+ if (res <= 0) {
+ if (res < 0) {
+ fuse_session_exit(w->se);
+ w->error = -1;
+ }
break;
}
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <errno.h>
struct fuse_session {
struct fuse_session_ops op;
return ch->se;
}
-int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
+int fuse_chan_recv(struct fuse_chan *ch, char *buf, size_t size)
{
return ch->op.receive(ch, buf, size);
}
+int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
+{
+ int res = fuse_chan_recv(ch, buf, size);
+ return res >= 0 ? res : (res != -EINTR && res != -EAGAIN) ? -1 : 0;
+}
+
int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count)
{
return ch->op.send(ch, iov, count);
fuse_setup_compat25;
fuse_unmount;
fuse_unmount_compat22;
+ fuse_chan_recv;
local:
*;