3 * Copyright (c) 2010-2012 Hans Petter Selasky. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 #include <sys/types.h>
38 #include <sys/queue.h>
39 #include <sys/fcntl.h>
41 #include <sys/param.h>
43 #include <fs/cuse/cuse_ioctl.h>
50 static const char *cuse_cmd_str(int cmd);
52 #define DPRINTF(...) do { \
53 if (cuse_debug_level != 0) \
54 printf(__VA_ARGS__); \
57 #define DPRINTF(...) do { } while (0)
60 struct cuse_vm_allocation {
65 struct cuse_dev_entered {
66 TAILQ_ENTRY(cuse_dev_entered) entry;
68 void *per_file_handle;
69 struct cuse_dev *cdev;
76 TAILQ_ENTRY(cuse_dev) entry;
77 const struct cuse_methods *mtod;
82 static int f_cuse = -1;
84 static pthread_mutex_t m_cuse;
85 static TAILQ_HEAD(, cuse_dev) h_cuse __guarded_by(m_cuse);
86 static TAILQ_HEAD(, cuse_dev_entered) h_cuse_entered __guarded_by(m_cuse);
87 static struct cuse_vm_allocation a_cuse[CUSE_ALLOC_UNIT_MAX]
91 cuse_lock(void) __locks_exclusive(m_cuse)
93 pthread_mutex_lock(&m_cuse);
97 cuse_unlock(void) __unlocks(m_cuse)
99 pthread_mutex_unlock(&m_cuse);
105 pthread_mutexattr_t attr;
107 f_cuse = open("/dev/cuse", O_RDWR);
109 if (feature_present("cuse") == 0)
110 return (CUSE_ERR_NOT_LOADED);
112 return (CUSE_ERR_INVALID);
114 pthread_mutexattr_init(&attr);
115 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
116 pthread_mutex_init(&m_cuse, &attr);
119 TAILQ_INIT(&h_cuse_entered);
130 return (CUSE_ERR_INVALID);
137 pthread_mutex_destroy(&m_cuse);
139 memset(a_cuse, 0, sizeof(a_cuse));
145 cuse_vmoffset(void *_ptr)
150 unsigned long remainder;
154 for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
155 if (a_cuse[n].ptr == NULL)
158 ptr_min = a_cuse[n].ptr;
159 ptr_max = a_cuse[n].ptr + a_cuse[n].size - 1;
161 if ((ptr >= ptr_min) && (ptr <= ptr_max)) {
165 remainder = (ptr - ptr_min);
167 remainder -= remainder % PAGE_SIZE;
169 return ((n * PAGE_SIZE * CUSE_ALLOC_PAGES_MAX) + remainder);
174 return (0x80000000UL); /* failure */
178 cuse_vmalloc(int size)
180 struct cuse_alloc_info info;
188 memset(&info, 0, sizeof(info));
193 info.page_count = (size + PAGE_SIZE - 1) / PAGE_SIZE;
196 for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
198 if (a_cuse[n].ptr != NULL)
201 a_cuse[n].ptr = ((uint8_t *)1); /* reserve */
208 error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_MEMORY, &info);
214 a_cuse[n].ptr = NULL;
221 ptr = mmap(NULL, info.page_count * PAGE_SIZE,
222 PROT_READ | PROT_WRITE,
223 MAP_SHARED, f_cuse, CUSE_ALLOC_PAGES_MAX *
226 if (ptr == MAP_FAILED) {
228 error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
235 a_cuse[n].ptr = NULL;
241 a_cuse[n].size = size;
244 return (ptr); /* success */
247 return (NULL); /* failure */
251 cuse_is_vmalloc_addr(void *ptr)
255 if (f_cuse < 0 || ptr == NULL)
256 return (0); /* false */
259 for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
260 if (a_cuse[n].ptr == ptr)
265 return (n != CUSE_ALLOC_UNIT_MAX);
269 cuse_vmfree(void *ptr)
271 struct cuse_alloc_info info;
278 memset(&info, 0, sizeof(info));
281 for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
282 if (a_cuse[n].ptr != ptr)
289 munmap(ptr, a_cuse[n].size);
291 error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
298 a_cuse[n].ptr = NULL;
307 cuse_alloc_unit_number_by_id(int *pnum, int id)
312 return (CUSE_ERR_INVALID);
314 *pnum = (id & CUSE_ID_MASK);
316 error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT_BY_ID, pnum);
318 return (CUSE_ERR_NO_MEMORY);
325 cuse_free_unit_number_by_id(int num, int id)
330 return (CUSE_ERR_INVALID);
332 if (num != -1 || id != -1)
333 num = (id & CUSE_ID_MASK) | (num & 0xFF);
335 error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT_BY_ID, &num);
337 return (CUSE_ERR_NO_MEMORY);
343 cuse_alloc_unit_number(int *pnum)
348 return (CUSE_ERR_INVALID);
350 error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT, pnum);
352 return (CUSE_ERR_NO_MEMORY);
358 cuse_free_unit_number(int num)
363 return (CUSE_ERR_INVALID);
365 error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT, &num);
367 return (CUSE_ERR_NO_MEMORY);
373 cuse_dev_create(const struct cuse_methods *mtod, void *priv0, void *priv1,
374 uid_t _uid, gid_t _gid, int _perms, const char *_fmt,...)
376 struct cuse_create_dev info;
377 struct cuse_dev *cdev;
384 cdev = malloc(sizeof(*cdev));
388 memset(cdev, 0, sizeof(*cdev));
394 memset(&info, 0, sizeof(info));
398 info.group_id = _gid;
399 info.permissions = _perms;
401 va_start(args, _fmt);
402 vsnprintf(info.devname, sizeof(info.devname), _fmt, args);
405 error = ioctl(f_cuse, CUSE_IOCTL_CREATE_DEV, &info);
411 TAILQ_INSERT_TAIL(&h_cuse, cdev, entry);
419 cuse_dev_destroy(struct cuse_dev *cdev)
427 TAILQ_REMOVE(&h_cuse, cdev, entry);
430 error = ioctl(f_cuse, CUSE_IOCTL_DESTROY_DEV, &cdev);
438 cuse_dev_get_priv0(struct cuse_dev *cdev)
440 return (cdev->priv0);
444 cuse_dev_get_priv1(struct cuse_dev *cdev)
446 return (cdev->priv1);
450 cuse_dev_set_priv0(struct cuse_dev *cdev, void *priv)
456 cuse_dev_set_priv1(struct cuse_dev *cdev, void *priv)
462 cuse_wait_and_process(void)
464 pthread_t curr = pthread_self();
465 struct cuse_dev_entered *pe;
466 struct cuse_dev_entered enter;
467 struct cuse_command info;
468 struct cuse_dev *cdev;
472 return (CUSE_ERR_INVALID);
474 error = ioctl(f_cuse, CUSE_IOCTL_GET_COMMAND, &info);
476 return (CUSE_ERR_OTHER);
482 enter.per_file_handle = (void *)info.per_file_handle;
483 enter.cmd = info.command;
485 enter.got_signal = 0;
487 TAILQ_INSERT_TAIL(&h_cuse_entered, &enter, entry);
490 DPRINTF("cuse: Command = %d = %s, flags = %d, arg = 0x%08x, ptr = 0x%08x\n",
491 (int)info.command, cuse_cmd_str(info.command), (int)info.fflags,
492 (int)info.argument, (int)info.data_pointer);
494 switch (info.command) {
496 if (cdev->mtod->cm_open != NULL)
497 error = (cdev->mtod->cm_open) (cdev, (int)info.fflags);
504 /* wait for other threads to stop */
511 TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
512 if (pe->cdev != cdev)
514 if (pe->thread == curr)
516 if (pe->per_file_handle !=
517 enter.per_file_handle)
520 pthread_kill(pe->thread, SIGHUP);
521 error = CUSE_ERR_BUSY;
531 if (cdev->mtod->cm_close != NULL)
532 error = (cdev->mtod->cm_close) (cdev, (int)info.fflags);
538 if (cdev->mtod->cm_read != NULL) {
539 error = (cdev->mtod->cm_read) (cdev, (int)info.fflags,
540 (void *)info.data_pointer, (int)info.argument);
542 error = CUSE_ERR_INVALID;
547 if (cdev->mtod->cm_write != NULL) {
548 error = (cdev->mtod->cm_write) (cdev, (int)info.fflags,
549 (void *)info.data_pointer, (int)info.argument);
551 error = CUSE_ERR_INVALID;
556 if (cdev->mtod->cm_ioctl != NULL) {
557 error = (cdev->mtod->cm_ioctl) (cdev, (int)info.fflags,
558 (unsigned int)info.argument, (void *)info.data_pointer);
560 error = CUSE_ERR_INVALID;
565 if (cdev->mtod->cm_poll != NULL) {
566 error = (cdev->mtod->cm_poll) (cdev, (int)info.fflags,
569 error = CUSE_POLL_ERROR;
573 case CUSE_CMD_SIGNAL:
575 TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
576 if (pe->cdev != cdev)
578 if (pe->thread == curr)
580 if (pe->per_file_handle !=
581 enter.per_file_handle)
584 pthread_kill(pe->thread, SIGHUP);
590 error = CUSE_ERR_INVALID;
594 DPRINTF("cuse: Command error = %d for %s\n",
595 error, cuse_cmd_str(info.command));
598 TAILQ_REMOVE(&h_cuse_entered, &enter, entry);
601 /* we ignore any sync command failures */
602 ioctl(f_cuse, CUSE_IOCTL_SYNC_COMMAND, &error);
607 static struct cuse_dev_entered *
608 cuse_dev_get_entered(void)
610 struct cuse_dev_entered *pe;
611 pthread_t curr = pthread_self();
614 TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
615 if (pe->thread == curr)
623 cuse_dev_set_per_file_handle(struct cuse_dev *cdev, void *handle)
625 struct cuse_dev_entered *pe;
627 pe = cuse_dev_get_entered();
628 if (pe == NULL || pe->cdev != cdev)
631 pe->per_file_handle = handle;
632 ioctl(f_cuse, CUSE_IOCTL_SET_PFH, &handle);
636 cuse_dev_get_per_file_handle(struct cuse_dev *cdev)
638 struct cuse_dev_entered *pe;
640 pe = cuse_dev_get_entered();
641 if (pe == NULL || pe->cdev != cdev)
644 return (pe->per_file_handle);
648 cuse_set_local(int val)
650 struct cuse_dev_entered *pe;
652 pe = cuse_dev_get_entered();
661 cuse_cmd_str(int cmd)
663 static const char *str[CUSE_CMD_MAX] = {
664 [CUSE_CMD_NONE] = "none",
665 [CUSE_CMD_OPEN] = "open",
666 [CUSE_CMD_CLOSE] = "close",
667 [CUSE_CMD_READ] = "read",
668 [CUSE_CMD_WRITE] = "write",
669 [CUSE_CMD_IOCTL] = "ioctl",
670 [CUSE_CMD_POLL] = "poll",
671 [CUSE_CMD_SIGNAL] = "signal",
672 [CUSE_CMD_SYNC] = "sync",
675 if ((cmd >= 0) && (cmd < CUSE_CMD_MAX) &&
687 struct cuse_dev_entered *pe;
689 pe = cuse_dev_get_entered();
693 return (pe->is_local);
697 cuse_copy_out(const void *src, void *user_dst, int len)
699 struct cuse_data_chunk info;
700 struct cuse_dev_entered *pe;
703 if ((f_cuse < 0) || (len < 0))
704 return (CUSE_ERR_INVALID);
706 pe = cuse_dev_get_entered();
708 return (CUSE_ERR_INVALID);
710 DPRINTF("cuse: copy_out(%p,%p,%d), cmd = %d = %s\n",
711 src, user_dst, len, pe->cmd, cuse_cmd_str(pe->cmd));
714 memcpy(user_dst, src, len);
716 info.local_ptr = (unsigned long)src;
717 info.peer_ptr = (unsigned long)user_dst;
720 error = ioctl(f_cuse, CUSE_IOCTL_WRITE_DATA, &info);
722 DPRINTF("cuse: copy_out() error = %d\n", errno);
723 return (CUSE_ERR_FAULT);
730 cuse_copy_in(const void *user_src, void *dst, int len)
732 struct cuse_data_chunk info;
733 struct cuse_dev_entered *pe;
736 if ((f_cuse < 0) || (len < 0))
737 return (CUSE_ERR_INVALID);
739 pe = cuse_dev_get_entered();
741 return (CUSE_ERR_INVALID);
743 DPRINTF("cuse: copy_in(%p,%p,%d), cmd = %d = %s\n",
744 user_src, dst, len, pe->cmd, cuse_cmd_str(pe->cmd));
747 memcpy(dst, user_src, len);
749 info.local_ptr = (unsigned long)dst;
750 info.peer_ptr = (unsigned long)user_src;
753 error = ioctl(f_cuse, CUSE_IOCTL_READ_DATA, &info);
755 DPRINTF("cuse: copy_in() error = %d\n", errno);
756 return (CUSE_ERR_FAULT);
763 cuse_dev_get_current(int *pcmd)
765 struct cuse_dev_entered *pe;
767 pe = cuse_dev_get_entered();
779 cuse_got_peer_signal(void)
781 struct cuse_dev_entered *pe;
783 pe = cuse_dev_get_entered();
785 return (CUSE_ERR_INVALID);
790 return (CUSE_ERR_OTHER);
794 cuse_poll_wakeup(void)
801 ioctl(f_cuse, CUSE_IOCTL_SELWAKEUP, &error);