xref: /freebsd/lib/libcuse/cuse_lib.c (revision b0d29bc47dba79f6f38e67eabadfb4b32ffd9390)
1 /* $FreeBSD$ */
2 /*-
3  * Copyright (c) 2010-2012 Hans Petter Selasky. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  */
26 
27 #include <stdio.h>
28 #include <stdint.h>
29 #include <pthread.h>
30 #include <signal.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 
37 #include <sys/types.h>
38 #include <sys/queue.h>
39 #include <sys/fcntl.h>
40 #include <sys/mman.h>
41 #include <sys/param.h>
42 
43 #include <fs/cuse/cuse_ioctl.h>
44 
45 #include "cuse.h"
46 
47 int	cuse_debug_level;
48 
49 #ifdef HAVE_DEBUG
50 static const char *cuse_cmd_str(int cmd);
51 
52 #define	DPRINTF(...) do {			\
53 	if (cuse_debug_level != 0)		\
54 		printf(__VA_ARGS__);		\
55 } while (0)
56 #else
57 #define	DPRINTF(...) do { } while (0)
58 #endif
59 
60 struct cuse_vm_allocation {
61 	uint8_t *ptr;
62 	uint32_t size;
63 };
64 
65 struct cuse_dev_entered {
66 	TAILQ_ENTRY(cuse_dev_entered) entry;
67 	pthread_t thread;
68 	void   *per_file_handle;
69 	struct cuse_dev *cdev;
70 	int	cmd;
71 	int	is_local;
72 	int	got_signal;
73 };
74 
75 struct cuse_dev {
76 	TAILQ_ENTRY(cuse_dev) entry;
77 	const struct cuse_methods *mtod;
78 	void   *priv0;
79 	void   *priv1;
80 };
81 
82 static int f_cuse = -1;
83 
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]
88     __guarded_by(m_cuse);
89 
90 #define	CUSE_LOCK() \
91 	pthread_mutex_lock(&m_cuse)
92 
93 #define	CUSE_UNLOCK() \
94 	pthread_mutex_unlock(&m_cuse)
95 
96 int
97 cuse_init(void)
98 {
99 	pthread_mutexattr_t attr;
100 
101 	f_cuse = open("/dev/cuse", O_RDWR);
102 	if (f_cuse < 0) {
103 		if (feature_present("cuse") == 0)
104 			return (CUSE_ERR_NOT_LOADED);
105 		else
106 			return (CUSE_ERR_INVALID);
107 	}
108 	pthread_mutexattr_init(&attr);
109 	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
110 	pthread_mutex_init(&m_cuse, &attr);
111 
112 	TAILQ_INIT(&h_cuse);
113 	TAILQ_INIT(&h_cuse_entered);
114 
115 	return (0);
116 }
117 
118 int
119 cuse_uninit(void)
120 {
121 	int f;
122 
123 	if (f_cuse < 0)
124 		return (CUSE_ERR_INVALID);
125 
126 	f = f_cuse;
127 	f_cuse = -1;
128 
129 	close(f);
130 
131 	pthread_mutex_destroy(&m_cuse);
132 
133 	memset(a_cuse, 0, sizeof(a_cuse));
134 
135 	return (0);
136 }
137 
138 unsigned long
139 cuse_vmoffset(void *_ptr)
140 {
141 	uint8_t *ptr_min;
142 	uint8_t *ptr_max;
143 	uint8_t *ptr = _ptr;
144 	unsigned long remainder;
145 	int n;
146 
147 	CUSE_LOCK();
148 	for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
149 		if (a_cuse[n].ptr == NULL)
150 			continue;
151 
152 		ptr_min = a_cuse[n].ptr;
153 		ptr_max = a_cuse[n].ptr + a_cuse[n].size - 1;
154 
155 		if ((ptr >= ptr_min) && (ptr <= ptr_max)) {
156 
157 			CUSE_UNLOCK();
158 
159 			remainder = (ptr - ptr_min);
160 
161 			remainder -= remainder % PAGE_SIZE;
162 
163 			return ((n * PAGE_SIZE * CUSE_ALLOC_PAGES_MAX) + remainder);
164 		}
165 	}
166 	CUSE_UNLOCK();
167 
168 	return (0x80000000UL);		/* failure */
169 }
170 
171 void   *
172 cuse_vmalloc(int size)
173 {
174 	struct cuse_alloc_info info;
175 	void *ptr;
176 	int error;
177 	int n;
178 
179 	if (f_cuse < 0)
180 		return (NULL);
181 
182 	memset(&info, 0, sizeof(info));
183 
184 	if (size < 1)
185 		return (NULL);
186 
187 	info.page_count = howmany(size, PAGE_SIZE);
188 
189 	CUSE_LOCK();
190 	for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
191 
192 		if (a_cuse[n].ptr != NULL)
193 			continue;
194 
195 		a_cuse[n].ptr = ((uint8_t *)1);	/* reserve */
196 		a_cuse[n].size = 0;
197 
198 		CUSE_UNLOCK();
199 
200 		info.alloc_nr = n;
201 
202 		error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_MEMORY, &info);
203 
204 		if (error) {
205 
206 			CUSE_LOCK();
207 
208 			a_cuse[n].ptr = NULL;
209 
210 			if (errno == EBUSY)
211 				continue;
212 			else
213 				break;
214 		}
215 		ptr = mmap(NULL, info.page_count * PAGE_SIZE,
216 		    PROT_READ | PROT_WRITE,
217 		    MAP_SHARED, f_cuse, CUSE_ALLOC_PAGES_MAX *
218 		    PAGE_SIZE * n);
219 
220 		if (ptr == MAP_FAILED) {
221 
222 			error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
223 
224 			if (error) {
225 				/* ignore */
226 			}
227 			CUSE_LOCK();
228 
229 			a_cuse[n].ptr = NULL;
230 
231 			break;
232 		}
233 		CUSE_LOCK();
234 		a_cuse[n].ptr = ptr;
235 		a_cuse[n].size = size;
236 		CUSE_UNLOCK();
237 
238 		return (ptr);		/* success */
239 	}
240 	CUSE_UNLOCK();
241 	return (NULL);			/* failure */
242 }
243 
244 int
245 cuse_is_vmalloc_addr(void *ptr)
246 {
247 	int n;
248 
249 	if (f_cuse < 0 || ptr == NULL)
250 		return (0);		/* false */
251 
252 	CUSE_LOCK();
253 	for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
254 		if (a_cuse[n].ptr == ptr)
255 			break;
256 	}
257 	CUSE_UNLOCK();
258 
259 	return (n != CUSE_ALLOC_UNIT_MAX);
260 }
261 
262 void
263 cuse_vmfree(void *ptr)
264 {
265 	struct cuse_vm_allocation temp;
266 	struct cuse_alloc_info info;
267 	int error;
268 	int n;
269 
270 	if (f_cuse < 0)
271 		return;
272 
273 	CUSE_LOCK();
274 	for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
275 		if (a_cuse[n].ptr != ptr)
276 			continue;
277 
278 		temp = a_cuse[n];
279 
280 		CUSE_UNLOCK();
281 
282 		munmap(temp.ptr, temp.size);
283 
284 		memset(&info, 0, sizeof(info));
285 
286 		info.alloc_nr = n;
287 
288 		error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
289 
290 		if (error != 0) {
291 			/* ignore any errors */
292 			DPRINTF("Freeing memory failed: %d\n", errno);
293 		}
294 		CUSE_LOCK();
295 
296 		a_cuse[n].ptr = NULL;
297 		a_cuse[n].size = 0;
298 
299 		break;
300 	}
301 	CUSE_UNLOCK();
302 }
303 
304 int
305 cuse_alloc_unit_number_by_id(int *pnum, int id)
306 {
307 	int error;
308 
309 	if (f_cuse < 0)
310 		return (CUSE_ERR_INVALID);
311 
312 	*pnum = (id & CUSE_ID_MASK);
313 
314 	error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT_BY_ID, pnum);
315 	if (error)
316 		return (CUSE_ERR_NO_MEMORY);
317 
318 	return (0);
319 
320 }
321 
322 int
323 cuse_free_unit_number_by_id(int num, int id)
324 {
325 	int error;
326 
327 	if (f_cuse < 0)
328 		return (CUSE_ERR_INVALID);
329 
330 	if (num != -1 || id != -1)
331 		num = (id & CUSE_ID_MASK) | (num & 0xFF);
332 
333 	error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT_BY_ID, &num);
334 	if (error)
335 		return (CUSE_ERR_NO_MEMORY);
336 
337 	return (0);
338 }
339 
340 int
341 cuse_alloc_unit_number(int *pnum)
342 {
343 	int error;
344 
345 	if (f_cuse < 0)
346 		return (CUSE_ERR_INVALID);
347 
348 	error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT, pnum);
349 	if (error)
350 		return (CUSE_ERR_NO_MEMORY);
351 
352 	return (0);
353 }
354 
355 int
356 cuse_free_unit_number(int num)
357 {
358 	int error;
359 
360 	if (f_cuse < 0)
361 		return (CUSE_ERR_INVALID);
362 
363 	error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT, &num);
364 	if (error)
365 		return (CUSE_ERR_NO_MEMORY);
366 
367 	return (0);
368 }
369 
370 struct cuse_dev *
371 cuse_dev_create(const struct cuse_methods *mtod, void *priv0, void *priv1,
372     uid_t _uid, gid_t _gid, int _perms, const char *_fmt,...)
373 {
374 	struct cuse_create_dev info;
375 	struct cuse_dev *cdev;
376 	va_list args;
377 	int error;
378 
379 	if (f_cuse < 0)
380 		return (NULL);
381 
382 	cdev = malloc(sizeof(*cdev));
383 	if (cdev == NULL)
384 		return (NULL);
385 
386 	memset(cdev, 0, sizeof(*cdev));
387 
388 	cdev->mtod = mtod;
389 	cdev->priv0 = priv0;
390 	cdev->priv1 = priv1;
391 
392 	memset(&info, 0, sizeof(info));
393 
394 	info.dev = cdev;
395 	info.user_id = _uid;
396 	info.group_id = _gid;
397 	info.permissions = _perms;
398 
399 	va_start(args, _fmt);
400 	vsnprintf(info.devname, sizeof(info.devname), _fmt, args);
401 	va_end(args);
402 
403 	error = ioctl(f_cuse, CUSE_IOCTL_CREATE_DEV, &info);
404 	if (error) {
405 		free(cdev);
406 		return (NULL);
407 	}
408 	CUSE_LOCK();
409 	TAILQ_INSERT_TAIL(&h_cuse, cdev, entry);
410 	CUSE_UNLOCK();
411 
412 	return (cdev);
413 }
414 
415 
416 void
417 cuse_dev_destroy(struct cuse_dev *cdev)
418 {
419 	int error;
420 
421 	if (f_cuse < 0)
422 		return;
423 
424 	CUSE_LOCK();
425 	TAILQ_REMOVE(&h_cuse, cdev, entry);
426 	CUSE_UNLOCK();
427 
428 	error = ioctl(f_cuse, CUSE_IOCTL_DESTROY_DEV, &cdev);
429 	if (error)
430 		return;
431 
432 	free(cdev);
433 }
434 
435 void   *
436 cuse_dev_get_priv0(struct cuse_dev *cdev)
437 {
438 	return (cdev->priv0);
439 }
440 
441 void   *
442 cuse_dev_get_priv1(struct cuse_dev *cdev)
443 {
444 	return (cdev->priv1);
445 }
446 
447 void
448 cuse_dev_set_priv0(struct cuse_dev *cdev, void *priv)
449 {
450 	cdev->priv0 = priv;
451 }
452 
453 void
454 cuse_dev_set_priv1(struct cuse_dev *cdev, void *priv)
455 {
456 	cdev->priv1 = priv;
457 }
458 
459 int
460 cuse_wait_and_process(void)
461 {
462 	pthread_t curr = pthread_self();
463 	struct cuse_dev_entered *pe;
464 	struct cuse_dev_entered enter;
465 	struct cuse_command info;
466 	struct cuse_dev *cdev;
467 	int error;
468 
469 	if (f_cuse < 0)
470 		return (CUSE_ERR_INVALID);
471 
472 	error = ioctl(f_cuse, CUSE_IOCTL_GET_COMMAND, &info);
473 	if (error)
474 		return (CUSE_ERR_OTHER);
475 
476 	cdev = info.dev;
477 
478 	CUSE_LOCK();
479 	enter.thread = curr;
480 	enter.per_file_handle = (void *)info.per_file_handle;
481 	enter.cmd = info.command;
482 	enter.is_local = 0;
483 	enter.got_signal = 0;
484 	enter.cdev = cdev;
485 	TAILQ_INSERT_TAIL(&h_cuse_entered, &enter, entry);
486 	CUSE_UNLOCK();
487 
488 	DPRINTF("cuse: Command = %d = %s, flags = %d, arg = 0x%08x, ptr = 0x%08x\n",
489 	    (int)info.command, cuse_cmd_str(info.command), (int)info.fflags,
490 	    (int)info.argument, (int)info.data_pointer);
491 
492 	switch (info.command) {
493 	case CUSE_CMD_OPEN:
494 		if (cdev->mtod->cm_open != NULL)
495 			error = (cdev->mtod->cm_open) (cdev, (int)info.fflags);
496 		else
497 			error = 0;
498 		break;
499 
500 	case CUSE_CMD_CLOSE:
501 
502 		/* wait for other threads to stop */
503 
504 		while (1) {
505 
506 			error = 0;
507 
508 			CUSE_LOCK();
509 			TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
510 				if (pe->cdev != cdev)
511 					continue;
512 				if (pe->thread == curr)
513 					continue;
514 				if (pe->per_file_handle !=
515 				    enter.per_file_handle)
516 					continue;
517 				pe->got_signal = 1;
518 				pthread_kill(pe->thread, SIGHUP);
519 				error = CUSE_ERR_BUSY;
520 			}
521 			CUSE_UNLOCK();
522 
523 			if (error == 0)
524 				break;
525 			else
526 				usleep(10000);
527 		}
528 
529 		if (cdev->mtod->cm_close != NULL)
530 			error = (cdev->mtod->cm_close) (cdev, (int)info.fflags);
531 		else
532 			error = 0;
533 		break;
534 
535 	case CUSE_CMD_READ:
536 		if (cdev->mtod->cm_read != NULL) {
537 			error = (cdev->mtod->cm_read) (cdev, (int)info.fflags,
538 			    (void *)info.data_pointer, (int)info.argument);
539 		} else {
540 			error = CUSE_ERR_INVALID;
541 		}
542 		break;
543 
544 	case CUSE_CMD_WRITE:
545 		if (cdev->mtod->cm_write != NULL) {
546 			error = (cdev->mtod->cm_write) (cdev, (int)info.fflags,
547 			    (void *)info.data_pointer, (int)info.argument);
548 		} else {
549 			error = CUSE_ERR_INVALID;
550 		}
551 		break;
552 
553 	case CUSE_CMD_IOCTL:
554 		if (cdev->mtod->cm_ioctl != NULL) {
555 			error = (cdev->mtod->cm_ioctl) (cdev, (int)info.fflags,
556 			    (unsigned int)info.argument, (void *)info.data_pointer);
557 		} else {
558 			error = CUSE_ERR_INVALID;
559 		}
560 		break;
561 
562 	case CUSE_CMD_POLL:
563 		if (cdev->mtod->cm_poll != NULL) {
564 			error = (cdev->mtod->cm_poll) (cdev, (int)info.fflags,
565 			    (int)info.argument);
566 		} else {
567 			error = CUSE_POLL_ERROR;
568 		}
569 		break;
570 
571 	case CUSE_CMD_SIGNAL:
572 		CUSE_LOCK();
573 		TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
574 			if (pe->cdev != cdev)
575 				continue;
576 			if (pe->thread == curr)
577 				continue;
578 			if (pe->per_file_handle !=
579 			    enter.per_file_handle)
580 				continue;
581 			pe->got_signal = 1;
582 			pthread_kill(pe->thread, SIGHUP);
583 		}
584 		CUSE_UNLOCK();
585 		break;
586 
587 	default:
588 		error = CUSE_ERR_INVALID;
589 		break;
590 	}
591 
592 	DPRINTF("cuse: Command error = %d for %s\n",
593 	    error, cuse_cmd_str(info.command));
594 
595 	CUSE_LOCK();
596 	TAILQ_REMOVE(&h_cuse_entered, &enter, entry);
597 	CUSE_UNLOCK();
598 
599 	/* we ignore any sync command failures */
600 	ioctl(f_cuse, CUSE_IOCTL_SYNC_COMMAND, &error);
601 
602 	return (0);
603 }
604 
605 static struct cuse_dev_entered *
606 cuse_dev_get_entered(void)
607 {
608 	struct cuse_dev_entered *pe;
609 	pthread_t curr = pthread_self();
610 
611 	CUSE_LOCK();
612 	TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
613 		if (pe->thread == curr)
614 			break;
615 	}
616 	CUSE_UNLOCK();
617 	return (pe);
618 }
619 
620 void
621 cuse_dev_set_per_file_handle(struct cuse_dev *cdev, void *handle)
622 {
623 	struct cuse_dev_entered *pe;
624 
625 	pe = cuse_dev_get_entered();
626 	if (pe == NULL || pe->cdev != cdev)
627 		return;
628 
629 	pe->per_file_handle = handle;
630 	ioctl(f_cuse, CUSE_IOCTL_SET_PFH, &handle);
631 }
632 
633 void   *
634 cuse_dev_get_per_file_handle(struct cuse_dev *cdev)
635 {
636 	struct cuse_dev_entered *pe;
637 
638 	pe = cuse_dev_get_entered();
639 	if (pe == NULL || pe->cdev != cdev)
640 		return (NULL);
641 
642 	return (pe->per_file_handle);
643 }
644 
645 void
646 cuse_set_local(int val)
647 {
648 	struct cuse_dev_entered *pe;
649 
650 	pe = cuse_dev_get_entered();
651 	if (pe == NULL)
652 		return;
653 
654 	pe->is_local = val;
655 }
656 
657 #ifdef HAVE_DEBUG
658 static const char *
659 cuse_cmd_str(int cmd)
660 {
661 	static const char *str[CUSE_CMD_MAX] = {
662 		[CUSE_CMD_NONE] = "none",
663 		[CUSE_CMD_OPEN] = "open",
664 		[CUSE_CMD_CLOSE] = "close",
665 		[CUSE_CMD_READ] = "read",
666 		[CUSE_CMD_WRITE] = "write",
667 		[CUSE_CMD_IOCTL] = "ioctl",
668 		[CUSE_CMD_POLL] = "poll",
669 		[CUSE_CMD_SIGNAL] = "signal",
670 		[CUSE_CMD_SYNC] = "sync",
671 	};
672 
673 	if ((cmd >= 0) && (cmd < CUSE_CMD_MAX) &&
674 	    (str[cmd] != NULL))
675 		return (str[cmd]);
676 	else
677 		return ("unknown");
678 }
679 
680 #endif
681 
682 int
683 cuse_get_local(void)
684 {
685 	struct cuse_dev_entered *pe;
686 
687 	pe = cuse_dev_get_entered();
688 	if (pe == NULL)
689 		return (0);
690 
691 	return (pe->is_local);
692 }
693 
694 int
695 cuse_copy_out(const void *src, void *user_dst, int len)
696 {
697 	struct cuse_data_chunk info;
698 	struct cuse_dev_entered *pe;
699 	int error;
700 
701 	if ((f_cuse < 0) || (len < 0))
702 		return (CUSE_ERR_INVALID);
703 
704 	pe = cuse_dev_get_entered();
705 	if (pe == NULL)
706 		return (CUSE_ERR_INVALID);
707 
708 	DPRINTF("cuse: copy_out(%p,%p,%d), cmd = %d = %s\n",
709 	    src, user_dst, len, pe->cmd, cuse_cmd_str(pe->cmd));
710 
711 	if (pe->is_local) {
712 		memcpy(user_dst, src, len);
713 	} else {
714 		info.local_ptr = (uintptr_t)src;
715 		info.peer_ptr = (uintptr_t)user_dst;
716 		info.length = len;
717 
718 		error = ioctl(f_cuse, CUSE_IOCTL_WRITE_DATA, &info);
719 		if (error) {
720 			DPRINTF("cuse: copy_out() error = %d\n", errno);
721 			return (CUSE_ERR_FAULT);
722 		}
723 	}
724 	return (0);
725 }
726 
727 int
728 cuse_copy_in(const void *user_src, void *dst, int len)
729 {
730 	struct cuse_data_chunk info;
731 	struct cuse_dev_entered *pe;
732 	int error;
733 
734 	if ((f_cuse < 0) || (len < 0))
735 		return (CUSE_ERR_INVALID);
736 
737 	pe = cuse_dev_get_entered();
738 	if (pe == NULL)
739 		return (CUSE_ERR_INVALID);
740 
741 	DPRINTF("cuse: copy_in(%p,%p,%d), cmd = %d = %s\n",
742 	    user_src, dst, len, pe->cmd, cuse_cmd_str(pe->cmd));
743 
744 	if (pe->is_local) {
745 		memcpy(dst, user_src, len);
746 	} else {
747 		info.local_ptr = (uintptr_t)dst;
748 		info.peer_ptr = (uintptr_t)user_src;
749 		info.length = len;
750 
751 		error = ioctl(f_cuse, CUSE_IOCTL_READ_DATA, &info);
752 		if (error) {
753 			DPRINTF("cuse: copy_in() error = %d\n", errno);
754 			return (CUSE_ERR_FAULT);
755 		}
756 	}
757 	return (0);
758 }
759 
760 struct cuse_dev *
761 cuse_dev_get_current(int *pcmd)
762 {
763 	struct cuse_dev_entered *pe;
764 
765 	pe = cuse_dev_get_entered();
766 	if (pe == NULL) {
767 		if (pcmd != NULL)
768 			*pcmd = 0;
769 		return (NULL);
770 	}
771 	if (pcmd != NULL)
772 		*pcmd = pe->cmd;
773 	return (pe->cdev);
774 }
775 
776 int
777 cuse_got_peer_signal(void)
778 {
779 	struct cuse_dev_entered *pe;
780 
781 	pe = cuse_dev_get_entered();
782 	if (pe == NULL)
783 		return (CUSE_ERR_INVALID);
784 
785 	if (pe->got_signal)
786 		return (0);
787 
788 	return (CUSE_ERR_OTHER);
789 }
790 
791 void
792 cuse_poll_wakeup(void)
793 {
794 	int error = 0;
795 
796 	if (f_cuse < 0)
797 		return;
798 
799 	ioctl(f_cuse, CUSE_IOCTL_SELWAKEUP, &error);
800 }
801