1 /* 2 * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 //! Operations on file descriptors 7 8 #include "fd.h" 9 10 #include <stdlib.h> 11 12 #include "fssh_atomic.h" 13 #include "fssh_fcntl.h" 14 #include "fssh_kernel_export.h" 15 #include "fssh_kernel_priv.h" 16 #include "fssh_string.h" 17 #include "fssh_uio.h" 18 #include "syscalls.h" 19 20 21 //#define TRACE_FD 22 #ifdef TRACE_FD 23 # define TRACE(x) dprintf x 24 #else 25 # define TRACE(x) 26 #endif 27 28 29 namespace FSShell { 30 31 32 io_context* gKernelIOContext; 33 34 35 /*** General fd routines ***/ 36 37 38 #ifdef DEBUG 39 void dump_fd(int fd, struct file_descriptor *descriptor); 40 41 void 42 dump_fd(int fd,struct file_descriptor *descriptor) 43 { 44 fssh_dprintf("fd[%d] = %p: type = %d, ref_count = %d, ops = %p, u.vnode = %p, u.mount = %p, cookie = %p, open_mode = %x, pos = %Ld\n", 45 fd, descriptor, (int)descriptor->type, (int)descriptor->ref_count, descriptor->ops, 46 descriptor->u.vnode, descriptor->u.mount, descriptor->cookie, (int)descriptor->open_mode, descriptor->pos); 47 } 48 #endif 49 50 51 /** Allocates and initializes a new file_descriptor */ 52 53 struct file_descriptor * 54 alloc_fd(void) 55 { 56 struct file_descriptor *descriptor; 57 58 descriptor = (file_descriptor*)malloc(sizeof(struct file_descriptor)); 59 if (descriptor == NULL) 60 return NULL; 61 62 descriptor->u.vnode = NULL; 63 descriptor->cookie = NULL; 64 descriptor->ref_count = 1; 65 descriptor->open_count = 0; 66 descriptor->open_mode = 0; 67 descriptor->pos = 0; 68 69 return descriptor; 70 } 71 72 73 bool 74 fd_close_on_exec(struct io_context *context, int fd) 75 { 76 return CHECK_BIT(context->fds_close_on_exec[fd / 8], fd & 7) ? true : false; 77 } 78 79 80 void 81 fd_set_close_on_exec(struct io_context *context, int fd, bool closeFD) 82 { 83 if (closeFD) 84 context->fds_close_on_exec[fd / 8] |= (1 << (fd & 7)); 85 else 86 context->fds_close_on_exec[fd / 8] &= ~(1 << (fd & 7)); 87 } 88 89 90 /** Searches a free slot in the FD table of the provided I/O context, and inserts 91 * the specified descriptor into it. 92 */ 93 94 int 95 new_fd_etc(struct io_context *context, struct file_descriptor *descriptor, 96 int firstIndex) 97 { 98 int fd = -1; 99 uint32_t i; 100 101 fssh_mutex_lock(&context->io_mutex); 102 103 for (i = firstIndex; i < context->table_size; i++) { 104 if (!context->fds[i]) { 105 fd = i; 106 break; 107 } 108 } 109 if (fd < 0) { 110 fd = FSSH_B_NO_MORE_FDS; 111 goto err; 112 } 113 114 context->fds[fd] = descriptor; 115 context->num_used_fds++; 116 fssh_atomic_add(&descriptor->open_count, 1); 117 118 err: 119 fssh_mutex_unlock(&context->io_mutex); 120 121 return fd; 122 } 123 124 125 int 126 new_fd(struct io_context *context, struct file_descriptor *descriptor) 127 { 128 return new_fd_etc(context, descriptor, 0); 129 } 130 131 132 /** Reduces the descriptor's reference counter, and frees all resources 133 * when it's no longer used. 134 */ 135 136 void 137 put_fd(struct file_descriptor *descriptor) 138 { 139 int32_t previous = fssh_atomic_add(&descriptor->ref_count, -1); 140 141 TRACE(("put_fd(descriptor = %p [ref = %ld, cookie = %p])\n", 142 descriptor, descriptor->ref_count, descriptor->cookie)); 143 144 // free the descriptor if we don't need it anymore 145 if (previous == 1) { 146 // free the underlying object 147 if (descriptor->ops != NULL && descriptor->ops->fd_free != NULL) 148 descriptor->ops->fd_free(descriptor); 149 150 free(descriptor); 151 } else if ((descriptor->open_mode & FSSH_O_DISCONNECTED) != 0 152 && previous - 1 == descriptor->open_count 153 && descriptor->ops != NULL) { 154 // the descriptor has been disconnected - it cannot 155 // be accessed anymore, let's close it (no one is 156 // currently accessing this descriptor) 157 158 if (descriptor->ops->fd_close) 159 descriptor->ops->fd_close(descriptor); 160 if (descriptor->ops->fd_free) 161 descriptor->ops->fd_free(descriptor); 162 163 // prevent this descriptor from being closed/freed again 164 descriptor->open_count = -1; 165 descriptor->ref_count = -1; 166 descriptor->ops = NULL; 167 descriptor->u.vnode = NULL; 168 169 // the file descriptor is kept intact, so that it's not 170 // reused until someone explicetly closes it 171 } 172 } 173 174 175 /** Decrements the open counter of the file descriptor and invokes 176 * its close hook when appropriate. 177 */ 178 179 void 180 close_fd(struct file_descriptor *descriptor) 181 { 182 if (fssh_atomic_add(&descriptor->open_count, -1) == 1) { 183 vfs_unlock_vnode_if_locked(descriptor); 184 185 if (descriptor->ops != NULL && descriptor->ops->fd_close != NULL) 186 descriptor->ops->fd_close(descriptor); 187 } 188 } 189 190 191 /** This descriptor's underlying object will be closed and freed 192 * as soon as possible (in one of the next calls to put_fd() - 193 * get_fd() will no longer succeed on this descriptor). 194 * This is useful if the underlying object is gone, for instance 195 * when a (mounted) volume got removed unexpectedly. 196 */ 197 198 void 199 disconnect_fd(struct file_descriptor *descriptor) 200 { 201 descriptor->open_mode |= FSSH_O_DISCONNECTED; 202 } 203 204 205 void 206 inc_fd_ref_count(struct file_descriptor *descriptor) 207 { 208 fssh_atomic_add(&descriptor->ref_count, 1); 209 } 210 211 212 struct file_descriptor * 213 get_fd(struct io_context *context, int fd) 214 { 215 struct file_descriptor *descriptor = NULL; 216 217 if (fd < 0) 218 return NULL; 219 220 fssh_mutex_lock(&context->io_mutex); 221 222 if ((uint32_t)fd < context->table_size) 223 descriptor = context->fds[fd]; 224 225 if (descriptor != NULL) { 226 // Disconnected descriptors cannot be accessed anymore 227 if (descriptor->open_mode & FSSH_O_DISCONNECTED) 228 descriptor = NULL; 229 else 230 inc_fd_ref_count(descriptor); 231 } 232 233 fssh_mutex_unlock(&context->io_mutex); 234 235 return descriptor; 236 } 237 238 239 /** Removes the file descriptor from the specified slot. 240 */ 241 242 static struct file_descriptor * 243 remove_fd(struct io_context *context, int fd) 244 { 245 struct file_descriptor *descriptor = NULL; 246 247 if (fd < 0) 248 return NULL; 249 250 fssh_mutex_lock(&context->io_mutex); 251 252 if ((uint32_t)fd < context->table_size) 253 descriptor = context->fds[fd]; 254 255 if (descriptor) { 256 // fd is valid 257 context->fds[fd] = NULL; 258 fd_set_close_on_exec(context, fd, false); 259 context->num_used_fds--; 260 261 if (descriptor->open_mode & FSSH_O_DISCONNECTED) 262 descriptor = NULL; 263 } 264 265 fssh_mutex_unlock(&context->io_mutex); 266 267 return descriptor; 268 } 269 270 271 static int 272 dup_fd(int fd, bool kernel) 273 { 274 struct io_context *context = get_current_io_context(kernel); 275 struct file_descriptor *descriptor; 276 int status; 277 278 TRACE(("dup_fd: fd = %d\n", fd)); 279 280 // Try to get the fd structure 281 descriptor = get_fd(context, fd); 282 if (descriptor == NULL) 283 return FSSH_B_FILE_ERROR; 284 285 // now put the fd in place 286 status = new_fd(context, descriptor); 287 if (status < 0) 288 put_fd(descriptor); 289 else { 290 fssh_mutex_lock(&context->io_mutex); 291 fd_set_close_on_exec(context, status, false); 292 fssh_mutex_unlock(&context->io_mutex); 293 } 294 295 return status; 296 } 297 298 299 /** POSIX says this should be the same as: 300 * close(newfd); 301 * fcntl(oldfd, F_DUPFD, newfd); 302 * 303 * We do dup2() directly to be thread-safe. 304 */ 305 306 static int 307 dup2_fd(int oldfd, int newfd, bool kernel) 308 { 309 struct file_descriptor *evicted = NULL; 310 struct io_context *context; 311 312 TRACE(("dup2_fd: ofd = %d, nfd = %d\n", oldfd, newfd)); 313 314 // quick check 315 if (oldfd < 0 || newfd < 0) 316 return FSSH_B_FILE_ERROR; 317 318 // Get current I/O context and lock it 319 context = get_current_io_context(kernel); 320 fssh_mutex_lock(&context->io_mutex); 321 322 // Check if the fds are valid (mutex must be locked because 323 // the table size could be changed) 324 if ((uint32_t)oldfd >= context->table_size 325 || (uint32_t)newfd >= context->table_size 326 || context->fds[oldfd] == NULL) { 327 fssh_mutex_unlock(&context->io_mutex); 328 return FSSH_B_FILE_ERROR; 329 } 330 331 // Check for identity, note that it cannot be made above 332 // because we always want to return an error on invalid 333 // handles 334 if (oldfd != newfd) { 335 // Now do the work 336 evicted = context->fds[newfd]; 337 fssh_atomic_add(&context->fds[oldfd]->ref_count, 1); 338 fssh_atomic_add(&context->fds[oldfd]->open_count, 1); 339 context->fds[newfd] = context->fds[oldfd]; 340 341 if (evicted == NULL) 342 context->num_used_fds++; 343 } 344 345 fd_set_close_on_exec(context, newfd, false); 346 347 fssh_mutex_unlock(&context->io_mutex); 348 349 // Say bye bye to the evicted fd 350 if (evicted) { 351 close_fd(evicted); 352 put_fd(evicted); 353 } 354 355 return newfd; 356 } 357 358 359 fssh_status_t 360 select_fd(int fd, uint8_t event, uint32_t ref, struct select_sync *sync, bool kernel) 361 { 362 // struct file_descriptor *descriptor; 363 // fssh_status_t status; 364 // 365 // TRACE(("select_fd(fd = %d, event = %u, ref = %lu, selectsync = %p)\n", fd, event, ref, sync)); 366 // 367 // descriptor = get_fd(get_current_io_context(kernel), fd); 368 // if (descriptor == NULL) 369 // return FSSH_B_FILE_ERROR; 370 // 371 // if (descriptor->ops->fd_select) { 372 // status = descriptor->ops->fd_select(descriptor, event, ref, sync); 373 // } else { 374 // // if the I/O subsystem doesn't support select(), we will 375 // // immediately notify the select call 376 // status = notify_select_event((void *)sync, ref, event); 377 // } 378 // 379 // put_fd(descriptor); 380 // return status; 381 382 return FSSH_B_BAD_VALUE; 383 } 384 385 386 fssh_status_t 387 deselect_fd(int fd, uint8_t event, struct select_sync *sync, bool kernel) 388 { 389 // struct file_descriptor *descriptor; 390 // fssh_status_t status; 391 // 392 // TRACE(("deselect_fd(fd = %d, event = %u, selectsync = %p)\n", fd, event, sync)); 393 // 394 // descriptor = get_fd(get_current_io_context(kernel), fd); 395 // if (descriptor == NULL) 396 // return FSSH_B_FILE_ERROR; 397 // 398 // if (descriptor->ops->fd_deselect) 399 // status = descriptor->ops->fd_deselect(descriptor, event, sync); 400 // else 401 // status = FSSH_B_OK; 402 // 403 // put_fd(descriptor); 404 // return status; 405 406 return FSSH_B_BAD_VALUE; 407 } 408 409 410 /** This function checks if the specified fd is valid in the current 411 * context. It can be used for a quick check; the fd is not locked 412 * so it could become invalid immediately after this check. 413 */ 414 415 bool 416 fd_is_valid(int fd, bool kernel) 417 { 418 struct file_descriptor *descriptor = get_fd(get_current_io_context(kernel), fd); 419 if (descriptor == NULL) 420 return false; 421 422 put_fd(descriptor); 423 return true; 424 } 425 426 427 struct vnode * 428 fd_vnode(struct file_descriptor *descriptor) 429 { 430 switch (descriptor->type) { 431 case FDTYPE_FILE: 432 case FDTYPE_DIR: 433 case FDTYPE_ATTR_DIR: 434 case FDTYPE_ATTR: 435 return descriptor->u.vnode; 436 } 437 438 return NULL; 439 } 440 441 442 static fssh_status_t 443 common_close(int fd, bool kernel) 444 { 445 struct io_context *io = get_current_io_context(kernel); 446 struct file_descriptor *descriptor = remove_fd(io, fd); 447 448 if (descriptor == NULL) 449 return FSSH_B_FILE_ERROR; 450 451 #ifdef TRACE_FD 452 if (!kernel) 453 TRACE(("_user_close(descriptor = %p)\n", descriptor)); 454 #endif 455 456 close_fd(descriptor); 457 put_fd(descriptor); 458 // the reference associated with the slot 459 460 return FSSH_B_OK; 461 } 462 463 464 // #pragma mark - 465 // Kernel calls 466 467 468 fssh_ssize_t 469 _kern_read(int fd, fssh_off_t pos, void *buffer, fssh_size_t length) 470 { 471 struct file_descriptor *descriptor; 472 fssh_ssize_t bytesRead; 473 474 descriptor = get_fd(get_current_io_context(true), fd); 475 if (!descriptor) 476 return FSSH_B_FILE_ERROR; 477 if ((descriptor->open_mode & FSSH_O_RWMASK) == FSSH_O_WRONLY) { 478 put_fd(descriptor); 479 return FSSH_B_FILE_ERROR; 480 } 481 482 if (pos == -1) 483 pos = descriptor->pos; 484 485 if (descriptor->ops->fd_read) { 486 bytesRead = descriptor->ops->fd_read(descriptor, pos, buffer, &length); 487 if (bytesRead >= FSSH_B_OK) { 488 if (length > FSSH_SSIZE_MAX) 489 bytesRead = FSSH_SSIZE_MAX; 490 else 491 bytesRead = (fssh_ssize_t)length; 492 493 descriptor->pos = pos + length; 494 } 495 } else 496 bytesRead = FSSH_B_BAD_VALUE; 497 498 put_fd(descriptor); 499 return bytesRead; 500 } 501 502 503 fssh_ssize_t 504 _kern_readv(int fd, fssh_off_t pos, const fssh_iovec *vecs, fssh_size_t count) 505 { 506 struct file_descriptor *descriptor; 507 fssh_ssize_t bytesRead = 0; 508 fssh_status_t status; 509 uint32_t i; 510 511 descriptor = get_fd(get_current_io_context(true), fd); 512 if (!descriptor) 513 return FSSH_B_FILE_ERROR; 514 if ((descriptor->open_mode & FSSH_O_RWMASK) == FSSH_O_WRONLY) { 515 put_fd(descriptor); 516 return FSSH_B_FILE_ERROR; 517 } 518 519 if (pos == -1) 520 pos = descriptor->pos; 521 522 if (descriptor->ops->fd_read) { 523 for (i = 0; i < count; i++) { 524 fssh_size_t length = vecs[i].iov_len; 525 status = descriptor->ops->fd_read(descriptor, pos, vecs[i].iov_base, &length); 526 if (status < FSSH_B_OK) { 527 bytesRead = status; 528 break; 529 } 530 531 if ((uint32_t)bytesRead + length > FSSH_SSIZE_MAX) 532 bytesRead = FSSH_SSIZE_MAX; 533 else 534 bytesRead += (fssh_ssize_t)length; 535 536 pos += vecs[i].iov_len; 537 } 538 } else 539 bytesRead = FSSH_B_BAD_VALUE; 540 541 descriptor->pos = pos; 542 put_fd(descriptor); 543 return bytesRead; 544 } 545 546 547 fssh_ssize_t 548 _kern_write(int fd, fssh_off_t pos, const void *buffer, fssh_size_t length) 549 { 550 struct file_descriptor *descriptor; 551 fssh_ssize_t bytesWritten; 552 553 descriptor = get_fd(get_current_io_context(true), fd); 554 if (descriptor == NULL) 555 return FSSH_B_FILE_ERROR; 556 if ((descriptor->open_mode & FSSH_O_RWMASK) == FSSH_O_RDONLY) { 557 put_fd(descriptor); 558 return FSSH_B_FILE_ERROR; 559 } 560 561 if (pos == -1) 562 pos = descriptor->pos; 563 564 if (descriptor->ops->fd_write) { 565 bytesWritten = descriptor->ops->fd_write(descriptor, pos, buffer, &length); 566 if (bytesWritten >= FSSH_B_OK) { 567 if (length > FSSH_SSIZE_MAX) 568 bytesWritten = FSSH_SSIZE_MAX; 569 else 570 bytesWritten = (fssh_ssize_t)length; 571 572 descriptor->pos = pos + length; 573 } 574 } else 575 bytesWritten = FSSH_B_BAD_VALUE; 576 577 put_fd(descriptor); 578 return bytesWritten; 579 } 580 581 582 fssh_ssize_t 583 _kern_writev(int fd, fssh_off_t pos, const fssh_iovec *vecs, fssh_size_t count) 584 { 585 struct file_descriptor *descriptor; 586 fssh_ssize_t bytesWritten = 0; 587 fssh_status_t status; 588 uint32_t i; 589 590 descriptor = get_fd(get_current_io_context(true), fd); 591 if (!descriptor) 592 return FSSH_B_FILE_ERROR; 593 if ((descriptor->open_mode & FSSH_O_RWMASK) == FSSH_O_RDONLY) { 594 put_fd(descriptor); 595 return FSSH_B_FILE_ERROR; 596 } 597 598 if (pos == -1) 599 pos = descriptor->pos; 600 601 if (descriptor->ops->fd_write) { 602 for (i = 0; i < count; i++) { 603 fssh_size_t length = vecs[i].iov_len; 604 status = descriptor->ops->fd_write(descriptor, pos, vecs[i].iov_base, &length); 605 if (status < FSSH_B_OK) { 606 bytesWritten = status; 607 break; 608 } 609 610 if ((uint32_t)bytesWritten + length > FSSH_SSIZE_MAX) 611 bytesWritten = FSSH_SSIZE_MAX; 612 else 613 bytesWritten += (fssh_ssize_t)length; 614 615 pos += vecs[i].iov_len; 616 } 617 } else 618 bytesWritten = FSSH_B_BAD_VALUE; 619 620 descriptor->pos = pos; 621 put_fd(descriptor); 622 return bytesWritten; 623 } 624 625 626 fssh_off_t 627 _kern_seek(int fd, fssh_off_t pos, int seekType) 628 { 629 struct file_descriptor *descriptor; 630 631 descriptor = get_fd(get_current_io_context(true), fd); 632 if (!descriptor) 633 return FSSH_B_FILE_ERROR; 634 635 if (descriptor->ops->fd_seek) 636 pos = descriptor->ops->fd_seek(descriptor, pos, seekType); 637 else 638 pos = FSSH_ESPIPE; 639 640 put_fd(descriptor); 641 return pos; 642 } 643 644 645 fssh_status_t 646 _kern_ioctl(int fd, uint32_t op, void *buffer, fssh_size_t length) 647 { 648 struct file_descriptor *descriptor; 649 int status; 650 651 TRACE(("sys_ioctl: fd %d\n", fd)); 652 653 descriptor = get_fd(get_current_io_context(true), fd); 654 if (descriptor == NULL) 655 return FSSH_B_FILE_ERROR; 656 657 if (descriptor->ops->fd_ioctl) 658 status = descriptor->ops->fd_ioctl(descriptor, op, buffer, length); 659 else 660 status = FSSH_EOPNOTSUPP; 661 662 put_fd(descriptor); 663 return status; 664 } 665 666 667 fssh_ssize_t 668 _kern_read_dir(int fd, struct fssh_dirent *buffer, fssh_size_t bufferSize, uint32_t maxCount) 669 { 670 struct file_descriptor *descriptor; 671 fssh_ssize_t retval; 672 673 TRACE(("sys_read_dir(fd = %d, buffer = %p, bufferSize = %ld, count = %lu)\n",fd, buffer, bufferSize, maxCount)); 674 675 descriptor = get_fd(get_current_io_context(true), fd); 676 if (descriptor == NULL) 677 return FSSH_B_FILE_ERROR; 678 679 if (descriptor->ops->fd_read_dir) { 680 uint32_t count = maxCount; 681 retval = descriptor->ops->fd_read_dir(descriptor, buffer, bufferSize, &count); 682 if (retval >= 0) 683 retval = count; 684 } else 685 retval = FSSH_EOPNOTSUPP; 686 687 put_fd(descriptor); 688 return retval; 689 } 690 691 692 fssh_status_t 693 _kern_rewind_dir(int fd) 694 { 695 struct file_descriptor *descriptor; 696 fssh_status_t status; 697 698 TRACE(("sys_rewind_dir(fd = %d)\n",fd)); 699 700 descriptor = get_fd(get_current_io_context(true), fd); 701 if (descriptor == NULL) 702 return FSSH_B_FILE_ERROR; 703 704 if (descriptor->ops->fd_rewind_dir) 705 status = descriptor->ops->fd_rewind_dir(descriptor); 706 else 707 status = FSSH_EOPNOTSUPP; 708 709 put_fd(descriptor); 710 return status; 711 } 712 713 714 fssh_status_t 715 _kern_close(int fd) 716 { 717 return common_close(fd, true); 718 } 719 720 721 int 722 _kern_dup(int fd) 723 { 724 return dup_fd(fd, true); 725 } 726 727 728 int 729 _kern_dup2(int ofd, int nfd) 730 { 731 return dup2_fd(ofd, nfd, true); 732 } 733 734 } // namespace FSShell 735