1987da915Sopenharmony_ci/*
2987da915Sopenharmony_ci    FUSE: Filesystem in Userspace
3987da915Sopenharmony_ci    Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
4987da915Sopenharmony_ci
5987da915Sopenharmony_ci    This program can be distributed under the terms of the GNU LGPLv2.
6987da915Sopenharmony_ci    See the file COPYING.LIB
7987da915Sopenharmony_ci*/
8987da915Sopenharmony_ci
9987da915Sopenharmony_ci#include "config.h"
10987da915Sopenharmony_ci#include "fuse_lowlevel.h"
11987da915Sopenharmony_ci#include "fuse_kernel.h"
12987da915Sopenharmony_ci#include "fuse_i.h"
13987da915Sopenharmony_ci
14987da915Sopenharmony_ci#include <stdio.h>
15987da915Sopenharmony_ci#include <errno.h>
16987da915Sopenharmony_ci#include <unistd.h>
17987da915Sopenharmony_ci#include <assert.h>
18987da915Sopenharmony_ci
19987da915Sopenharmony_cistatic int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf,
20987da915Sopenharmony_ci                                  size_t size)
21987da915Sopenharmony_ci{
22987da915Sopenharmony_ci    struct fuse_chan *ch = *chp;
23987da915Sopenharmony_ci    int err;
24987da915Sopenharmony_ci    ssize_t res;
25987da915Sopenharmony_ci    struct fuse_session *se = fuse_chan_session(ch);
26987da915Sopenharmony_ci    assert(se != NULL);
27987da915Sopenharmony_ci
28987da915Sopenharmony_ci restart:
29987da915Sopenharmony_ci    res = read(fuse_chan_fd(ch), buf, size);
30987da915Sopenharmony_ci    err = errno;
31987da915Sopenharmony_ci
32987da915Sopenharmony_ci    if (fuse_session_exited(se))
33987da915Sopenharmony_ci        return 0;
34987da915Sopenharmony_ci    if (res == -1) {
35987da915Sopenharmony_ci        /* ENOENT means the operation was interrupted, it's safe
36987da915Sopenharmony_ci           to restart */
37987da915Sopenharmony_ci        if (err == ENOENT)
38987da915Sopenharmony_ci            goto restart;
39987da915Sopenharmony_ci
40987da915Sopenharmony_ci        if (err == ENODEV) {
41987da915Sopenharmony_ci            fuse_session_exit(se);
42987da915Sopenharmony_ci            return 0;
43987da915Sopenharmony_ci        }
44987da915Sopenharmony_ci        /* Errors occuring during normal operation: EINTR (read
45987da915Sopenharmony_ci           interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
46987da915Sopenharmony_ci           umounted) */
47987da915Sopenharmony_ci        if (err != EINTR && err != EAGAIN)
48987da915Sopenharmony_ci            perror("fuse: reading device");
49987da915Sopenharmony_ci        return -err;
50987da915Sopenharmony_ci    }
51987da915Sopenharmony_ci    if ((size_t) res < sizeof(struct fuse_in_header)) {
52987da915Sopenharmony_ci        fprintf(stderr, "short read on fuse device\n");
53987da915Sopenharmony_ci        return -EIO;
54987da915Sopenharmony_ci    }
55987da915Sopenharmony_ci    return res;
56987da915Sopenharmony_ci}
57987da915Sopenharmony_ci
58987da915Sopenharmony_cistatic int fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec iov[],
59987da915Sopenharmony_ci                               size_t count)
60987da915Sopenharmony_ci{
61987da915Sopenharmony_ci    if (iov) {
62987da915Sopenharmony_ci        ssize_t res = writev(fuse_chan_fd(ch), iov, count);
63987da915Sopenharmony_ci        int err = errno;
64987da915Sopenharmony_ci
65987da915Sopenharmony_ci        if (res == -1) {
66987da915Sopenharmony_ci            struct fuse_session *se = fuse_chan_session(ch);
67987da915Sopenharmony_ci
68987da915Sopenharmony_ci            assert(se != NULL);
69987da915Sopenharmony_ci
70987da915Sopenharmony_ci            /* ENOENT means the operation was interrupted */
71987da915Sopenharmony_ci            if (!fuse_session_exited(se) && err != ENOENT)
72987da915Sopenharmony_ci                perror("fuse: writing device");
73987da915Sopenharmony_ci            return -err;
74987da915Sopenharmony_ci        }
75987da915Sopenharmony_ci    }
76987da915Sopenharmony_ci    return 0;
77987da915Sopenharmony_ci}
78987da915Sopenharmony_ci
79987da915Sopenharmony_cistatic void fuse_kern_chan_destroy(struct fuse_chan *ch)
80987da915Sopenharmony_ci{
81987da915Sopenharmony_ci    close(fuse_chan_fd(ch));
82987da915Sopenharmony_ci}
83987da915Sopenharmony_ci
84987da915Sopenharmony_ci#define MIN_BUFSIZE 0x21000
85987da915Sopenharmony_ci
86987da915Sopenharmony_cistruct fuse_chan *fuse_kern_chan_new(int fd)
87987da915Sopenharmony_ci{
88987da915Sopenharmony_ci    struct fuse_chan_ops op = {
89987da915Sopenharmony_ci        .receive = fuse_kern_chan_receive,
90987da915Sopenharmony_ci        .send = fuse_kern_chan_send,
91987da915Sopenharmony_ci        .destroy = fuse_kern_chan_destroy,
92987da915Sopenharmony_ci    };
93987da915Sopenharmony_ci    size_t bufsize = getpagesize() + 0x1000;
94987da915Sopenharmony_ci    bufsize = bufsize < MIN_BUFSIZE ? MIN_BUFSIZE : bufsize;
95987da915Sopenharmony_ci    return fuse_chan_new(&op, fd, bufsize, NULL);
96987da915Sopenharmony_ci}
97