Example #1
0
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);
}
Example #2
0
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);
	}
}
Example #3
0
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);
		}
	}
}
Example #4
0
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;
}
Example #5
0
/*
 * 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;
}
Example #6
0
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;
}