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