xref: /freebsd/lib/libcuse/cuse_lib.c (revision a0b956f5ac5e0941f9e74e24c1c53e05ad061a38)
1 /* $FreeBSD$ */
2 /*-
3  * Copyright (c) 2010-2022 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 	unsigned long 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 %
162 			    (unsigned long)getpagesize();
163 
164 			return ((n * CUSE_ALLOC_BYTES_MAX) + remainder);
165 		}
166 	}
167 	CUSE_UNLOCK();
168 
169 	return (0x80000000UL);		/* failure */
170 }
171 
172 void   *
173 cuse_vmalloc(int size)
174 {
175 	struct cuse_alloc_info info;
176 	unsigned long pgsize;
177 	unsigned long n;
178 	void *ptr;
179 	int error;
180 
181 	if (f_cuse < 0)
182 		return (NULL);
183 
184 	memset(&info, 0, sizeof(info));
185 
186 	if (size < 1)
187 		return (NULL);
188 
189 	pgsize = getpagesize();
190 	info.page_count = howmany(size, pgsize);
191 
192 	CUSE_LOCK();
193 	for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
194 
195 		if (a_cuse[n].ptr != NULL)
196 			continue;
197 
198 		a_cuse[n].ptr = ((uint8_t *)1);	/* reserve */
199 		a_cuse[n].size = 0;
200 
201 		CUSE_UNLOCK();
202 
203 		info.alloc_nr = n;
204 
205 		error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_MEMORY, &info);
206 
207 		if (error) {
208 
209 			CUSE_LOCK();
210 
211 			a_cuse[n].ptr = NULL;
212 
213 			if (errno == EBUSY)
214 				continue;
215 			else
216 				break;
217 		}
218 		ptr = mmap(NULL, info.page_count * pgsize,
219 		    PROT_READ | PROT_WRITE,
220 		    MAP_SHARED, f_cuse, CUSE_ALLOC_BYTES_MAX * n);
221 
222 		if (ptr == MAP_FAILED) {
223 
224 			error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
225 
226 			if (error) {
227 				/* ignore */
228 			}
229 			CUSE_LOCK();
230 
231 			a_cuse[n].ptr = NULL;
232 
233 			break;
234 		}
235 		CUSE_LOCK();
236 		a_cuse[n].ptr = ptr;
237 		a_cuse[n].size = size;
238 		CUSE_UNLOCK();
239 
240 		return (ptr);		/* success */
241 	}
242 	CUSE_UNLOCK();
243 	return (NULL);			/* failure */
244 }
245 
246 int
247 cuse_is_vmalloc_addr(void *ptr)
248 {
249 	int n;
250 
251 	if (f_cuse < 0 || ptr == NULL)
252 		return (0);		/* false */
253 
254 	CUSE_LOCK();
255 	for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
256 		if (a_cuse[n].ptr == ptr)
257 			break;
258 	}
259 	CUSE_UNLOCK();
260 
261 	return (n != CUSE_ALLOC_UNIT_MAX);
262 }
263 
264 void
265 cuse_vmfree(void *ptr)
266 {
267 	struct cuse_vm_allocation temp;
268 	struct cuse_alloc_info info;
269 	int error;
270 	int n;
271 
272 	if (f_cuse < 0)
273 		return;
274 
275 	CUSE_LOCK();
276 	for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
277 		if (a_cuse[n].ptr != ptr)
278 			continue;
279 
280 		temp = a_cuse[n];
281 
282 		CUSE_UNLOCK();
283 
284 		munmap(temp.ptr, temp.size);
285 
286 		memset(&info, 0, sizeof(info));
287 
288 		info.alloc_nr = n;
289 
290 		error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
291 
292 		if (error != 0) {
293 			/* ignore any errors */
294 			DPRINTF("Freeing memory failed: %d\n", errno);
295 		}
296 		CUSE_LOCK();
297 
298 		a_cuse[n].ptr = NULL;
299 		a_cuse[n].size = 0;
300 
301 		break;
302 	}
303 	CUSE_UNLOCK();
304 }
305 
306 int
307 cuse_alloc_unit_number_by_id(int *pnum, int id)
308 {
309 	int error;
310 
311 	if (f_cuse < 0)
312 		return (CUSE_ERR_INVALID);
313 
314 	*pnum = (id & CUSE_ID_MASK);
315 
316 	error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT_BY_ID, pnum);
317 	if (error)
318 		return (CUSE_ERR_NO_MEMORY);
319 
320 	return (0);
321 
322 }
323 
324 int
325 cuse_free_unit_number_by_id(int num, int id)
326 {
327 	int error;
328 
329 	if (f_cuse < 0)
330 		return (CUSE_ERR_INVALID);
331 
332 	if (num != -1 || id != -1)
333 		num = (id & CUSE_ID_MASK) | (num & 0xFF);
334 
335 	error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT_BY_ID, &num);
336 	if (error)
337 		return (CUSE_ERR_NO_MEMORY);
338 
339 	return (0);
340 }
341 
342 int
343 cuse_alloc_unit_number(int *pnum)
344 {
345 	int error;
346 
347 	if (f_cuse < 0)
348 		return (CUSE_ERR_INVALID);
349 
350 	error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT, pnum);
351 	if (error)
352 		return (CUSE_ERR_NO_MEMORY);
353 
354 	return (0);
355 }
356 
357 int
358 cuse_free_unit_number(int num)
359 {
360 	int error;
361 
362 	if (f_cuse < 0)
363 		return (CUSE_ERR_INVALID);
364 
365 	error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT, &num);
366 	if (error)
367 		return (CUSE_ERR_NO_MEMORY);
368 
369 	return (0);
370 }
371 
372 struct cuse_dev *
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,...)
375 {
376 	struct cuse_create_dev info;
377 	struct cuse_dev *cdev;
378 	va_list args;
379 	int error;
380 
381 	if (f_cuse < 0)
382 		return (NULL);
383 
384 	cdev = malloc(sizeof(*cdev));
385 	if (cdev == NULL)
386 		return (NULL);
387 
388 	memset(cdev, 0, sizeof(*cdev));
389 
390 	cdev->mtod = mtod;
391 	cdev->priv0 = priv0;
392 	cdev->priv1 = priv1;
393 
394 	memset(&info, 0, sizeof(info));
395 
396 	info.dev = cdev;
397 	info.user_id = _uid;
398 	info.group_id = _gid;
399 	info.permissions = _perms;
400 
401 	va_start(args, _fmt);
402 	vsnprintf(info.devname, sizeof(info.devname), _fmt, args);
403 	va_end(args);
404 
405 	error = ioctl(f_cuse, CUSE_IOCTL_CREATE_DEV, &info);
406 	if (error) {
407 		free(cdev);
408 		return (NULL);
409 	}
410 	CUSE_LOCK();
411 	TAILQ_INSERT_TAIL(&h_cuse, cdev, entry);
412 	CUSE_UNLOCK();
413 
414 	return (cdev);
415 }
416 
417 
418 void
419 cuse_dev_destroy(struct cuse_dev *cdev)
420 {
421 	int error;
422 
423 	if (f_cuse < 0)
424 		return;
425 
426 	CUSE_LOCK();
427 	TAILQ_REMOVE(&h_cuse, cdev, entry);
428 	CUSE_UNLOCK();
429 
430 	error = ioctl(f_cuse, CUSE_IOCTL_DESTROY_DEV, &cdev);
431 	if (error)
432 		return;
433 
434 	free(cdev);
435 }
436 
437 void   *
438 cuse_dev_get_priv0(struct cuse_dev *cdev)
439 {
440 	return (cdev->priv0);
441 }
442 
443 void   *
444 cuse_dev_get_priv1(struct cuse_dev *cdev)
445 {
446 	return (cdev->priv1);
447 }
448 
449 void
450 cuse_dev_set_priv0(struct cuse_dev *cdev, void *priv)
451 {
452 	cdev->priv0 = priv;
453 }
454 
455 void
456 cuse_dev_set_priv1(struct cuse_dev *cdev, void *priv)
457 {
458 	cdev->priv1 = priv;
459 }
460 
461 int
462 cuse_wait_and_process(void)
463 {
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;
469 	int error;
470 
471 	if (f_cuse < 0)
472 		return (CUSE_ERR_INVALID);
473 
474 	error = ioctl(f_cuse, CUSE_IOCTL_GET_COMMAND, &info);
475 	if (error)
476 		return (CUSE_ERR_OTHER);
477 
478 	cdev = info.dev;
479 
480 	CUSE_LOCK();
481 	enter.thread = curr;
482 	enter.per_file_handle = (void *)info.per_file_handle;
483 	enter.cmd = info.command;
484 	enter.is_local = 0;
485 	enter.got_signal = 0;
486 	enter.cdev = cdev;
487 	TAILQ_INSERT_TAIL(&h_cuse_entered, &enter, entry);
488 	CUSE_UNLOCK();
489 
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);
493 
494 	switch (info.command) {
495 	case CUSE_CMD_OPEN:
496 		if (cdev->mtod->cm_open != NULL)
497 			error = (cdev->mtod->cm_open) (cdev, (int)info.fflags);
498 		else
499 			error = 0;
500 		break;
501 
502 	case CUSE_CMD_CLOSE:
503 
504 		/* wait for other threads to stop */
505 
506 		while (1) {
507 
508 			error = 0;
509 
510 			CUSE_LOCK();
511 			TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
512 				if (pe->cdev != cdev)
513 					continue;
514 				if (pe->thread == curr)
515 					continue;
516 				if (pe->per_file_handle !=
517 				    enter.per_file_handle)
518 					continue;
519 				pe->got_signal = 1;
520 				pthread_kill(pe->thread, SIGHUP);
521 				error = CUSE_ERR_BUSY;
522 			}
523 			CUSE_UNLOCK();
524 
525 			if (error == 0)
526 				break;
527 			else
528 				usleep(10000);
529 		}
530 
531 		if (cdev->mtod->cm_close != NULL)
532 			error = (cdev->mtod->cm_close) (cdev, (int)info.fflags);
533 		else
534 			error = 0;
535 		break;
536 
537 	case CUSE_CMD_READ:
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);
541 		} else {
542 			error = CUSE_ERR_INVALID;
543 		}
544 		break;
545 
546 	case CUSE_CMD_WRITE:
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);
550 		} else {
551 			error = CUSE_ERR_INVALID;
552 		}
553 		break;
554 
555 	case CUSE_CMD_IOCTL:
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);
559 		} else {
560 			error = CUSE_ERR_INVALID;
561 		}
562 		break;
563 
564 	case CUSE_CMD_POLL:
565 		if (cdev->mtod->cm_poll != NULL) {
566 			error = (cdev->mtod->cm_poll) (cdev, (int)info.fflags,
567 			    (int)info.argument);
568 		} else {
569 			error = CUSE_POLL_ERROR;
570 		}
571 		break;
572 
573 	case CUSE_CMD_SIGNAL:
574 		CUSE_LOCK();
575 		TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
576 			if (pe->cdev != cdev)
577 				continue;
578 			if (pe->thread == curr)
579 				continue;
580 			if (pe->per_file_handle !=
581 			    enter.per_file_handle)
582 				continue;
583 			pe->got_signal = 1;
584 			pthread_kill(pe->thread, SIGHUP);
585 		}
586 		CUSE_UNLOCK();
587 		break;
588 
589 	default:
590 		error = CUSE_ERR_INVALID;
591 		break;
592 	}
593 
594 	DPRINTF("cuse: Command error = %d for %s\n",
595 	    error, cuse_cmd_str(info.command));
596 
597 	CUSE_LOCK();
598 	TAILQ_REMOVE(&h_cuse_entered, &enter, entry);
599 	CUSE_UNLOCK();
600 
601 	/* we ignore any sync command failures */
602 	ioctl(f_cuse, CUSE_IOCTL_SYNC_COMMAND, &error);
603 
604 	return (0);
605 }
606 
607 static struct cuse_dev_entered *
608 cuse_dev_get_entered(void)
609 {
610 	struct cuse_dev_entered *pe;
611 	pthread_t curr = pthread_self();
612 
613 	CUSE_LOCK();
614 	TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
615 		if (pe->thread == curr)
616 			break;
617 	}
618 	CUSE_UNLOCK();
619 	return (pe);
620 }
621 
622 void
623 cuse_dev_set_per_file_handle(struct cuse_dev *cdev, void *handle)
624 {
625 	struct cuse_dev_entered *pe;
626 
627 	pe = cuse_dev_get_entered();
628 	if (pe == NULL || pe->cdev != cdev)
629 		return;
630 
631 	pe->per_file_handle = handle;
632 	ioctl(f_cuse, CUSE_IOCTL_SET_PFH, &handle);
633 }
634 
635 void   *
636 cuse_dev_get_per_file_handle(struct cuse_dev *cdev)
637 {
638 	struct cuse_dev_entered *pe;
639 
640 	pe = cuse_dev_get_entered();
641 	if (pe == NULL || pe->cdev != cdev)
642 		return (NULL);
643 
644 	return (pe->per_file_handle);
645 }
646 
647 void
648 cuse_set_local(int val)
649 {
650 	struct cuse_dev_entered *pe;
651 
652 	pe = cuse_dev_get_entered();
653 	if (pe == NULL)
654 		return;
655 
656 	pe->is_local = val;
657 }
658 
659 #ifdef HAVE_DEBUG
660 static const char *
661 cuse_cmd_str(int cmd)
662 {
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",
673 	};
674 
675 	if ((cmd >= 0) && (cmd < CUSE_CMD_MAX) &&
676 	    (str[cmd] != NULL))
677 		return (str[cmd]);
678 	else
679 		return ("unknown");
680 }
681 
682 #endif
683 
684 int
685 cuse_get_local(void)
686 {
687 	struct cuse_dev_entered *pe;
688 
689 	pe = cuse_dev_get_entered();
690 	if (pe == NULL)
691 		return (0);
692 
693 	return (pe->is_local);
694 }
695 
696 int
697 cuse_copy_out(const void *src, void *user_dst, int len)
698 {
699 	struct cuse_data_chunk info;
700 	struct cuse_dev_entered *pe;
701 	int error;
702 
703 	if ((f_cuse < 0) || (len < 0))
704 		return (CUSE_ERR_INVALID);
705 
706 	pe = cuse_dev_get_entered();
707 	if (pe == NULL)
708 		return (CUSE_ERR_INVALID);
709 
710 	DPRINTF("cuse: copy_out(%p,%p,%d), cmd = %d = %s\n",
711 	    src, user_dst, len, pe->cmd, cuse_cmd_str(pe->cmd));
712 
713 	if (pe->is_local) {
714 		memcpy(user_dst, src, len);
715 	} else {
716 		info.local_ptr = (uintptr_t)src;
717 		info.peer_ptr = (uintptr_t)user_dst;
718 		info.length = len;
719 
720 		error = ioctl(f_cuse, CUSE_IOCTL_WRITE_DATA, &info);
721 		if (error) {
722 			DPRINTF("cuse: copy_out() error = %d\n", errno);
723 			return (CUSE_ERR_FAULT);
724 		}
725 	}
726 	return (0);
727 }
728 
729 int
730 cuse_copy_in(const void *user_src, void *dst, int len)
731 {
732 	struct cuse_data_chunk info;
733 	struct cuse_dev_entered *pe;
734 	int error;
735 
736 	if ((f_cuse < 0) || (len < 0))
737 		return (CUSE_ERR_INVALID);
738 
739 	pe = cuse_dev_get_entered();
740 	if (pe == NULL)
741 		return (CUSE_ERR_INVALID);
742 
743 	DPRINTF("cuse: copy_in(%p,%p,%d), cmd = %d = %s\n",
744 	    user_src, dst, len, pe->cmd, cuse_cmd_str(pe->cmd));
745 
746 	if (pe->is_local) {
747 		memcpy(dst, user_src, len);
748 	} else {
749 		info.local_ptr = (uintptr_t)dst;
750 		info.peer_ptr = (uintptr_t)user_src;
751 		info.length = len;
752 
753 		error = ioctl(f_cuse, CUSE_IOCTL_READ_DATA, &info);
754 		if (error) {
755 			DPRINTF("cuse: copy_in() error = %d\n", errno);
756 			return (CUSE_ERR_FAULT);
757 		}
758 	}
759 	return (0);
760 }
761 
762 struct cuse_dev *
763 cuse_dev_get_current(int *pcmd)
764 {
765 	struct cuse_dev_entered *pe;
766 
767 	pe = cuse_dev_get_entered();
768 	if (pe == NULL) {
769 		if (pcmd != NULL)
770 			*pcmd = 0;
771 		return (NULL);
772 	}
773 	if (pcmd != NULL)
774 		*pcmd = pe->cmd;
775 	return (pe->cdev);
776 }
777 
778 int
779 cuse_got_peer_signal(void)
780 {
781 	struct cuse_dev_entered *pe;
782 
783 	pe = cuse_dev_get_entered();
784 	if (pe == NULL)
785 		return (CUSE_ERR_INVALID);
786 
787 	if (pe->got_signal)
788 		return (0);
789 
790 	return (CUSE_ERR_OTHER);
791 }
792 
793 void
794 cuse_poll_wakeup(void)
795 {
796 	int error = 0;
797 
798 	if (f_cuse < 0)
799 		return;
800 
801 	ioctl(f_cuse, CUSE_IOCTL_SELWAKEUP, &error);
802 }
803