58a8f6e9ec
The manual page date will be bumped separately. MFC after: 1 week Sponsored by: NVIDIA Networking
796 lines
15 KiB
C
796 lines
15 KiB
C
/* $FreeBSD$ */
|
|
/*-
|
|
* Copyright (c) 2010-2022 Hans Petter Selasky. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <pthread.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/param.h>
|
|
|
|
#include <fs/cuse/cuse_ioctl.h>
|
|
|
|
#include "cuse.h"
|
|
|
|
int cuse_debug_level;
|
|
|
|
#ifdef HAVE_DEBUG
|
|
static const char *cuse_cmd_str(int cmd);
|
|
|
|
#define DPRINTF(...) do { \
|
|
if (cuse_debug_level != 0) \
|
|
printf(__VA_ARGS__); \
|
|
} while (0)
|
|
#else
|
|
#define DPRINTF(...) do { } while (0)
|
|
#endif
|
|
|
|
struct cuse_vm_allocation {
|
|
uint8_t *ptr;
|
|
uint32_t size;
|
|
};
|
|
|
|
struct cuse_dev_entered {
|
|
TAILQ_ENTRY(cuse_dev_entered) entry;
|
|
pthread_t thread;
|
|
void *per_file_handle;
|
|
struct cuse_dev *cdev;
|
|
int cmd;
|
|
int is_local;
|
|
int got_signal;
|
|
};
|
|
|
|
struct cuse_dev {
|
|
TAILQ_ENTRY(cuse_dev) entry;
|
|
const struct cuse_methods *mtod;
|
|
void *priv0;
|
|
void *priv1;
|
|
};
|
|
|
|
static int f_cuse = -1;
|
|
|
|
static pthread_mutex_t m_cuse;
|
|
static TAILQ_HEAD(, cuse_dev) h_cuse __guarded_by(m_cuse);
|
|
static TAILQ_HEAD(, cuse_dev_entered) h_cuse_entered __guarded_by(m_cuse);
|
|
static struct cuse_vm_allocation a_cuse[CUSE_ALLOC_UNIT_MAX]
|
|
__guarded_by(m_cuse);
|
|
|
|
#define CUSE_LOCK() \
|
|
pthread_mutex_lock(&m_cuse)
|
|
|
|
#define CUSE_UNLOCK() \
|
|
pthread_mutex_unlock(&m_cuse)
|
|
|
|
int
|
|
cuse_init(void)
|
|
{
|
|
pthread_mutexattr_t attr;
|
|
|
|
f_cuse = open("/dev/cuse", O_RDWR);
|
|
if (f_cuse < 0) {
|
|
if (feature_present("cuse") == 0)
|
|
return (CUSE_ERR_NOT_LOADED);
|
|
else
|
|
return (CUSE_ERR_INVALID);
|
|
}
|
|
pthread_mutexattr_init(&attr);
|
|
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
|
pthread_mutex_init(&m_cuse, &attr);
|
|
|
|
TAILQ_INIT(&h_cuse);
|
|
TAILQ_INIT(&h_cuse_entered);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cuse_uninit(void)
|
|
{
|
|
int f;
|
|
|
|
if (f_cuse < 0)
|
|
return (CUSE_ERR_INVALID);
|
|
|
|
f = f_cuse;
|
|
f_cuse = -1;
|
|
|
|
close(f);
|
|
|
|
pthread_mutex_destroy(&m_cuse);
|
|
|
|
memset(a_cuse, 0, sizeof(a_cuse));
|
|
|
|
return (0);
|
|
}
|
|
|
|
unsigned long
|
|
cuse_vmoffset(void *_ptr)
|
|
{
|
|
uint8_t *ptr_min;
|
|
uint8_t *ptr_max;
|
|
uint8_t *ptr = _ptr;
|
|
unsigned long remainder;
|
|
unsigned long n;
|
|
|
|
CUSE_LOCK();
|
|
for (n = remainder = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
|
|
if (a_cuse[n].ptr == NULL)
|
|
continue;
|
|
|
|
ptr_min = a_cuse[n].ptr;
|
|
ptr_max = a_cuse[n].ptr + a_cuse[n].size - 1;
|
|
|
|
if ((ptr >= ptr_min) && (ptr <= ptr_max)) {
|
|
remainder = (ptr - ptr_min);
|
|
break;
|
|
}
|
|
}
|
|
CUSE_UNLOCK();
|
|
|
|
return ((n << CUSE_ALLOC_UNIT_SHIFT) + remainder);
|
|
}
|
|
|
|
void *
|
|
cuse_vmalloc(unsigned size)
|
|
{
|
|
struct cuse_alloc_info info;
|
|
unsigned long pgsize;
|
|
unsigned long x;
|
|
unsigned long m;
|
|
unsigned long n;
|
|
void *ptr;
|
|
int error;
|
|
|
|
/* some sanity checks */
|
|
if (f_cuse < 0 || size < 1 || size > CUSE_ALLOC_BYTES_MAX)
|
|
return (NULL);
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
pgsize = getpagesize();
|
|
info.page_count = howmany(size, pgsize);
|
|
|
|
/* compute how many units the allocation needs */
|
|
m = howmany(size, 1 << CUSE_ALLOC_UNIT_SHIFT);
|
|
if (m == 0 || m > CUSE_ALLOC_UNIT_MAX)
|
|
return (NULL);
|
|
|
|
CUSE_LOCK();
|
|
for (n = 0; n <= CUSE_ALLOC_UNIT_MAX - m; ) {
|
|
if (a_cuse[n].size != 0) {
|
|
/* skip to next available unit, depending on allocation size */
|
|
n += howmany(a_cuse[n].size, 1 << CUSE_ALLOC_UNIT_SHIFT);
|
|
continue;
|
|
}
|
|
/* check if there are "m" free units ahead */
|
|
for (x = 1; x != m; x++) {
|
|
if (a_cuse[n + x].size != 0)
|
|
break;
|
|
}
|
|
if (x != m) {
|
|
/* skip to next available unit, if any */
|
|
n += x + 1;
|
|
continue;
|
|
}
|
|
/* reserve this unit by setting the size to a non-zero value */
|
|
a_cuse[n].size = size;
|
|
CUSE_UNLOCK();
|
|
|
|
info.alloc_nr = n;
|
|
|
|
error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_MEMORY, &info);
|
|
|
|
if (error == 0) {
|
|
ptr = mmap(NULL, info.page_count * pgsize,
|
|
PROT_READ | PROT_WRITE,
|
|
MAP_SHARED, f_cuse, n << CUSE_ALLOC_UNIT_SHIFT);
|
|
|
|
if (ptr != MAP_FAILED) {
|
|
CUSE_LOCK();
|
|
a_cuse[n].ptr = ptr;
|
|
CUSE_UNLOCK();
|
|
|
|
return (ptr); /* success */
|
|
}
|
|
|
|
(void) ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
|
|
}
|
|
|
|
CUSE_LOCK();
|
|
a_cuse[n].size = 0;
|
|
n++;
|
|
}
|
|
CUSE_UNLOCK();
|
|
return (NULL); /* failure */
|
|
}
|
|
|
|
int
|
|
cuse_is_vmalloc_addr(void *ptr)
|
|
{
|
|
int n;
|
|
|
|
if (f_cuse < 0 || ptr == NULL)
|
|
return (0); /* false */
|
|
|
|
CUSE_LOCK();
|
|
for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
|
|
if (a_cuse[n].ptr == ptr)
|
|
break;
|
|
}
|
|
CUSE_UNLOCK();
|
|
|
|
return (n != CUSE_ALLOC_UNIT_MAX);
|
|
}
|
|
|
|
void
|
|
cuse_vmfree(void *ptr)
|
|
{
|
|
struct cuse_vm_allocation temp;
|
|
struct cuse_alloc_info info;
|
|
int error;
|
|
int n;
|
|
|
|
if (f_cuse < 0 || ptr == NULL)
|
|
return;
|
|
|
|
CUSE_LOCK();
|
|
for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
|
|
if (a_cuse[n].ptr != ptr)
|
|
continue;
|
|
|
|
temp = a_cuse[n];
|
|
|
|
CUSE_UNLOCK();
|
|
|
|
munmap(temp.ptr, temp.size);
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
info.alloc_nr = n;
|
|
|
|
error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
|
|
|
|
if (error != 0) {
|
|
/* ignore any errors */
|
|
DPRINTF("Freeing memory failed: %d\n", errno);
|
|
}
|
|
CUSE_LOCK();
|
|
|
|
a_cuse[n].ptr = NULL;
|
|
a_cuse[n].size = 0;
|
|
|
|
break;
|
|
}
|
|
CUSE_UNLOCK();
|
|
}
|
|
|
|
int
|
|
cuse_alloc_unit_number_by_id(int *pnum, int id)
|
|
{
|
|
int error;
|
|
|
|
if (f_cuse < 0)
|
|
return (CUSE_ERR_INVALID);
|
|
|
|
*pnum = (id & CUSE_ID_MASK);
|
|
|
|
error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT_BY_ID, pnum);
|
|
if (error)
|
|
return (CUSE_ERR_NO_MEMORY);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
int
|
|
cuse_free_unit_number_by_id(int num, int id)
|
|
{
|
|
int error;
|
|
|
|
if (f_cuse < 0)
|
|
return (CUSE_ERR_INVALID);
|
|
|
|
if (num != -1 || id != -1)
|
|
num = (id & CUSE_ID_MASK) | (num & 0xFF);
|
|
|
|
error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT_BY_ID, &num);
|
|
if (error)
|
|
return (CUSE_ERR_NO_MEMORY);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cuse_alloc_unit_number(int *pnum)
|
|
{
|
|
int error;
|
|
|
|
if (f_cuse < 0)
|
|
return (CUSE_ERR_INVALID);
|
|
|
|
error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT, pnum);
|
|
if (error)
|
|
return (CUSE_ERR_NO_MEMORY);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cuse_free_unit_number(int num)
|
|
{
|
|
int error;
|
|
|
|
if (f_cuse < 0)
|
|
return (CUSE_ERR_INVALID);
|
|
|
|
error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT, &num);
|
|
if (error)
|
|
return (CUSE_ERR_NO_MEMORY);
|
|
|
|
return (0);
|
|
}
|
|
|
|
struct cuse_dev *
|
|
cuse_dev_create(const struct cuse_methods *mtod, void *priv0, void *priv1,
|
|
uid_t _uid, gid_t _gid, int _perms, const char *_fmt,...)
|
|
{
|
|
struct cuse_create_dev info;
|
|
struct cuse_dev *cdev;
|
|
va_list args;
|
|
int error;
|
|
|
|
if (f_cuse < 0)
|
|
return (NULL);
|
|
|
|
cdev = malloc(sizeof(*cdev));
|
|
if (cdev == NULL)
|
|
return (NULL);
|
|
|
|
memset(cdev, 0, sizeof(*cdev));
|
|
|
|
cdev->mtod = mtod;
|
|
cdev->priv0 = priv0;
|
|
cdev->priv1 = priv1;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
info.dev = cdev;
|
|
info.user_id = _uid;
|
|
info.group_id = _gid;
|
|
info.permissions = _perms;
|
|
|
|
va_start(args, _fmt);
|
|
vsnprintf(info.devname, sizeof(info.devname), _fmt, args);
|
|
va_end(args);
|
|
|
|
error = ioctl(f_cuse, CUSE_IOCTL_CREATE_DEV, &info);
|
|
if (error) {
|
|
free(cdev);
|
|
return (NULL);
|
|
}
|
|
CUSE_LOCK();
|
|
TAILQ_INSERT_TAIL(&h_cuse, cdev, entry);
|
|
CUSE_UNLOCK();
|
|
|
|
return (cdev);
|
|
}
|
|
|
|
|
|
void
|
|
cuse_dev_destroy(struct cuse_dev *cdev)
|
|
{
|
|
int error;
|
|
|
|
if (f_cuse < 0)
|
|
return;
|
|
|
|
CUSE_LOCK();
|
|
TAILQ_REMOVE(&h_cuse, cdev, entry);
|
|
CUSE_UNLOCK();
|
|
|
|
error = ioctl(f_cuse, CUSE_IOCTL_DESTROY_DEV, &cdev);
|
|
if (error)
|
|
return;
|
|
|
|
free(cdev);
|
|
}
|
|
|
|
void *
|
|
cuse_dev_get_priv0(struct cuse_dev *cdev)
|
|
{
|
|
return (cdev->priv0);
|
|
}
|
|
|
|
void *
|
|
cuse_dev_get_priv1(struct cuse_dev *cdev)
|
|
{
|
|
return (cdev->priv1);
|
|
}
|
|
|
|
void
|
|
cuse_dev_set_priv0(struct cuse_dev *cdev, void *priv)
|
|
{
|
|
cdev->priv0 = priv;
|
|
}
|
|
|
|
void
|
|
cuse_dev_set_priv1(struct cuse_dev *cdev, void *priv)
|
|
{
|
|
cdev->priv1 = priv;
|
|
}
|
|
|
|
int
|
|
cuse_wait_and_process(void)
|
|
{
|
|
pthread_t curr = pthread_self();
|
|
struct cuse_dev_entered *pe;
|
|
struct cuse_dev_entered enter;
|
|
struct cuse_command info;
|
|
struct cuse_dev *cdev;
|
|
int error;
|
|
|
|
if (f_cuse < 0)
|
|
return (CUSE_ERR_INVALID);
|
|
|
|
error = ioctl(f_cuse, CUSE_IOCTL_GET_COMMAND, &info);
|
|
if (error)
|
|
return (CUSE_ERR_OTHER);
|
|
|
|
cdev = info.dev;
|
|
|
|
CUSE_LOCK();
|
|
enter.thread = curr;
|
|
enter.per_file_handle = (void *)info.per_file_handle;
|
|
enter.cmd = info.command;
|
|
enter.is_local = 0;
|
|
enter.got_signal = 0;
|
|
enter.cdev = cdev;
|
|
TAILQ_INSERT_TAIL(&h_cuse_entered, &enter, entry);
|
|
CUSE_UNLOCK();
|
|
|
|
DPRINTF("cuse: Command = %d = %s, flags = %d, arg = 0x%08x, ptr = 0x%08x\n",
|
|
(int)info.command, cuse_cmd_str(info.command), (int)info.fflags,
|
|
(int)info.argument, (int)info.data_pointer);
|
|
|
|
switch (info.command) {
|
|
case CUSE_CMD_OPEN:
|
|
if (cdev->mtod->cm_open != NULL)
|
|
error = (cdev->mtod->cm_open) (cdev, (int)info.fflags);
|
|
else
|
|
error = 0;
|
|
break;
|
|
|
|
case CUSE_CMD_CLOSE:
|
|
|
|
/* wait for other threads to stop */
|
|
|
|
while (1) {
|
|
|
|
error = 0;
|
|
|
|
CUSE_LOCK();
|
|
TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
|
|
if (pe->cdev != cdev)
|
|
continue;
|
|
if (pe->thread == curr)
|
|
continue;
|
|
if (pe->per_file_handle !=
|
|
enter.per_file_handle)
|
|
continue;
|
|
pe->got_signal = 1;
|
|
pthread_kill(pe->thread, SIGHUP);
|
|
error = CUSE_ERR_BUSY;
|
|
}
|
|
CUSE_UNLOCK();
|
|
|
|
if (error == 0)
|
|
break;
|
|
else
|
|
usleep(10000);
|
|
}
|
|
|
|
if (cdev->mtod->cm_close != NULL)
|
|
error = (cdev->mtod->cm_close) (cdev, (int)info.fflags);
|
|
else
|
|
error = 0;
|
|
break;
|
|
|
|
case CUSE_CMD_READ:
|
|
if (cdev->mtod->cm_read != NULL) {
|
|
error = (cdev->mtod->cm_read) (cdev, (int)info.fflags,
|
|
(void *)info.data_pointer, (int)info.argument);
|
|
} else {
|
|
error = CUSE_ERR_INVALID;
|
|
}
|
|
break;
|
|
|
|
case CUSE_CMD_WRITE:
|
|
if (cdev->mtod->cm_write != NULL) {
|
|
error = (cdev->mtod->cm_write) (cdev, (int)info.fflags,
|
|
(void *)info.data_pointer, (int)info.argument);
|
|
} else {
|
|
error = CUSE_ERR_INVALID;
|
|
}
|
|
break;
|
|
|
|
case CUSE_CMD_IOCTL:
|
|
if (cdev->mtod->cm_ioctl != NULL) {
|
|
error = (cdev->mtod->cm_ioctl) (cdev, (int)info.fflags,
|
|
(unsigned int)info.argument, (void *)info.data_pointer);
|
|
} else {
|
|
error = CUSE_ERR_INVALID;
|
|
}
|
|
break;
|
|
|
|
case CUSE_CMD_POLL:
|
|
if (cdev->mtod->cm_poll != NULL) {
|
|
error = (cdev->mtod->cm_poll) (cdev, (int)info.fflags,
|
|
(int)info.argument);
|
|
} else {
|
|
error = CUSE_POLL_ERROR;
|
|
}
|
|
break;
|
|
|
|
case CUSE_CMD_SIGNAL:
|
|
CUSE_LOCK();
|
|
TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
|
|
if (pe->cdev != cdev)
|
|
continue;
|
|
if (pe->thread == curr)
|
|
continue;
|
|
if (pe->per_file_handle !=
|
|
enter.per_file_handle)
|
|
continue;
|
|
pe->got_signal = 1;
|
|
pthread_kill(pe->thread, SIGHUP);
|
|
}
|
|
CUSE_UNLOCK();
|
|
break;
|
|
|
|
default:
|
|
error = CUSE_ERR_INVALID;
|
|
break;
|
|
}
|
|
|
|
DPRINTF("cuse: Command error = %d for %s\n",
|
|
error, cuse_cmd_str(info.command));
|
|
|
|
CUSE_LOCK();
|
|
TAILQ_REMOVE(&h_cuse_entered, &enter, entry);
|
|
CUSE_UNLOCK();
|
|
|
|
/* we ignore any sync command failures */
|
|
ioctl(f_cuse, CUSE_IOCTL_SYNC_COMMAND, &error);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static struct cuse_dev_entered *
|
|
cuse_dev_get_entered(void)
|
|
{
|
|
struct cuse_dev_entered *pe;
|
|
pthread_t curr = pthread_self();
|
|
|
|
CUSE_LOCK();
|
|
TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
|
|
if (pe->thread == curr)
|
|
break;
|
|
}
|
|
CUSE_UNLOCK();
|
|
return (pe);
|
|
}
|
|
|
|
void
|
|
cuse_dev_set_per_file_handle(struct cuse_dev *cdev, void *handle)
|
|
{
|
|
struct cuse_dev_entered *pe;
|
|
|
|
pe = cuse_dev_get_entered();
|
|
if (pe == NULL || pe->cdev != cdev)
|
|
return;
|
|
|
|
pe->per_file_handle = handle;
|
|
ioctl(f_cuse, CUSE_IOCTL_SET_PFH, &handle);
|
|
}
|
|
|
|
void *
|
|
cuse_dev_get_per_file_handle(struct cuse_dev *cdev)
|
|
{
|
|
struct cuse_dev_entered *pe;
|
|
|
|
pe = cuse_dev_get_entered();
|
|
if (pe == NULL || pe->cdev != cdev)
|
|
return (NULL);
|
|
|
|
return (pe->per_file_handle);
|
|
}
|
|
|
|
void
|
|
cuse_set_local(int val)
|
|
{
|
|
struct cuse_dev_entered *pe;
|
|
|
|
pe = cuse_dev_get_entered();
|
|
if (pe == NULL)
|
|
return;
|
|
|
|
pe->is_local = val;
|
|
}
|
|
|
|
#ifdef HAVE_DEBUG
|
|
static const char *
|
|
cuse_cmd_str(int cmd)
|
|
{
|
|
static const char *str[CUSE_CMD_MAX] = {
|
|
[CUSE_CMD_NONE] = "none",
|
|
[CUSE_CMD_OPEN] = "open",
|
|
[CUSE_CMD_CLOSE] = "close",
|
|
[CUSE_CMD_READ] = "read",
|
|
[CUSE_CMD_WRITE] = "write",
|
|
[CUSE_CMD_IOCTL] = "ioctl",
|
|
[CUSE_CMD_POLL] = "poll",
|
|
[CUSE_CMD_SIGNAL] = "signal",
|
|
[CUSE_CMD_SYNC] = "sync",
|
|
};
|
|
|
|
if ((cmd >= 0) && (cmd < CUSE_CMD_MAX) &&
|
|
(str[cmd] != NULL))
|
|
return (str[cmd]);
|
|
else
|
|
return ("unknown");
|
|
}
|
|
|
|
#endif
|
|
|
|
int
|
|
cuse_get_local(void)
|
|
{
|
|
struct cuse_dev_entered *pe;
|
|
|
|
pe = cuse_dev_get_entered();
|
|
if (pe == NULL)
|
|
return (0);
|
|
|
|
return (pe->is_local);
|
|
}
|
|
|
|
int
|
|
cuse_copy_out(const void *src, void *user_dst, int len)
|
|
{
|
|
struct cuse_data_chunk info;
|
|
struct cuse_dev_entered *pe;
|
|
int error;
|
|
|
|
if ((f_cuse < 0) || (len < 0))
|
|
return (CUSE_ERR_INVALID);
|
|
|
|
pe = cuse_dev_get_entered();
|
|
if (pe == NULL)
|
|
return (CUSE_ERR_INVALID);
|
|
|
|
DPRINTF("cuse: copy_out(%p,%p,%d), cmd = %d = %s\n",
|
|
src, user_dst, len, pe->cmd, cuse_cmd_str(pe->cmd));
|
|
|
|
if (pe->is_local) {
|
|
memcpy(user_dst, src, len);
|
|
} else {
|
|
info.local_ptr = (uintptr_t)src;
|
|
info.peer_ptr = (uintptr_t)user_dst;
|
|
info.length = len;
|
|
|
|
error = ioctl(f_cuse, CUSE_IOCTL_WRITE_DATA, &info);
|
|
if (error) {
|
|
DPRINTF("cuse: copy_out() error = %d\n", errno);
|
|
return (CUSE_ERR_FAULT);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cuse_copy_in(const void *user_src, void *dst, int len)
|
|
{
|
|
struct cuse_data_chunk info;
|
|
struct cuse_dev_entered *pe;
|
|
int error;
|
|
|
|
if ((f_cuse < 0) || (len < 0))
|
|
return (CUSE_ERR_INVALID);
|
|
|
|
pe = cuse_dev_get_entered();
|
|
if (pe == NULL)
|
|
return (CUSE_ERR_INVALID);
|
|
|
|
DPRINTF("cuse: copy_in(%p,%p,%d), cmd = %d = %s\n",
|
|
user_src, dst, len, pe->cmd, cuse_cmd_str(pe->cmd));
|
|
|
|
if (pe->is_local) {
|
|
memcpy(dst, user_src, len);
|
|
} else {
|
|
info.local_ptr = (uintptr_t)dst;
|
|
info.peer_ptr = (uintptr_t)user_src;
|
|
info.length = len;
|
|
|
|
error = ioctl(f_cuse, CUSE_IOCTL_READ_DATA, &info);
|
|
if (error) {
|
|
DPRINTF("cuse: copy_in() error = %d\n", errno);
|
|
return (CUSE_ERR_FAULT);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
struct cuse_dev *
|
|
cuse_dev_get_current(int *pcmd)
|
|
{
|
|
struct cuse_dev_entered *pe;
|
|
|
|
pe = cuse_dev_get_entered();
|
|
if (pe == NULL) {
|
|
if (pcmd != NULL)
|
|
*pcmd = 0;
|
|
return (NULL);
|
|
}
|
|
if (pcmd != NULL)
|
|
*pcmd = pe->cmd;
|
|
return (pe->cdev);
|
|
}
|
|
|
|
int
|
|
cuse_got_peer_signal(void)
|
|
{
|
|
struct cuse_dev_entered *pe;
|
|
|
|
pe = cuse_dev_get_entered();
|
|
if (pe == NULL)
|
|
return (CUSE_ERR_INVALID);
|
|
|
|
if (pe->got_signal)
|
|
return (0);
|
|
|
|
return (CUSE_ERR_OTHER);
|
|
}
|
|
|
|
void
|
|
cuse_poll_wakeup(void)
|
|
{
|
|
int error = 0;
|
|
|
|
if (f_cuse < 0)
|
|
return;
|
|
|
|
ioctl(f_cuse, CUSE_IOCTL_SELWAKEUP, &error);
|
|
}
|