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 46 int cuse_debug_level; 47 48 #ifdef HAVE_DEBUG 49 static 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 59 struct cuse_vm_allocation { 60 uint8_t *ptr; 61 uint32_t size; 62 }; 63 64 struct 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 74 struct cuse_dev { 75 TAILQ_ENTRY(cuse_dev) entry; 76 const struct cuse_methods *mtod; 77 void *priv0; 78 void *priv1; 79 }; 80 81 static int f_cuse = -1; 82 83 static pthread_mutex_t m_cuse; 84 static TAILQ_HEAD(, cuse_dev) h_cuse __guarded_by(m_cuse); 85 static TAILQ_HEAD(, cuse_dev_entered) h_cuse_entered __guarded_by(m_cuse); 86 static 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 95 int 96 cuse_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 117 int 118 cuse_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 137 unsigned long 138 cuse_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 164 void * 165 cuse_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 238 int 239 cuse_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 256 void 257 cuse_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 298 int 299 cuse_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 316 int 317 cuse_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 334 int 335 cuse_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 349 int 350 cuse_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 364 struct cuse_dev * 365 cuse_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 410 void 411 cuse_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 429 void * 430 cuse_dev_get_priv0(struct cuse_dev *cdev) 431 { 432 return (cdev->priv0); 433 } 434 435 void * 436 cuse_dev_get_priv1(struct cuse_dev *cdev) 437 { 438 return (cdev->priv1); 439 } 440 441 void 442 cuse_dev_set_priv0(struct cuse_dev *cdev, void *priv) 443 { 444 cdev->priv0 = priv; 445 } 446 447 void 448 cuse_dev_set_priv1(struct cuse_dev *cdev, void *priv) 449 { 450 cdev->priv1 = priv; 451 } 452 453 int 454 cuse_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 599 static struct cuse_dev_entered * 600 cuse_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 614 void 615 cuse_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 627 void * 628 cuse_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 639 void 640 cuse_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 652 static const char * 653 cuse_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 676 int 677 cuse_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 688 int 689 cuse_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 721 int 722 cuse_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 754 struct cuse_dev * 755 cuse_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 770 int 771 cuse_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 785 void 786 cuse_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