]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libcuse/cuse_lib.c
MFV r272802:
[FreeBSD/FreeBSD.git] / lib / libcuse / cuse_lib.c
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 static void
91 cuse_lock(void) __locks_exclusive(m_cuse)
92 {
93         pthread_mutex_lock(&m_cuse);
94 }
95
96 static void
97 cuse_unlock(void) __unlocks(m_cuse)
98 {
99         pthread_mutex_unlock(&m_cuse);
100 }
101
102 int
103 cuse_init(void)
104 {
105         pthread_mutexattr_t attr;
106
107         f_cuse = open("/dev/cuse", O_RDWR);
108         if (f_cuse < 0) {
109                 if (feature_present("cuse") == 0)
110                         return (CUSE_ERR_NOT_LOADED);
111                 else
112                         return (CUSE_ERR_INVALID);
113         }
114         pthread_mutexattr_init(&attr);
115         pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
116         pthread_mutex_init(&m_cuse, &attr);
117
118         TAILQ_INIT(&h_cuse);
119         TAILQ_INIT(&h_cuse_entered);
120
121         return (0);
122 }
123
124 int
125 cuse_uninit(void)
126 {
127         int f;
128
129         if (f_cuse < 0)
130                 return (CUSE_ERR_INVALID);
131
132         f = f_cuse;
133         f_cuse = -1;
134
135         close(f);
136
137         pthread_mutex_destroy(&m_cuse);
138
139         memset(a_cuse, 0, sizeof(a_cuse));
140
141         return (0);
142 }
143
144 unsigned long
145 cuse_vmoffset(void *_ptr)
146 {
147         uint8_t *ptr_min;
148         uint8_t *ptr_max;
149         uint8_t *ptr = _ptr;
150         unsigned long remainder;
151         int n;
152
153         cuse_lock();
154         for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
155                 if (a_cuse[n].ptr == NULL)
156                         continue;
157
158                 ptr_min = a_cuse[n].ptr;
159                 ptr_max = a_cuse[n].ptr + a_cuse[n].size - 1;
160
161                 if ((ptr >= ptr_min) && (ptr <= ptr_max)) {
162
163                         cuse_unlock();
164
165                         remainder = (ptr - ptr_min);
166
167                         remainder -= remainder % PAGE_SIZE;
168
169                         return ((n * PAGE_SIZE * CUSE_ALLOC_PAGES_MAX) + remainder);
170                 }
171         }
172         cuse_unlock();
173
174         return (0x80000000UL);          /* failure */
175 }
176
177 void   *
178 cuse_vmalloc(int size)
179 {
180         struct cuse_alloc_info info;
181         void *ptr;
182         int error;
183         int n;
184
185         if (f_cuse < 0)
186                 return (NULL);
187
188         memset(&info, 0, sizeof(info));
189
190         if (size < 1)
191                 return (NULL);
192
193         info.page_count = (size + PAGE_SIZE - 1) / PAGE_SIZE;
194
195         cuse_lock();
196         for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
197
198                 if (a_cuse[n].ptr != NULL)
199                         continue;
200
201                 a_cuse[n].ptr = ((uint8_t *)1); /* reserve */
202                 a_cuse[n].size = 0;
203
204                 cuse_unlock();
205
206                 info.alloc_nr = n;
207
208                 error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_MEMORY, &info);
209
210                 if (error) {
211
212                         cuse_lock();
213
214                         a_cuse[n].ptr = NULL;
215
216                         if (errno == EBUSY)
217                                 continue;
218                         else
219                                 break;
220                 }
221                 ptr = mmap(NULL, info.page_count * PAGE_SIZE,
222                     PROT_READ | PROT_WRITE,
223                     MAP_SHARED, f_cuse, CUSE_ALLOC_PAGES_MAX *
224                     PAGE_SIZE * n);
225
226                 if (ptr == MAP_FAILED) {
227
228                         error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
229
230                         if (error) {
231                                 /* ignore */
232                         }
233                         cuse_lock();
234
235                         a_cuse[n].ptr = NULL;
236
237                         break;
238                 }
239                 cuse_lock();
240                 a_cuse[n].ptr = ptr;
241                 a_cuse[n].size = size;
242                 cuse_unlock();
243
244                 return (ptr);           /* success */
245         }
246         cuse_unlock();
247         return (NULL);                  /* failure */
248 }
249
250 int
251 cuse_is_vmalloc_addr(void *ptr)
252 {
253         int n;
254
255         if (f_cuse < 0 || ptr == NULL)
256                 return (0);             /* false */
257
258         cuse_lock();
259         for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
260                 if (a_cuse[n].ptr == ptr)
261                         break;
262         }
263         cuse_unlock();
264
265         return (n != CUSE_ALLOC_UNIT_MAX);
266 }
267
268 void
269 cuse_vmfree(void *ptr)
270 {
271         struct cuse_alloc_info info;
272         int error;
273         int n;
274
275         if (f_cuse < 0)
276                 return;
277
278         memset(&info, 0, sizeof(info));
279
280         cuse_lock();
281         for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
282                 if (a_cuse[n].ptr != ptr)
283                         continue;
284
285                 cuse_unlock();
286
287                 info.alloc_nr = n;
288
289                 munmap(ptr, a_cuse[n].size);
290
291                 error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
292
293                 if (error) {
294                         /* ignore */
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 = (unsigned long)src;
717                 info.peer_ptr = (unsigned long)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 = (unsigned long)dst;
750                 info.peer_ptr = (unsigned long)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 }