mirror of https://github.com/trapexit/mergerfs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
248 lines
4.5 KiB
248 lines
4.5 KiB
#define _GNU_SOURCE
|
|
|
|
#include "fuse_chan.h"
|
|
#include "fuse_lowlevel.h"
|
|
#include "fuse_kernel.h"
|
|
#include "sys.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/ioctl.h>
|
|
#include <unistd.h>
|
|
|
|
static const char DEV_FUSE[] = "/dev/fuse";
|
|
|
|
static
|
|
void*
|
|
aligned_mem(const uint64_t size_)
|
|
{
|
|
int rv;
|
|
void *mem;
|
|
int pagesize;
|
|
|
|
pagesize = sys_get_pagesize();
|
|
|
|
rv = posix_memalign(&mem,pagesize,size_);
|
|
if(rv == 0)
|
|
return mem;
|
|
|
|
errno = rv;
|
|
return NULL;
|
|
}
|
|
|
|
static
|
|
int
|
|
clone_devfuse_fd(const int devfuse_fd_)
|
|
{
|
|
#ifdef FUSE_DEV_IOC_CLONE
|
|
int rv;
|
|
int clone_fd;
|
|
|
|
clone_fd = open(DEV_FUSE,O_RDWR|O_CLOEXEC);
|
|
if(clone_fd == -1)
|
|
return devfuse_fd_;
|
|
|
|
rv = ioctl(clone_fd,FUSE_DEV_IOC_CLONE,&devfuse_fd_);
|
|
if(rv == -1)
|
|
{
|
|
perror("fuse: failed to clone /dev/fuse");
|
|
close(clone_fd);
|
|
return devfuse_fd_;
|
|
}
|
|
|
|
return clone_fd;
|
|
#else
|
|
return devfuse_fd_;
|
|
#endif
|
|
}
|
|
|
|
int
|
|
fuse_msg_bufsize(void)
|
|
{
|
|
int bufsize;
|
|
|
|
bufsize = ((FUSE_MSG_MAX_PAGES + 1) * sys_get_pagesize());
|
|
|
|
return bufsize;
|
|
}
|
|
|
|
fuse_chan_t*
|
|
fuse_chan_new(int32_t devfuse_fd_,
|
|
uint32_t flags_)
|
|
{
|
|
int rv;
|
|
int bufsize;
|
|
fuse_chan_t *ch;
|
|
|
|
ch = (fuse_chan_t*)calloc(1,sizeof(fuse_chan_t));
|
|
if(ch == NULL)
|
|
{
|
|
fprintf(stderr, "fuse: failed to allocate channel memory\n");
|
|
return NULL;
|
|
}
|
|
|
|
bufsize = fuse_msg_bufsize();
|
|
ch->fd = clone_devfuse_fd(devfuse_fd_);
|
|
ch->buf = aligned_mem(bufsize);
|
|
ch->bufsize = bufsize;
|
|
ch->flags = flags_;
|
|
if(flags_ & (FUSE_CHAN_SPLICE_READ|FUSE_CHAN_SPLICE_WRITE))
|
|
{
|
|
rv = sys_alloc_pipe(ch->splice_pipe,bufsize);
|
|
if(rv == -1)
|
|
ch->flags &= ~(FUSE_CHAN_SPLICE_READ|FUSE_CHAN_SPLICE_WRITE);
|
|
}
|
|
|
|
return ch;
|
|
}
|
|
|
|
static
|
|
int64_t
|
|
fuse_chan_recv_splice(fuse_chan_t *ch_,
|
|
char *buf_,
|
|
uint64_t size_)
|
|
{
|
|
int64_t rv;
|
|
struct iovec iov;
|
|
|
|
restart_splice:
|
|
rv = splice(ch_->fd,NULL,ch_->splice_pipe[1],NULL,size_,SPLICE_F_MOVE);
|
|
switch((rv == -1) ? errno : 0)
|
|
{
|
|
case 0:
|
|
break;
|
|
case ENOENT:
|
|
case EINTR:
|
|
case EAGAIN:
|
|
goto restart_splice;
|
|
case ENODEV:
|
|
return 0;
|
|
default:
|
|
return -errno;
|
|
}
|
|
|
|
iov.iov_base = buf_;
|
|
iov.iov_len = rv;
|
|
restart_vmsplice:
|
|
rv = vmsplice(ch_->splice_pipe[0],&iov,1,SPLICE_F_MOVE);
|
|
switch((rv == -1) ? errno : 0)
|
|
{
|
|
case 0:
|
|
break;
|
|
case ENOENT:
|
|
case EINTR:
|
|
case EAGAIN:
|
|
goto restart_vmsplice;
|
|
case ENODEV:
|
|
return 0;
|
|
default:
|
|
return -errno;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
static
|
|
int
|
|
fuse_chan_recv_read(fuse_chan_t *ch_,
|
|
char *buf_,
|
|
uint64_t size_)
|
|
{
|
|
int64_t rv;
|
|
|
|
restart:
|
|
rv = read(ch_->fd,buf_,size_);
|
|
switch((rv == -1) ? errno : 0)
|
|
{
|
|
case 0:
|
|
break;
|
|
case ENOENT:
|
|
case EINTR:
|
|
case EAGAIN:
|
|
goto restart;
|
|
case ENODEV:
|
|
return 0;
|
|
default:
|
|
return -errno;
|
|
}
|
|
|
|
if(rv < sizeof(struct fuse_in_header))
|
|
{
|
|
fprintf(stderr, "fuse: short read on fuse device\n");
|
|
return -EIO;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
int64_t
|
|
fuse_chan_recv(fuse_chan_t *ch_,
|
|
char *buf_,
|
|
size_t size_)
|
|
{
|
|
if(ch_->flags & FUSE_CHAN_SPLICE_READ)
|
|
return fuse_chan_recv_splice(ch_,buf_,size_);
|
|
return fuse_chan_recv_read(ch_,buf_,size_);
|
|
}
|
|
|
|
static
|
|
int64_t
|
|
fuse_chan_send_write(fuse_chan_t *ch_,
|
|
const struct iovec iov_[],
|
|
size_t count_)
|
|
{
|
|
int64_t rv;
|
|
|
|
if(iov_ == NULL)
|
|
return 0;
|
|
|
|
rv = writev(ch_->fd,iov_,count_);
|
|
if(rv == -1)
|
|
return -errno;
|
|
|
|
return rv;
|
|
}
|
|
|
|
static
|
|
int64_t
|
|
fuse_chan_send_splice(fuse_chan_t *ch_,
|
|
const struct iovec iov_[],
|
|
size_t count_)
|
|
{
|
|
int64_t rv;
|
|
|
|
rv = vmsplice(ch_->splice_pipe[1],iov_,count_,SPLICE_F_MOVE);
|
|
if(rv == -1)
|
|
return -errno;
|
|
|
|
rv = splice(ch_->splice_pipe[0],NULL,ch_->fd,NULL,rv,SPLICE_F_MOVE);
|
|
|
|
return rv;
|
|
}
|
|
|
|
int64_t
|
|
fuse_chan_send(fuse_chan_t *ch,
|
|
const struct iovec iov[],
|
|
size_t count)
|
|
{
|
|
if(ch->flags & FUSE_CHAN_SPLICE_WRITE)
|
|
return fuse_chan_send_splice(ch,iov,count);
|
|
return fuse_chan_send_write(ch,iov,count);
|
|
}
|
|
|
|
void
|
|
fuse_chan_destroy(fuse_chan_t *ch)
|
|
{
|
|
close(ch->fd);
|
|
if(ch->flags & (FUSE_CHAN_SPLICE_READ|FUSE_CHAN_SPLICE_WRITE))
|
|
{
|
|
close(ch->splice_pipe[0]);
|
|
close(ch->splice_pipe[1]);
|
|
}
|
|
free(ch->buf);
|
|
free(ch);
|
|
}
|