1650 lines · 39.7 KB · C
RawDownload
	int retval; 	tsk->min_flt = tsk->maj_flt = 0;	tsk->nvcsw = tsk->nivcsw = 0; 	tsk->mm = NULL;	tsk->active_mm = NULL; 	/*	 * Are we cloning a kernel thread?	 *	 * We need to steal a active VM for that..	 */	oldmm = current->mm;	if (!oldmm)		return 0; 	if (clone_flags & CLONE_VM) {		atomic_inc(&oldmm->mm_users);		mm = oldmm;		goto good_mm;	} 	retval = -ENOMEM;	mm = dup_mm(tsk);	if (!mm)		goto fail_nomem; good_mm:	tsk->mm = mm;	tsk->active_mm = mm;	return 0; fail_nomem:	return retval;} static inline struct fs_struct *__copy_fs_struct(struct fs_struct *old){	struct fs_struct *fs = kmem_cache_alloc(fs_cachep, GFP_KERNEL);	/* We don't need to lock fs - think why ;-) */	if (fs) {		atomic_set(&fs->count, 1);		rwlock_init(&fs->lock);		fs->umask = old->umask;		read_lock(&old->lock);		fs->rootmnt = mntget(old->rootmnt);		fs->root = dget(old->root);		fs->pwdmnt = mntget(old->pwdmnt);		fs->pwd = dget(old->pwd);		if (old->altroot) {			fs->altrootmnt = mntget(old->altrootmnt);			fs->altroot = dget(old->altroot);		} else {			fs->altrootmnt = NULL;			fs->altroot = NULL;		}		read_unlock(&old->lock);	}	return fs;} struct fs_struct *copy_fs_struct(struct fs_struct *old){	return __copy_fs_struct(old);} EXPORT_SYMBOL_GPL(copy_fs_struct); static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk){	if (clone_flags & CLONE_FS) {		atomic_inc(&current->fs->count);		return 0;	}	tsk->fs = __copy_fs_struct(current->fs);	if (!tsk->fs)		return -ENOMEM;	return 0;} static int count_open_files(struct fdtable *fdt){	int size = fdt->max_fdset;	int i; 	/* Find the last open fd */	for (i = size/(8*sizeof(long)); i > 0; ) {		if (fdt->open_fds->fds_bits[--i])			break;	}	i = (i+1) * 8 * sizeof(long);	return i;} static struct files_struct *alloc_files(void){	struct files_struct *newf;	struct fdtable *fdt; 	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;	fdt = &newf->fdtab;	fdt->max_fds = NR_OPEN_DEFAULT;	fdt->max_fdset = EMBEDDED_FD_SET_SIZE;	fdt->close_on_exec = (fd_set *)&newf->close_on_exec_init;	fdt->open_fds = (fd_set *)&newf->open_fds_init;	fdt->fd = &newf->fd_array[0];	INIT_RCU_HEAD(&fdt->rcu);	fdt->free_files = NULL;	fdt->next = NULL;	rcu_assign_pointer(newf->fdt, fdt);out:	return newf;} /* * Allocate a new files structure and copy contents from the * passed in files structure. */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; 	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 copy_files(unsigned long clone_flags, struct task_struct * tsk){	struct files_struct *oldf, *newf;	int error = 0; 	/*	 * 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 = dup_fd(oldf, &error);	if (!newf)		goto out; 	tsk->files = newf;	error = 0;out:	return error;} /* *	Helper to unshare the files of the current task. *	We don't want to expose copy_files internals to *	the exec layer of the kernel. */ int unshare_files(void){	struct files_struct *files  = current->files;	int rc; 	BUG_ON(!files); 	/* This can race but the race causes us to copy when we don't	   need to and drop the copy */	if(atomic_read(&files->count) == 1)	{		atomic_inc(&files->count);		return 0;	}	rc = copy_files(0, current);	if(rc)		current->files = files;	return rc;} EXPORT_SYMBOL(unshare_files); static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk){	struct sighand_struct *sig; 	if (clone_flags & (CLONE_SIGHAND | CLONE_THREAD)) {		atomic_inc(&current->sighand->count);		return 0;	}	sig = kmem_cache_alloc(sighand_cachep, GFP_KERNEL);	rcu_assign_pointer(tsk->sighand, sig);	if (!sig)		return -ENOMEM;	atomic_set(&sig->count, 1);	memcpy(sig->action, current->sighand->action, sizeof(sig->action));	return 0;} void __cleanup_sighand(struct sighand_struct *sighand){	if (atomic_dec_and_test(&sighand->count))		kmem_cache_free(sighand_cachep, sighand);} static inline int copy_signal(unsigned long clone_flags, struct task_struct * tsk){	struct signal_struct *sig;	int ret; 	if (clone_flags & CLONE_THREAD) {		atomic_inc(&current->signal->count);		atomic_inc(&current->signal->live);		return 0;	}	sig = kmem_cache_alloc(signal_cachep, GFP_KERNEL);	tsk->signal = sig;	if (!sig)		return -ENOMEM; 	ret = copy_thread_group_keys(tsk);	if (ret < 0) {		kmem_cache_free(signal_cachep, sig);		return ret;	} 	atomic_set(&sig->count, 1);	atomic_set(&sig->live, 1);	init_waitqueue_head(&sig->wait_chldexit);	sig->flags = 0;	sig->group_exit_code = 0;	sig->group_exit_task = NULL;	sig->group_stop_count = 0;	sig->curr_target = NULL;	init_sigpending(&sig->shared_pending);	INIT_LIST_HEAD(&sig->posix_timers); 	hrtimer_init(&sig->real_timer, CLOCK_MONOTONIC, HRTIMER_REL);	sig->it_real_incr.tv64 = 0;	sig->real_timer.function = it_real_fn;	sig->tsk = tsk; 	sig->it_virt_expires = cputime_zero;	sig->it_virt_incr = cputime_zero;	sig->it_prof_expires = cputime_zero;	sig->it_prof_incr = cputime_zero; 	sig->leader = 0;	/* session leadership doesn't inherit */	sig->tty_old_pgrp = 0; 	sig->utime = sig->stime = sig->cutime = sig->cstime = cputime_zero;	sig->nvcsw = sig->nivcsw = sig->cnvcsw = sig->cnivcsw = 0;	sig->min_flt = sig->maj_flt = sig->cmin_flt = sig->cmaj_flt = 0;	sig->sched_time = 0;	INIT_LIST_HEAD(&sig->cpu_timers[0]);	INIT_LIST_HEAD(&sig->cpu_timers[1]);	INIT_LIST_HEAD(&sig->cpu_timers[2]); 	task_lock(current->group_leader);	memcpy(sig->rlim, current->signal->rlim, sizeof sig->rlim);	task_unlock(current->group_leader); 	if (sig->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) {		/*		 * New sole thread in the process gets an expiry time		 * of the whole CPU time limit.		 */		tsk->it_prof_expires =			secs_to_cputime(sig->rlim[RLIMIT_CPU].rlim_cur);	} 	return 0;} void __cleanup_signal(struct signal_struct *sig){	exit_thread_group_keys(sig);	kmem_cache_free(signal_cachep, sig);} static inline void cleanup_signal(struct task_struct *tsk){	struct signal_struct *sig = tsk->signal; 	atomic_dec(&sig->live); 	if (atomic_dec_and_test(&sig->count))		__cleanup_signal(sig);} static inline void copy_flags(unsigned long clone_flags, struct task_struct *p){	unsigned long new_flags = p->flags; 	new_flags &= ~(PF_SUPERPRIV | PF_NOFREEZE);	new_flags |= PF_FORKNOEXEC;	if (!(clone_flags & CLONE_PTRACE))		p->ptrace = 0;	p->flags = new_flags;} asmlinkage long sys_set_tid_address(int __user *tidptr){	current->clear_child_tid = tidptr; 	return current->pid;} /* * This creates a new process as a copy of the old one, * but does not actually start it yet. * * It copies the registers, and all the appropriate * parts of the process environment (as per the clone * flags). The actual kick-off is left to the caller. */static task_t *copy_process(unsigned long clone_flags,				 unsigned long stack_start,				 struct pt_regs *regs,				 unsigned long stack_size,				 int __user *parent_tidptr,				 int __user *child_tidptr,				 int pid){	int retval;	struct task_struct *p = NULL; 	if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))		return ERR_PTR(-EINVAL); 	/*	 * Thread groups must share signals as well, and detached threads	 * can only be started up within the thread group.	 */	if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))		return ERR_PTR(-EINVAL); 	/*	 * Shared signal handlers imply shared VM. By way of the above,	 * thread groups also imply shared VM. Blocking this case allows	 * for various simplifications in other code.	 */	if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))		return ERR_PTR(-EINVAL); 	retval = security_task_create(clone_flags);	if (retval)		goto fork_out; 	retval = -ENOMEM;	p = dup_task_struct(current);	if (!p)		goto fork_out; 	retval = -EAGAIN;	if (atomic_read(&p->user->processes) >=			p->signal->rlim[RLIMIT_NPROC].rlim_cur) {		if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&				p->user != &root_user)			goto bad_fork_free;	} 	atomic_inc(&p->user->__count);	atomic_inc(&p->user->processes);	get_group_info(p->group_info); 	/*	 * If multiple threads are within copy_process(), then this check	 * triggers too late. This doesn't hurt, the check is only there	 * to stop root fork bombs.	 */	if (nr_threads >= max_threads)		goto bad_fork_cleanup_count; 	if (!try_module_get(task_thread_info(p)->exec_domain->module))		goto bad_fork_cleanup_count; 	if (p->binfmt && !try_module_get(p->binfmt->module))		goto bad_fork_cleanup_put_domain; 	p->did_exec = 0;	copy_flags(clone_flags, p);	p->pid = pid;	retval = -EFAULT;	if (clone_flags & CLONE_PARENT_SETTID)		if (put_user(p->pid, parent_tidptr))			goto bad_fork_cleanup; 	p->proc_dentry = NULL; 	INIT_LIST_HEAD(&p->children);	INIT_LIST_HEAD(&p->sibling);	p->vfork_done = NULL;	spin_lock_init(&p->alloc_lock);	spin_lock_init(&p->proc_lock); 	clear_tsk_thread_flag(p, TIF_SIGPENDING);

Lines 501–1000 of 1650. Previous lines