#include #include #include #include #include #include // TODO have a limit for the fd table static scache_t *filecache; #define FILE_HOLD(f) __atomic_add_fetch(&(f)->refcount, 1, __ATOMIC_SEQ_CST) #define FILE_RELEASE(f) \ if (__atomic_sub_fetch(&(f)->refcount, 1, __ATOMIC_SEQ_CST) == 0) {\ cleanfile(f); \ } static void ctor(scache_t *cache, void *obj) { file_t *file = obj; file->vnode = NULL; MUTEX_INIT(&file->mutex); file->refcount = 1; file->offset = 0; } static file_t* newfile() { if (filecache == NULL) { filecache = slab_newcache(sizeof(file_t), 0, ctor, ctor); __assert(filecache); } return slab_allocate(filecache); } static void cleanfile(file_t *file) { if (file->vnode) { vfs_close(file->vnode, fileflagstovnodeflags(file->flags)); VOP_RELEASE(file->vnode); } slab_free(filecache, file); } file_t *fd_allocate() { return newfile(); } file_t *fd_get(int fd) { proc_t *proc = _cpu()->thread->proc; bool intstatus = interrupt_set(false); spinlock_acquire(&proc->fdlock); file_t *file = fd < proc->fdcount ? proc->fd[fd].file : NULL; if (file) FILE_HOLD(file); spinlock_release(&proc->fdlock); interrupt_set(intstatus); if (file) MUTEX_ACQUIRE(&file->mutex, false); return file; } void fd_release(file_t *file) { MUTEX_RELEASE(&file->mutex); FILE_RELEASE(file); } int fd_new(int flags, file_t **rfile, int *rfd) { file_t *file = newfile(); if (file == NULL) return ENOMEM; proc_t *proc = _cpu()->thread->proc; spinlock_acquire(&proc->fdlock); // find first free fd int fd = -1; int sfd; for (sfd = proc->fdfirst; fd < proc->fdcount && proc->fd[fd].file; ++fd); if (sfd != proc->fdcount) fd = sfd; // resize table if not found if (fd == -1) { void *newtable = realloc(proc->fd, sizeof(fd_t) * (proc->fdcount + 1)); if (newtable == NULL) { spinlock_release(&proc->fdlock); cleanfile(file); return ENOMEM; } proc->fd = newtable; fd = proc->fdcount; ++proc->fdcount; } proc->fdfirst = fd + 1; proc->fd[fd].file = file; proc->fd[fd].flags = flags; MUTEX_ACQUIRE(&file->mutex, false); FILE_HOLD(file); spinlock_release(&proc->fdlock); *rfile = file; *rfd = fd; return 0; } int fd_close(int fd) { proc_t *proc = _cpu()->thread->proc; bool intstatus = interrupt_set(false); spinlock_acquire(&proc->fdlock); file_t *file = fd < proc->fdcount ? proc->fd[fd].file : NULL; if (file) { proc->fd[fd].file = NULL; proc->fdfirst = fd; } spinlock_release(&proc->fdlock); interrupt_set(intstatus); if (file) { FILE_RELEASE(file); } else { return EBADF; } return 0; } int fd_clone(proc_t *targproc) { proc_t *proc = _cpu()->thread->proc; int error = 0; spinlock_acquire(&proc->fdlock); targproc->fdcount = proc->fdcount; targproc->fdfirst = proc->fdfirst; targproc->fd = alloc(proc->fdcount * sizeof(fd_t)); if (targproc->fd == NULL) { error = ENOMEM; goto cleanup; } for (int i = 0; i < targproc->fdcount; ++i) { if (proc->fd[i].file == NULL) continue; targproc->fd[i] = proc->fd[i]; FILE_HOLD(targproc->fd[i].file); } cleanup: spinlock_release(&proc->fdlock); return error; }