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