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