/* ==== fd.c ============================================================ * Copyright (c) 1993, 1994 by Chris Provenzano, proven@mit.edu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Chris Provenzano. * 4. The name of Chris Provenzano may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY CHRIS PROVENZANO ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL CHRIS PROVENZANO BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Description : All the syscalls dealing with fds. * * 1.00 93/08/14 proven * -Started coding this file. * * 1.01 93/11/13 proven * -The functions readv() and writev() added. */ #ifndef lint static const char rcsid[] = "$Id: fd.c,v 1.2 1998/07/21 19:48:00 peter Exp $"; #endif #include #include #include #include #include #include #include #ifdef HAVE_SYS_FILIO_H #include /* For ioctl */ #endif #if __STDC__ #include #else #include #endif #include #include #include /* * These first functions really should not be called by the user. * * I really should dynamically figure out what the table size is. */ static pthread_mutex_t fd_table_mutex = PTHREAD_MUTEX_INITIALIZER; static const int dtablecount = 4096/sizeof(struct fd_table_entry); int dtablesize; static int fd_get_pthread_fd_from_kernel_fd( int ); /* ========================================================================== * Allocate dtablecount entries at once and populate the fd_table. * * fd_init_entry() */ int fd_init_entry(int entry) { struct fd_table_entry *fd_entry; int i, round; if (fd_table[entry] == NULL) { round = entry - entry % dtablecount; if ((fd_entry = (struct fd_table_entry *)malloc( sizeof(struct fd_table_entry) * dtablecount)) == NULL) { return(NOTOK); } for (i = 0; i < dtablecount && round+i < dtablesize; i++) { fd_table[round + i] = &fd_entry[i]; fd_table[round + i]->ops = NULL; fd_table[round + i]->type = FD_NT; fd_table[round + i]->fd.i = NOTOK; fd_table[round + i]->flags = 0; fd_table[round + i]->count = 0; pthread_mutex_init(&(fd_table[round + i]->mutex), NULL); pthread_queue_init(&(fd_table[round + i]->r_queue)); pthread_queue_init(&(fd_table[round + i]->w_queue)); fd_table[round + i]->r_owner = NULL; fd_table[round + i]->w_owner = NULL; fd_table[round + i]->r_lockcount= 0; fd_table[round + i]->w_lockcount= 0; fd_table[round + i]->next = NULL; } } return(OK); } /* ========================================================================== * fd_check_entry() */ int fd_check_entry(unsigned int entry) { int ret = OK; pthread_mutex_lock(&fd_table_mutex); if (entry < dtablesize) { if (fd_table[entry] == NULL) { if (fd_init_entry(entry)) { SET_ERRNO(EBADF); ret = -EBADF; } } } else { SET_ERRNO(EBADF); ret = -EBADF; } pthread_mutex_unlock(&fd_table_mutex); return(ret); } /* ========================================================================== * fd_init() */ void fd_init(void) { int i; if ((dtablesize = machdep_sys_getdtablesize()) < 0) { /* Can't figure out the table size. */ PANIC(); } /* select() can only handle FD_SETSIZE descriptors, so our inner loop will * break if dtablesize is higher than that. This should be removed if and * when the inner loop is rewritten to use poll(). */ if (dtablesize > FD_SETSIZE) { dtablesize = FD_SETSIZE; } if (fd_table = (struct fd_table_entry **)malloc( sizeof(struct fd_table_entry) * dtablesize)) { memset(fd_table, 0, sizeof(struct fd_table_entry) * dtablesize); if (fd_check_entry(0) == OK) { return; } } /* * There isn't enough memory to allocate a fd table at init time. * This is a problem. */ PANIC(); } /* ========================================================================== * fd_allocate() */ int fd_allocate() { pthread_mutex_t * mutex; int i; for (i = 0; i < dtablesize; i++) { if (fd_check_entry(i) == OK) { mutex = &(fd_table[i]->mutex); if (pthread_mutex_trylock(mutex)) { continue; } if (fd_table[i]->count || fd_table[i]->r_owner || fd_table[i]->w_owner) { pthread_mutex_unlock(mutex); continue; } if (fd_table[i]->type == FD_NT) { /* Test to see if the kernel version is in use */ if ((machdep_sys_fcntl(i, F_GETFL, NULL)) >= OK) { /* If so continue; */ pthread_mutex_unlock(mutex); continue; } } fd_table[i]->count++; pthread_mutex_unlock(mutex); return(i); } } SET_ERRNO(ENFILE); return(NOTOK); } /*---------------------------------------------------------------------- * Function: fd_get_pthread_fd_from_kernel_fd * Purpose: get the fd_table index of a kernel fd * Args: fd = kernel fd to convert * Returns: fd_table index, -1 if not found * Notes: *----------------------------------------------------------------------*/ static int fd_get_pthread_fd_from_kernel_fd( int kfd ) { int j; /* This is *SICK*, but unless there is a faster way to * turn a kernel fd into an fd_table index, this has to do. */ for( j=0; j < dtablesize; j++ ) { if( fd_table[j] && fd_table[j]->type != FD_NT && fd_table[j]->type != FD_NIU && fd_table[j]->fd.i == kfd ) { return j; } } /* Not listed byfd, Check for kernel fd == pthread fd */ if( fd_table[kfd] == NULL || fd_table[kfd]->type == FD_NT ) { /* Assume that the kernel fd is the same */ return kfd; } return NOTOK; /* Not found */ } /* ========================================================================== * fd_basic_basic_unlock() * * The real work of unlock without the locking of fd_table[fd].lock. */ void fd_basic_basic_unlock(struct fd_table_entry * entry, int lock_type) { struct pthread *pthread; if (entry->r_owner == pthread_run) { if ((entry->type == FD_HALF_DUPLEX) || (entry->type == FD_TEST_HALF_DUPLEX) || (lock_type == FD_READ) || (lock_type == FD_RDWR)) { if (entry->r_lockcount == 0) { if (pthread = pthread_queue_deq(&entry->r_queue)) { pthread_sched_prevent(); entry->r_owner = pthread; if ((SET_PF_DONE_EVENT(pthread)) == OK) { pthread_sched_other_resume(pthread); } else { pthread_sched_resume(); } } else { entry->r_owner = NULL; } } else { entry->r_lockcount--; } } } if (entry->w_owner == pthread_run) { if ((entry->type != FD_HALF_DUPLEX) && (entry->type != FD_TEST_HALF_DUPLEX) && ((lock_type == FD_WRITE) || (lock_type == FD_RDWR))) { if (entry->w_lockcount == 0) { if (pthread = pthread_queue_deq(&entry->w_queue)) { pthread_sched_prevent(); entry->w_owner = pthread; if ((SET_PF_DONE_EVENT(pthread)) == OK) { pthread_sched_other_resume(pthread); } else { pthread_sched_resume(); } } else { entry->w_owner = NULL; } } else { entry->w_lockcount--; } } } } /* ========================================================================== * fd_basic_unlock() */ void fd_basic_unlock(int fd, int lock_type) { fd_basic_basic_unlock(fd_table[fd], lock_type); } /* ========================================================================== * fd_unlock() */ void fd_unlock(int fd, int lock_type) { pthread_mutex_t *mutex; mutex = &(fd_table[fd]->mutex); pthread_mutex_lock(mutex); fd_basic_basic_unlock(fd_table[fd], lock_type); pthread_mutex_unlock(mutex); } /* ========================================================================== * fd_basic_lock() * * The real work of lock without the locking of fd_table[fd].lock. * Be sure to leave the lock the same way you found it. i.e. locked. */ int fd_basic_lock(unsigned int fd, int lock_type, pthread_mutex_t * mutex, struct timespec * timeout) { semaphore *plock; switch (fd_table[fd]->type) { case FD_NIU: /* If not in use return EBADF error */ SET_ERRNO(EBADF); return(NOTOK); break; case FD_NT: /* * If not tested, test it and see if it is valid * If not ok return EBADF error */ fd_kern_init(fd); if (fd_table[fd]->type == FD_NIU) { SET_ERRNO(EBADF); return(NOTOK); } break; case FD_TEST_HALF_DUPLEX: case FD_TEST_FULL_DUPLEX: /* If a parent process reset the fd to its proper state */ if (!fork_lock) { /* It had better be a kernel fd */ fd_kern_reset(fd); } break; default: break; } if ((fd_table[fd]->type == FD_HALF_DUPLEX) || (fd_table[fd]->type == FD_TEST_HALF_DUPLEX) || (lock_type == FD_READ) || (lock_type == FD_RDWR)) { if (fd_table[fd]->r_owner) { if (fd_table[fd]->r_owner != pthread_run) { pthread_sched_prevent(); pthread_queue_enq(&fd_table[fd]->r_queue, pthread_run); SET_PF_WAIT_EVENT(pthread_run); pthread_mutex_unlock(mutex); if (timeout) { /* get current time */ struct timespec current_time; machdep_gettimeofday(¤t_time); sleep_schedule(¤t_time, timeout); /* Reschedule will unlock pthread_run */ pthread_run->data.fd.fd = fd; pthread_run->data.fd.branch = __LINE__; pthread_resched_resume(PS_FDLR_WAIT); pthread_mutex_lock(mutex); /* If we're the owner then we have to cancel the sleep */ if (fd_table[fd]->r_owner != pthread_run) { CLEAR_PF_DONE_EVENT(pthread_run); SET_ERRNO(ETIMEDOUT); return(NOTOK); } sleep_cancel(pthread_run); } else { /* Reschedule will unlock pthread_run */ pthread_run->data.fd.fd = fd; pthread_run->data.fd.branch = __LINE__; pthread_resched_resume(PS_FDLR_WAIT); pthread_mutex_lock(mutex); } CLEAR_PF_DONE_EVENT(pthread_run); } else { fd_table[fd]->r_lockcount++; } } fd_table[fd]->r_owner = pthread_run; } if ((fd_table[fd]->type != FD_HALF_DUPLEX) && (fd_table[fd]->type != FD_TEST_HALF_DUPLEX) && ((lock_type == FD_WRITE) || (lock_type == FD_RDWR))) { if (fd_table[fd]->w_owner) { if (fd_table[fd]->w_owner != pthread_run) { pthread_sched_prevent(); pthread_queue_enq(&fd_table[fd]->w_queue, pthread_run); SET_PF_WAIT_EVENT(pthread_run); pthread_mutex_unlock(mutex); if (timeout) { /* get current time */ struct timespec current_time; machdep_gettimeofday(¤t_time); sleep_schedule(¤t_time, timeout); /* Reschedule will unlock pthread_run */ pthread_run->data.fd.fd = fd; pthread_run->data.fd.branch = __LINE__; pthread_resched_resume(PS_FDLR_WAIT); pthread_mutex_lock(mutex); /* If we're the owner then we have to cancel the sleep */ if (fd_table[fd]->w_owner != pthread_run) { if (lock_type == FD_RDWR) { /* Unlock current thread */ fd_basic_unlock(fd, FD_READ); } CLEAR_PF_DONE_EVENT(pthread_run); SET_ERRNO(ETIMEDOUT); return(NOTOK); } sleep_cancel(pthread_run); } else { /* Reschedule will unlock pthread_run */ pthread_run->data.fd.fd = fd; pthread_run->data.fd.branch = __LINE__; pthread_resched_resume(PS_FDLR_WAIT); pthread_mutex_lock(mutex); } CLEAR_PF_DONE_EVENT(pthread_run); } else { fd_table[fd]->w_lockcount++; } } fd_table[fd]->w_owner = pthread_run; } if (!fd_table[fd]->count) { fd_basic_unlock(fd, lock_type); return(NOTOK); } return(OK); } /*---------------------------------------------------------------------- * Function: fd_unlock_for_cancel * Purpose: Unlock all fd locks held prior to being cancelled * Args: void * Returns: * OK or NOTOK * Notes: * Assumes the kernel is locked on entry *----------------------------------------------------------------------*/ int fd_unlock_for_cancel( void ) { int i, fd; struct pthread_select_data *data; int rdlk, wrlk, lktype; int found; /* What we do depends on the previous state of the thread */ switch( pthread_run->old_state ) { case PS_RUNNING: case PS_JOIN: case PS_SLEEP_WAIT: case PS_WAIT_WAIT: case PS_SIGWAIT: case PS_FDLR_WAIT: case PS_FDLW_WAIT: case PS_DEAD: case PS_UNALLOCED: break; /* Nothing to do */ case PS_COND_WAIT: CLEAR_PF_GROUP( pthread_run, PF_EVENT_GROUP ); /* Must reaquire the mutex according to the standard */ if( pthread_run->data.mutex == NULL ) { PANIC(); } pthread_mutex_lock( pthread_run->data.mutex ); break; case PS_FDR_WAIT: CLEAR_PF_GROUP( pthread_run, PF_EVENT_GROUP); /* Free the lock on the fd being used */ fd = fd_get_pthread_fd_from_kernel_fd( pthread_run->data.fd.fd ); if( fd == NOTOK ) { PANIC(); /* Can't find fd */ } fd_unlock( fd, FD_READ ); break; case PS_FDW_WAIT: /* Waiting on i/o */ CLEAR_PF_GROUP( pthread_run, PF_EVENT_GROUP); /* Free the lock on the fd being used */ fd = fd_get_pthread_fd_from_kernel_fd( pthread_run->data.fd.fd ); if( fd == NOTOK ) { PANIC(); /* Can't find fd */ } fd_unlock( fd, FD_WRITE ); break; case PS_SELECT_WAIT: data = pthread_run->data.select_data; CLEAR_PF_GROUP( pthread_run, PF_EVENT_GROUP); for( i = 0; i < data->nfds; i++) { rdlk =(FD_ISSET(i,&data->readfds) || FD_ISSET(i,&data->exceptfds)); wrlk = FD_ISSET(i, &data->writefds); lktype = rdlk ? (wrlk ? FD_RDWR : FD_READ) : FD_WRITE; if( ! (rdlk || wrlk) ) continue; /* No locks, no unlock */ if( (fd = fd_get_pthread_fd_from_kernel_fd( i )) == NOTOK ) { PANIC(); /* Can't find fd */ } fd_unlock( fd, lktype ); } break; case PS_MUTEX_WAIT: PANIC(); /* Should never cancel a mutex wait */ default: PANIC(); /* Unknown thread status */ } } /* ========================================================================== * fd_lock() */ #define pthread_mutex_lock_timedwait(a, b) pthread_mutex_lock(a) int fd_lock(unsigned int fd, int lock_type, struct timespec * timeout) { struct timespec current_time; pthread_mutex_t *mutex; int error; if ((error = fd_check_entry(fd)) == OK) { mutex = &(fd_table[fd]->mutex); if (pthread_mutex_lock_timedwait(mutex, timeout)) { SET_ERRNO(ETIMEDOUT); return(-ETIMEDOUT); } error = fd_basic_lock(fd, lock_type, mutex, timeout); pthread_mutex_unlock(mutex); } return(error); } /* ========================================================================== * fd_free() * * Assumes fd is locked and owner by pthread_run * Don't clear the queues, fd_unlock will do that. */ struct fd_table_entry * fd_free(int fd) { struct fd_table_entry *fd_valid; fd_valid = NULL; fd_table[fd]->r_lockcount = 0; fd_table[fd]->w_lockcount = 0; if (--fd_table[fd]->count) { fd_valid = fd_table[fd]; fd_table[fd] = fd_table[fd]->next; fd_valid->next = fd_table[fd]->next; /* Don't touch queues of fd_valid */ } fd_table[fd]->type = FD_NIU; fd_table[fd]->fd.i = NOTOK; fd_table[fd]->next = NULL; fd_table[fd]->flags = 0; fd_table[fd]->count = 0; return(fd_valid); } /* ========================================================================== * ======================================================================= */ /* ========================================================================== * read_timedwait() */ ssize_t read_timedwait(int fd, void *buf, size_t nbytes, struct timespec * timeout) { int ret; if ((ret = fd_lock(fd, FD_READ, NULL)) == OK) { ret = fd_table[fd]->ops->read(fd_table[fd]->fd, fd_table[fd]->flags, buf, nbytes, timeout); fd_unlock(fd, FD_READ); } return(ret); } /* ========================================================================== * read() */ ssize_t read(int fd, void *buf, size_t nbytes) { return(read_timedwait(fd, buf, nbytes, NULL)); } /* ========================================================================== * readv_timedwait() */ int readv_timedwait(int fd, const struct iovec *iov, int iovcnt, struct timespec * timeout) { int ret; if ((ret = fd_lock(fd, FD_READ, NULL)) == OK) { ret = fd_table[fd]->ops->readv(fd_table[fd]->fd, fd_table[fd]->flags, iov, iovcnt, timeout); fd_unlock(fd, FD_READ); } return(ret); } /* ========================================================================== * readv() */ ssize_t readv(int fd, const struct iovec *iov, int iovcnt) { return(readv_timedwait(fd, iov, iovcnt, NULL)); } /* ========================================================================== * write() */ ssize_t write_timedwait(int fd, const void *buf, size_t nbytes, struct timespec * timeout) { int ret; if ((ret = fd_lock(fd, FD_WRITE, NULL)) == OK) { ret = fd_table[fd]->ops->write(fd_table[fd]->fd, fd_table[fd]->flags, buf, nbytes, timeout); fd_unlock(fd, FD_WRITE); } return(ret); } /* ========================================================================== * write() */ ssize_t write(int fd, const void * buf, size_t nbytes) { return(write_timedwait(fd, buf, nbytes, NULL)); } /* ========================================================================== * writev_timedwait() */ int writev_timedwait(int fd, const struct iovec *iov, int iovcnt, struct timespec * timeout) { int ret; if ((ret = fd_lock(fd, FD_WRITE, NULL)) == OK) { ret = fd_table[fd]->ops->writev(fd_table[fd]->fd, fd_table[fd]->flags, iov, iovcnt, timeout); fd_unlock(fd, FD_WRITE); } return(ret); } /* ========================================================================== * writev() */ ssize_t writev(int fd, const struct iovec *iov, int iovcnt) { return(writev_timedwait(fd, iov, iovcnt, NULL)); } /* ========================================================================== * lseek() */ off_t lseek(int fd, off_t offset, int whence) { int ret; if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) { ret = fd_table[fd]->ops->seek(fd_table[fd]->fd, fd_table[fd]->flags, offset, whence); fd_unlock(fd, FD_RDWR); } return(ret); } /* ========================================================================== * close() * * The whole close procedure is a bit odd and needs a bit of a rethink. * For now close() locks the fd, calls fd_free() which checks to see if * there are any other fd values poinging to the same real fd. If so * It breaks the wait queue into two sections those that are waiting on fd * and those waiting on other fd's. Those that are waiting on fd are connected * to the fd_table[fd] queue, and the count is set to zero, (BUT THE LOCK IS NOT * RELEASED). close() then calls fd_unlock which give the fd to the next queued * element which determins that the fd is closed and then calls fd_unlock etc... * * XXX close() is even uglier now. You may assume that the kernel fd is the * same as fd if fd_table[fd] == NULL or if fd_table[fd]->type == FD_NT. * This is true because before any fd_table[fd] is allocated the corresponding * kernel fd must be checks to see if it's valid. */ int close(int fd) { struct fd_table_entry * entry; pthread_mutex_t *mutex; union fd_data realfd; int ret, flags; if(fd < 0 || fd >= dtablesize) { SET_ERRNO(EBADF); return -1; } /* Need to lock the newfd by hand */ pthread_mutex_lock(&fd_table_mutex); if (fd_table[fd]) { pthread_mutex_unlock(&fd_table_mutex); mutex = &(fd_table[fd]->mutex); pthread_mutex_lock(mutex); /* * XXX Gross hack ... because of fork(), any fd closed by the * parent should not change the fd of the child, unless it owns it. */ switch(fd_table[fd]->type) { case FD_NIU: pthread_mutex_unlock(mutex); ret = -EBADF; break; case FD_NT: /* * If it's not tested then the only valid possibility is it's * kernel fd. */ ret = machdep_sys_close(fd); fd_table[fd]->type = FD_NIU; pthread_mutex_unlock(mutex); break; case FD_TEST_FULL_DUPLEX: case FD_TEST_HALF_DUPLEX: realfd = fd_table[fd]->fd; flags = fd_table[fd]->flags; if ((entry = fd_free(fd)) == NULL) { ret = fd_table[fd]->ops->close(realfd, flags); } else { /* There can't be any others waiting for fd. */ pthread_mutex_unlock(&entry->mutex); /* Note: entry->mutex = mutex */ mutex = &(fd_table[fd]->mutex); } pthread_mutex_unlock(mutex); break; default: ret = fd_basic_lock(fd, FD_RDWR, mutex, NULL); if (ret == OK) { realfd = fd_table[fd]->fd; flags = fd_table[fd]->flags; pthread_mutex_unlock(mutex); if ((entry = fd_free(fd)) == NULL) { ret = fd_table[fd]->ops->close(realfd, flags); } else { fd_basic_basic_unlock(entry, FD_RDWR); pthread_mutex_unlock(&entry->mutex); /* Note: entry->mutex = mutex */ } fd_unlock(fd, FD_RDWR); } else { pthread_mutex_unlock(mutex); } break; } } else { /* Don't bother creating a table entry */ pthread_mutex_unlock(&fd_table_mutex); ret = machdep_sys_close(fd); } if( ret < 0) { SET_ERRNO(-ret); ret = -1; } return(ret); } /* ========================================================================== * fd_basic_dup() * * * This is a MAJOR guess!! I don't know if the mutext unlock is valid * in the BIG picture. But it seems to be needed to avoid deadlocking * with ourselves when we try to close the duped file descriptor. */ static inline void fd_basic_dup(int fd, int newfd) { fd_table[newfd]->next = fd_table[fd]->next; fd_table[fd]->next = fd_table[newfd]; fd_table[newfd] = fd_table[fd]; fd_table[fd]->count++; pthread_mutex_unlock(&fd_table[newfd]->next->mutex); } /* ========================================================================== * dup2() * * Note: Always lock the lower number fd first to avoid deadlocks. * Note: Leave the newfd locked. It will be unlocked at close() time. * Note: newfd must be locked by hand so it can be closed if it is open, * or it won't be opened while dup is in progress. */ int dup2(fd, newfd) { struct fd_table_entry * entry; pthread_mutex_t *mutex; union fd_data realfd; int ret, flags; if ((ret = fd_check_entry(newfd)) != OK) return ret; if (newfd < dtablesize) { if (fd < newfd) { if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) { /* Need to lock the newfd by hand */ mutex = &(fd_table[newfd]->mutex); pthread_mutex_lock(mutex); /* Is it inuse */ if (fd_basic_lock(newfd, FD_RDWR, mutex, NULL) == OK) { realfd = fd_table[newfd]->fd; flags = fd_table[newfd]->flags; /* free it and check close status */ if ((entry = fd_free(newfd)) == NULL) { entry = fd_table[newfd]; entry->ops->close(realfd, flags); if (entry->r_queue.q_next) { if (fd_table[fd]->next) { fd_table[fd]->r_queue.q_last->next = entry->r_queue.q_next; } else { fd_table[fd]->r_queue.q_next = entry->r_queue.q_next; } fd_table[fd]->r_queue.q_last = entry->r_queue.q_last; } if (entry->w_queue.q_next) { if (fd_table[fd]->next) { fd_table[fd]->w_queue.q_last->next = entry->w_queue.q_next; } else { fd_table[fd]->w_queue.q_next = entry->w_queue.q_next; } fd_table[fd]->w_queue.q_last = entry->w_queue.q_last; } entry->r_queue.q_next = NULL; entry->w_queue.q_next = NULL; entry->r_queue.q_last = NULL; entry->w_queue.q_last = NULL; entry->r_owner = NULL; entry->w_owner = NULL; ret = OK; } else { fd_basic_basic_unlock(entry, FD_RDWR); pthread_mutex_unlock(&entry->mutex); /* Note: entry->mutex = mutex */ } } fd_basic_dup(fd, newfd); } fd_unlock(fd, FD_RDWR); } else { /* Need to lock the newfd by hand */ mutex = &(fd_table[newfd]->mutex); pthread_mutex_lock(mutex); if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) { /* Is newfd inuse */ if ((ret = fd_basic_lock(newfd, FD_RDWR, mutex, NULL)) == OK) { realfd = fd_table[newfd]->fd; flags = fd_table[newfd]->flags; /* free it and check close status */ if ((entry = fd_free(newfd)) == NULL) { entry = fd_table[newfd]; entry->ops->close(realfd, flags); if (entry->r_queue.q_next) { if (fd_table[fd]->next) { fd_table[fd]->r_queue.q_last->next = entry->r_queue.q_next; } else { fd_table[fd]->r_queue.q_next = entry->r_queue.q_next; } fd_table[fd]->r_queue.q_last = entry->r_queue.q_last; } if (entry->w_queue.q_next) { if (fd_table[fd]->next) { fd_table[fd]->w_queue.q_last->next = entry->w_queue.q_next; } else { fd_table[fd]->w_queue.q_next = entry->w_queue.q_next; } fd_table[fd]->w_queue.q_last = entry->w_queue.q_last; } entry->r_queue.q_next = NULL; entry->w_queue.q_next = NULL; entry->r_queue.q_last = NULL; entry->w_queue.q_last = NULL; entry->r_owner = NULL; entry->w_owner = NULL; ret = OK; } else { fd_basic_basic_unlock(entry, FD_RDWR); pthread_mutex_unlock(&entry->mutex); /* Note: entry->mutex = mutex */ } fd_basic_dup(fd, newfd); } fd_unlock(fd, FD_RDWR); } } } else { ret = NOTOK; } return(ret); } /* ========================================================================== * dup() */ int dup(int fd) { int ret; if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) { ret = fd_allocate(); fd_basic_dup(fd, ret); fd_unlock(fd, FD_RDWR); } return(ret); } /* ========================================================================== * fcntl() */ int fcntl(int fd, int cmd, ...) { int ret, realfd, flags; struct flock *flock; semaphore *plock; va_list ap; flags = 0; if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) { va_start(ap, cmd); switch(cmd) { case F_DUPFD: ret = fd_allocate(); fd_basic_dup(va_arg(ap, int), ret); break; case F_SETFD: break; case F_GETFD: break; case F_GETFL: ret = fd_table[fd]->flags; break; case F_SETFL: flags = va_arg(ap, int); if ((ret = fd_table[fd]->ops->fcntl(fd_table[fd]->fd, fd_table[fd]->flags, cmd, flags | __FD_NONBLOCK)) == OK) { fd_table[fd]->flags = flags; } break; /* case F_SETLKW: */ /* * Do the same as SETLK but if it fails with EACCES or EAGAIN * block the thread and try again later, not implemented yet */ /* case F_SETLK: */ /* case F_GETLK: flock = va_arg(ap, struct flock*); ret = fd_table[fd]->ops->fcntl(fd_table[fd]->fd, fd_table[fd]->flags, cmd, flock); break; */ default: /* Might want to make va_arg use a union */ ret = fd_table[fd]->ops->fcntl(fd_table[fd]->fd, fd_table[fd]->flags, cmd, va_arg(ap, void*)); break; } va_end(ap); fd_unlock(fd, FD_RDWR); } return(ret); } /* ========================================================================== * getdtablesize() */ int getdtablesize() { return dtablesize; } /* ========================================================================== * ioctl() * * Really want to do a real implementation of this that parses the args ala * fcntl(), above, but it will have to be a totally platform-specific, * nightmare-on-elm-st-style sort of thing. Might even deserve its own file * ala select()... --SNL */ #ifndef ioctl_request_type #define ioctl_request_type unsigned long /* Dummy patch by Monty */ #endif int ioctl(int fd, ioctl_request_type request, ...) { int ret; pthread_va_list ap; caddr_t arg; va_start( ap, request ); /* Get the arg */ arg = va_arg(ap,caddr_t); va_end( ap ); if (fd < 0 || fd >= dtablesize) ret = NOTOK; else if (fd_table[fd]->fd.i == NOTOK) ret = machdep_sys_ioctl(fd, request, arg); else if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) { ret = machdep_sys_ioctl(fd_table[fd]->fd.i, request, arg); if( ret == 0 && request == FIONBIO ) { /* Properly set NONBLOCK flag */ int v = *(int *)arg; if( v ) fd_table[fd]->flags |= __FD_NONBLOCK; else fd_table[fd]->flags &= ~__FD_NONBLOCK; } fd_unlock(fd, FD_RDWR); } return ret; }