static void __free_fdtable(struct fdtable *fdt) { free_fdset(fdt->open_fds, fdt->max_fdset); free_fdset(fdt->close_on_exec, fdt->max_fdset); free_fd_array(fdt->fd, fdt->max_fds); kfree(fdt); }
void put_files_struct(struct files_struct *files) { if (atomic_dec_and_test(&files->count)) { close_files(files); /* * Free the fd and fdset arrays if we expanded them. */ if (files->fd != &files->fd_array[0]) free_fd_array(files->fd, files->max_fds); if (files->max_fdset > __FD_SETSIZE) { free_fdset(files->open_fds, files->max_fdset); free_fdset(files->close_on_exec, files->max_fdset); } kmem_cache_free(files_cachep, files); } }
static inline void __exit_files(struct task_struct *tsk) { struct files_struct * files = tsk->files; if (files) { tsk->files = NULL; if (atomic_dec_and_test(&files->count)) { close_files(files); /* * Free the fd and fdset arrays if we expanded them. */ if (files->fd != &files->fd_array[0]) free_fd_array(files->fd, files->max_fds); if (files->max_fdset > __FD_SETSIZE) { free_fdset(files->open_fds, files->max_fdset); free_fdset(files->close_on_exec, files->max_fdset); } kmem_cache_free(files_cachep, files); } } }
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; }
int expand_fd_array(struct files_struct *files, int nr) { struct file **new_fds; int error, nfds; error = -EMFILE; if (files->max_fds >= NR_OPEN || nr >= NR_OPEN) goto out; nfds = files->max_fds; spin_unlock(&files->file_lock); /* * Expand to the max in easy steps, and keep expanding it until * we have enough for the requested fd array size. */ do { #if NR_OPEN_DEFAULT < 256 if (nfds < 256) nfds = 256; else #endif if (nfds < (PAGE_SIZE / sizeof(struct file *))) nfds = PAGE_SIZE / sizeof(struct file *); else { nfds = nfds * 2; if (nfds > NR_OPEN) nfds = NR_OPEN; } } while (nfds <= nr); error = -ENOMEM; new_fds = alloc_fd_array(nfds); spin_lock(&files->file_lock); if (!new_fds) goto out; /* Copy the existing array and install the new pointer */ if (nfds > files->max_fds) { struct file **old_fds; int i; old_fds = xchg(&files->fd, new_fds); i = xchg(&files->max_fds, nfds); /* Don't copy/clear the array if we are creating a new fd array for fork() */ if (i) { memcpy(new_fds, old_fds, i * sizeof(struct file *)); /* clear the remainder of the array */ memset(&new_fds[i], 0, (nfds-i) * sizeof(struct file *)); spin_unlock(&files->file_lock); free_fd_array(old_fds, i); spin_lock(&files->file_lock); } } else { /* Somebody expanded the array while we slept ... */ spin_unlock(&files->file_lock); free_fd_array(new_fds, nfds); spin_lock(&files->file_lock); } error = 0; out: return error; }