asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd) { int err = -EBADF; lock_kernel(); #ifdef FDSET_DEBUG printk (KERN_ERR __FUNCTION__ " 0: oldfd = %d, newfd = %d\n", oldfd, newfd); #endif if (!fcheck(oldfd)) goto out; if (newfd >= NR_OPEN) goto out; /* following POSIX.1 6.2.1 */ err = newfd; if (newfd == oldfd) goto out; /* We must be able to do the fd setting inside dupfd() without blocking after the sys_close(). */ if ((err = expand_files(current->files, newfd)) < 0) goto out; sys_close(newfd); err = dupfd(oldfd, newfd); out: #ifdef FDSET_DEBUG printk (KERN_ERR __FUNCTION__ ": return %d\n", err); #endif unlock_kernel(); return err; }
static inline int dupfd(unsigned int fd, unsigned int start) { struct files_struct * files = current->files; struct file * file; unsigned int newfd; int error; error = -EINVAL; if (start >= NR_OPEN) goto out; error = -EBADF; file = fget(fd); if (!file) goto out; repeat: error = -EMFILE; if (start < files->next_fd) start = files->next_fd; /* At this point, start MUST be <= max_fdset */ #if 1 if (start > files->max_fdset) printk (KERN_ERR "dupfd: fd %d, max %d\n", start, files->max_fdset); #endif newfd = find_next_zero_bit(files->open_fds->fds_bits, files->max_fdset, start); if (newfd >= current->rlim[RLIMIT_NOFILE].rlim_cur) goto out_putf; error = expand_files(files, newfd); if (error < 0) goto out_putf; if (error) /* If we might have blocked, try again. */ goto repeat; FD_SET(newfd, files->open_fds); FD_CLR(newfd, files->close_on_exec); if (start <= files->next_fd) files->next_fd = newfd + 1; fd_install(newfd, file); error = newfd; out: #ifdef FDSET_DEBUG if (error < 0) printk (KERN_ERR __FUNCTION__ ": return %d\n", error); #endif return error; out_putf: fput(file); goto out; }
/* Install a given filp in a given files_struct, with CLOEXEC set. * Safe for files != current->files. * Mostly cut-and-paste from linux-2.6.0/fs/fcntl.c:locate_fd() */ int cr_dup_other(struct files_struct *files, struct file *filp) { unsigned int newfd; unsigned int start; unsigned int max_fds; int error; cr_fdtable_t *fdt; spin_lock(&files->file_lock); repeat: fdt = cr_fdtable(files); start = CR_NEXT_FD(files, fdt); newfd = start; max_fds = CR_MAX_FDS(fdt); if (start < max_fds) { newfd = find_next_zero_bit(CR_OPEN_FDS_BITS(fdt), max_fds, start); } /* XXX: Really shouldn't be using current here. * However, I haven't bothered to figure out the locking * requirements for using anything else. * XXX: Probably could just pass the limit in. * XXX: Later kernels push this into expand_files() */ error = -EMFILE; if (newfd >= CR_RLIM(current)[RLIMIT_NOFILE].rlim_cur) { goto out; } error = expand_files(files, newfd); if (error < 0) { goto out; } else if (error) { /* grew - search again (also reacquires fdt) */ goto repeat; } CR_NEXT_FD(files, fdt) = newfd + 1; /* Claim */ cr_set_open_fd(newfd, fdt); cr_set_close_on_exec(newfd, fdt); /* Install */ get_file(filp); rcu_assign_pointer(fdt->fd[newfd], filp); error = newfd; out: spin_unlock(&files->file_lock); return error; }
/* * Find an empty file descriptor entry, and mark it busy. */ int get_unused_fd_flags(int flags) { struct files_struct * files = current->files; int fd, error; struct fdtable *fdt; error = -EMFILE; spin_lock(&files->file_lock); repeat: fdt = files_fdtable(files); fd = find_next_zero_bit(fdt->open_fds->fds_bits, fdt->max_fds, files->next_fd); /* * N.B. For clone tasks sharing a files structure, this test * will limit the total number of files that can be opened. */ if (fd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur) goto out; /* Do we need to expand the fd array or fd set? */ error = expand_files(files, fd); if (error < 0) goto out; if (error) { /* * If we needed to expand the fs array we * might have blocked - try again. */ error = -EMFILE; goto repeat; } FD_SET(fd, fdt->open_fds); if (flags & O_CLOEXEC) FD_SET(fd, fdt->close_on_exec); else FD_CLR(fd, fdt->close_on_exec); files->next_fd = fd + 1; #if 1 /* Sanity check */ if (fdt->fd[fd] != NULL) { printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd); fdt->fd[fd] = NULL; } #endif error = fd; out: spin_unlock(&files->file_lock); return error; }
/** * @brief Program entry point * @details reads options and arguments with getopt and calls the appropriate * expand function. * * @param argc The argument counter * @param argv The argument vector * @return 0 on success; otherwise non-zero */ int main(int argc, char **argv) { int c; while((c = getopt(argc, argv, shortopts)) != -1) { switch(c) { case 't': if(ISDIGIT(*optarg)) { tabstop = strtol(optarg, NULL, 0); if(tabstop == 0) { // TODO: errorhandling (void) printf("tabstop cannot be 0"); return 1; } } else { // TODO: errorhandling (void) printf("NaN"); return 1; } break; case ':': // TODO: errorhandling (void) printf("case ':'"); return 1; case '?': // TODO: errorhandling (void) printf("case '?'"); return 1; default: assert(0); } } char error = 0; if(optind < argc) { file_list = &argv[optind]; error = expand_files(); } else error = expand_stdin(); return error; }
static int copy_files(unsigned long clone_flags, struct task_struct * tsk) { struct files_struct *oldf, *newf; struct file **old_fds, **new_fds; int open_files, size, i, error = 0, expand; /* * A background process may not have any files ... */ oldf = current->files; if (!oldf) goto out; if (clone_flags & CLONE_FILES) { atomic_inc(&oldf->count); goto out; } /* * Note: we may be using current for both targets (See exec.c) * This works because we cache current->files (old) as oldf. Don't * break this. */ tsk->files = NULL; error = -ENOMEM; newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL); if (!newf) goto out; atomic_set(&newf->count, 1); spin_lock_init(&newf->file_lock); newf->next_fd = 0; newf->max_fds = NR_OPEN_DEFAULT; newf->max_fdset = __FD_SETSIZE; newf->close_on_exec = &newf->close_on_exec_init; newf->open_fds = &newf->open_fds_init; newf->fd = &newf->fd_array[0]; spin_lock(&oldf->file_lock); open_files = count_open_files(oldf, oldf->max_fdset); expand = 0; /* * Check whether we need to allocate a larger fd array or fd set. * Note: we're not a clone task, so the open count won't change. */ if (open_files > newf->max_fdset) { newf->max_fdset = 0; expand = 1; } if (open_files > newf->max_fds) { newf->max_fds = 0; expand = 1; } /* if the old fdset gets grown now, we'll only copy up to "size" fds */ if (expand) { spin_unlock(&oldf->file_lock); spin_lock(&newf->file_lock); error = expand_files(newf, open_files-1); spin_unlock(&newf->file_lock); if (error < 0) goto out_release; spin_lock(&oldf->file_lock); } old_fds = oldf->fd; new_fds = newf->fd; memcpy(newf->open_fds->fds_bits, oldf->open_fds->fds_bits, open_files/8); memcpy(newf->close_on_exec->fds_bits, oldf->close_on_exec->fds_bits, open_files/8); for (i = open_files; i != 0; i--) { struct file *f = *old_fds++; if (f) { get_file(f); } else { /* * The fd may be claimed in the fd bitmap but not yet * instantiated in the files array if a sibling thread * is partway through open(). So make sure that this * fd is available to the new process. */ FD_CLR(open_files - i, newf->open_fds); } *new_fds++ = f; } spin_unlock(&oldf->file_lock); /* compute the remainder to be cleared */ size = (newf->max_fds - open_files) * sizeof(struct file *); /* This is long word aligned thus could use a optimized version */ memset(new_fds, 0, size); if (newf->max_fdset > open_files) { int left = (newf->max_fdset-open_files)/8; int start = open_files / (8 * sizeof(unsigned long)); memset(&newf->open_fds->fds_bits[start], 0, left); memset(&newf->close_on_exec->fds_bits[start], 0, left); } tsk->files = newf; error = 0; out: return error; out_release: free_fdset (newf->close_on_exec, newf->max_fdset); free_fdset (newf->open_fds, newf->max_fdset); free_fd_array(newf->fd, newf->max_fds); kmem_cache_free(files_cachep, newf); goto out; }
/* * Allocate a new files structure and copy contents from the * passed in files structure. * errorp will be valid only when the returned files_struct is NULL. */ static struct files_struct *dup_fd(struct files_struct *oldf, int *errorp) { struct files_struct *newf; struct file **old_fds, **new_fds; int open_files, size, i, expand; struct fdtable *old_fdt, *new_fdt; *errorp = -ENOMEM; newf = alloc_files(); if (!newf) goto out; spin_lock(&oldf->file_lock); old_fdt = files_fdtable(oldf); new_fdt = files_fdtable(newf); size = old_fdt->max_fdset; open_files = count_open_files(old_fdt); expand = 0; /* * Check whether we need to allocate a larger fd array or fd set. * Note: we're not a clone task, so the open count won't change. */ if (open_files > new_fdt->max_fdset) { new_fdt->max_fdset = 0; expand = 1; } if (open_files > new_fdt->max_fds) { new_fdt->max_fds = 0; expand = 1; } /* if the old fdset gets grown now, we'll only copy up to "size" fds */ if (expand) { spin_unlock(&oldf->file_lock); spin_lock(&newf->file_lock); *errorp = expand_files(newf, open_files-1); spin_unlock(&newf->file_lock); if (*errorp < 0) goto out_release; new_fdt = files_fdtable(newf); /* * Reacquire the oldf lock and a pointer to its fd table * who knows it may have a new bigger fd table. We need * the latest pointer. */ spin_lock(&oldf->file_lock); old_fdt = files_fdtable(oldf); } old_fds = old_fdt->fd; new_fds = new_fdt->fd; memcpy(new_fdt->open_fds->fds_bits, old_fdt->open_fds->fds_bits, open_files/8); memcpy(new_fdt->close_on_exec->fds_bits, old_fdt->close_on_exec->fds_bits, open_files/8); for (i = open_files; i != 0; i--) { struct file *f = *old_fds++; if (f) { get_file(f); } else { /* * The fd may be claimed in the fd bitmap but not yet * instantiated in the files array if a sibling thread * is partway through open(). So make sure that this * fd is available to the new process. */ FD_CLR(open_files - i, new_fdt->open_fds); } rcu_assign_pointer(*new_fds++, f); } spin_unlock(&oldf->file_lock); /* compute the remainder to be cleared */ size = (new_fdt->max_fds - open_files) * sizeof(struct file *); /* This is long word aligned thus could use a optimized version */ memset(new_fds, 0, size); if (new_fdt->max_fdset > open_files) { int left = (new_fdt->max_fdset-open_files)/8; int start = open_files / (8 * sizeof(unsigned long)); memset(&new_fdt->open_fds->fds_bits[start], 0, left); memset(&new_fdt->close_on_exec->fds_bits[start], 0, left); } out: return newf; out_release: free_fdset (new_fdt->close_on_exec, new_fdt->max_fdset); free_fdset (new_fdt->open_fds, new_fdt->max_fdset); free_fd_array(new_fdt->fd, new_fdt->max_fds); kmem_cache_free(files_cachep, newf); return NULL; }
static int task_get_unused_fd_flags(struct shfile_proc *proc, int flags) { struct files_struct *files = proc->files; int fd, error; struct fdtable *fdt; unsigned long rlim_cur; unsigned long irqs; if (files == NULL) return -ESRCH; error = -EMFILE; spin_lock(&files->file_lock); repeat: fdt = files_fdtable(files); fd = find_next_zero_bit(fdt->open_fds, fdt->max_fds, files->next_fd); /* * N.B. For clone tasks sharing a files structure, this test * will limit the total number of files that can be opened. */ rlim_cur = 0; if (lock_task_sighand(proc->tsk, &irqs)) { rlim_cur = proc->tsk->signal->rlim[RLIMIT_NOFILE].rlim_cur; unlock_task_sighand(proc->tsk, &irqs); } if (fd >= rlim_cur) goto out; /* Do we need to expand the fd array or fd set? */ error = expand_files(files, fd); if (error < 0) goto out; if (error) { /* * If we needed to expand the fs array we * might have blocked - try again. */ error = -EMFILE; goto repeat; } __set_open_fd(fd, fdt); if (flags & O_CLOEXEC) __set_close_on_exec(fd, fdt); else __clear_close_on_exec(fd, fdt); files->next_fd = fd + 1; #if 1 /* Sanity check */ if (fdt->fd[fd] != NULL) { printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd); fdt->fd[fd] = NULL; } #endif error = fd; out: spin_unlock(&files->file_lock); return error; }