/*! Searches a free slot in the FD table of the provided I/O context, and inserts the specified descriptor into it. */ int new_fd_etc(struct io_context* context, struct file_descriptor* descriptor, int firstIndex) { int fd = -1; uint32 i; mutex_lock(&context->io_mutex); for (i = firstIndex; i < context->table_size; i++) { if (!context->fds[i]) { fd = i; break; } } if (fd < 0) { fd = B_NO_MORE_FDS; goto err; } TFD(NewFD(context, fd, descriptor)); context->fds[fd] = descriptor; context->num_used_fds++; atomic_add(&descriptor->open_count, 1); err: mutex_unlock(&context->io_mutex); return fd; }
/*! POSIX says this should be the same as: close(newfd); fcntl(oldfd, F_DUPFD, newfd); We do dup2() directly to be thread-safe. */ static int dup2_fd(int oldfd, int newfd, bool kernel) { struct file_descriptor* evicted = NULL; struct io_context* context; TRACE(("dup2_fd: ofd = %d, nfd = %d\n", oldfd, newfd)); // quick check if (oldfd < 0 || newfd < 0) return B_FILE_ERROR; // Get current I/O context and lock it context = get_current_io_context(kernel); mutex_lock(&context->io_mutex); // Check if the fds are valid (mutex must be locked because // the table size could be changed) if ((uint32)oldfd >= context->table_size || (uint32)newfd >= context->table_size || context->fds[oldfd] == NULL || (context->fds[oldfd]->open_mode & O_DISCONNECTED) != 0) { mutex_unlock(&context->io_mutex); return B_FILE_ERROR; } // Check for identity, note that it cannot be made above // because we always want to return an error on invalid // handles select_info* selectInfos = NULL; if (oldfd != newfd) { // Now do the work TFD(Dup2FD(context, oldfd, newfd)); evicted = context->fds[newfd]; selectInfos = context->select_infos[newfd]; context->select_infos[newfd] = NULL; atomic_add(&context->fds[oldfd]->ref_count, 1); atomic_add(&context->fds[oldfd]->open_count, 1); context->fds[newfd] = context->fds[oldfd]; if (evicted == NULL) context->num_used_fds++; } fd_set_close_on_exec(context, newfd, false); mutex_unlock(&context->io_mutex); // Say bye bye to the evicted fd if (evicted) { deselect_select_infos(evicted, selectInfos, true); close_fd(evicted); put_fd(evicted); } return newfd; }
static struct file_descriptor* get_fd_locked(struct io_context* context, int fd) { if (fd < 0 || (uint32)fd >= context->table_size) return NULL; struct file_descriptor* descriptor = context->fds[fd]; if (descriptor != NULL) { TFD(GetFD(context, fd, descriptor)); inc_fd_ref_count(descriptor); } return descriptor; }
static struct file_descriptor* get_fd_locked(struct io_context* context, int fd) { if (fd < 0 || (uint32)fd >= context->table_size) return NULL; struct file_descriptor* descriptor = context->fds[fd]; if (descriptor != NULL) { // Disconnected descriptors cannot be accessed anymore if (descriptor->open_mode & O_DISCONNECTED) descriptor = NULL; else { TFD(GetFD(context, fd, descriptor)); inc_fd_ref_count(descriptor); } } return descriptor; }
/*! Reduces the descriptor's reference counter, and frees all resources when it's no longer used. */ void put_fd(struct file_descriptor* descriptor) { int32 previous = atomic_add(&descriptor->ref_count, -1); TFD(PutFD(descriptor)); TRACE(("put_fd(descriptor = %p [ref = %ld, cookie = %p])\n", descriptor, descriptor->ref_count, descriptor->cookie)); // free the descriptor if we don't need it anymore if (previous == 1) { // free the underlying object if (descriptor->ops != NULL && descriptor->ops->fd_free != NULL) descriptor->ops->fd_free(descriptor); free(descriptor); } else if ((descriptor->open_mode & O_DISCONNECTED) != 0 && previous - 1 == descriptor->open_count && descriptor->ops != NULL) { // the descriptor has been disconnected - it cannot // be accessed anymore, let's close it (no one is // currently accessing this descriptor) if (descriptor->ops->fd_close) descriptor->ops->fd_close(descriptor); if (descriptor->ops->fd_free) descriptor->ops->fd_free(descriptor); // prevent this descriptor from being closed/freed again descriptor->open_count = -1; descriptor->ref_count = -1; descriptor->ops = NULL; descriptor->u.vnode = NULL; // the file descriptor is kept intact, so that it's not // reused until someone explicetly closes it } }
/*! Removes the file descriptor from the specified slot. */ static struct file_descriptor* remove_fd(struct io_context* context, int fd) { struct file_descriptor* descriptor = NULL; if (fd < 0) return NULL; mutex_lock(&context->io_mutex); if ((uint32)fd < context->table_size) descriptor = context->fds[fd]; select_info* selectInfos = NULL; bool disconnected = false; if (descriptor != NULL) { // fd is valid TFD(RemoveFD(context, fd, descriptor)); context->fds[fd] = NULL; fd_set_close_on_exec(context, fd, false); context->num_used_fds--; selectInfos = context->select_infos[fd]; context->select_infos[fd] = NULL; disconnected = (descriptor->open_mode & O_DISCONNECTED); } mutex_unlock(&context->io_mutex); if (selectInfos != NULL) deselect_select_infos(descriptor, selectInfos, true); return disconnected ? NULL : descriptor; }