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