Skip to content
Snippets Groups Projects
namespace.c 58 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
	 * as belonging to new namespace.  We have already acquired a private
	 * fs_struct, so tsk->fs->lock is not needed.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
	q = new_ns->root;
	while (p) {
Linus Torvalds's avatar
Linus Torvalds committed
		if (fs) {
Jan Blunck's avatar
Jan Blunck committed
			if (p == fs->root.mnt) {
Linus Torvalds's avatar
Linus Torvalds committed
				rootmnt = p;
Jan Blunck's avatar
Jan Blunck committed
				fs->root.mnt = mntget(q);
Linus Torvalds's avatar
Linus Torvalds committed
			}
Jan Blunck's avatar
Jan Blunck committed
			if (p == fs->pwd.mnt) {
Linus Torvalds's avatar
Linus Torvalds committed
				pwdmnt = p;
Jan Blunck's avatar
Jan Blunck committed
				fs->pwd.mnt = mntget(q);
Linus Torvalds's avatar
Linus Torvalds committed
			}
		}
		p = next_mnt(p, mnt_ns->root);
Linus Torvalds's avatar
Linus Torvalds committed
		q = next_mnt(q, new_ns->root);
	}
	up_write(&namespace_sem);
Linus Torvalds's avatar
Linus Torvalds committed

	if (rootmnt)
		mntput(rootmnt);
	if (pwdmnt)
		mntput(pwdmnt);

struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
	struct mnt_namespace *new_ns;

	if (!(flags & CLONE_NEWNS))
	new_ns = dup_mnt_ns(ns, new_fs);
SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
		char __user *, type, unsigned long, flags, void __user *, data)
Linus Torvalds's avatar
Linus Torvalds committed
{
	int retval;
	unsigned long data_page;
	unsigned long type_page;
	unsigned long dev_page;
	char *dir_page;

Ram Pai's avatar
Ram Pai committed
	retval = copy_mount_options(type, &type_page);
Linus Torvalds's avatar
Linus Torvalds committed
	if (retval < 0)
		return retval;

	dir_page = getname(dir_name);
	retval = PTR_ERR(dir_page);
	if (IS_ERR(dir_page))
		goto out1;

Ram Pai's avatar
Ram Pai committed
	retval = copy_mount_options(dev_name, &dev_page);
Linus Torvalds's avatar
Linus Torvalds committed
	if (retval < 0)
		goto out2;

Ram Pai's avatar
Ram Pai committed
	retval = copy_mount_options(data, &data_page);
Linus Torvalds's avatar
Linus Torvalds committed
	if (retval < 0)
		goto out3;

	lock_kernel();
Ram Pai's avatar
Ram Pai committed
	retval = do_mount((char *)dev_page, dir_page, (char *)type_page,
			  flags, (void *)data_page);
Linus Torvalds's avatar
Linus Torvalds committed
	unlock_kernel();
	free_page(data_page);

out3:
	free_page(dev_page);
out2:
	putname(dir_page);
out1:
	free_page(type_page);
	return retval;
}

/*
 * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
 * It can block. Requires the big lock held.
 */
void set_fs_root(struct fs_struct *fs, struct path *path)
Linus Torvalds's avatar
Linus Torvalds committed
{
Jan Blunck's avatar
Jan Blunck committed
	struct path old_root;

Linus Torvalds's avatar
Linus Torvalds committed
	write_lock(&fs->lock);
	old_root = fs->root;
	fs->root = *path;
	path_get(path);
Linus Torvalds's avatar
Linus Torvalds committed
	write_unlock(&fs->lock);
Jan Blunck's avatar
Jan Blunck committed
	if (old_root.dentry)
		path_put(&old_root);
Linus Torvalds's avatar
Linus Torvalds committed
}

/*
 * Replace the fs->{pwdmnt,pwd} with {mnt,dentry}. Put the old values.
 * It can block. Requires the big lock held.
 */
void set_fs_pwd(struct fs_struct *fs, struct path *path)
Linus Torvalds's avatar
Linus Torvalds committed
{
Jan Blunck's avatar
Jan Blunck committed
	struct path old_pwd;
Linus Torvalds's avatar
Linus Torvalds committed

	write_lock(&fs->lock);
	old_pwd = fs->pwd;
	fs->pwd = *path;
	path_get(path);
Linus Torvalds's avatar
Linus Torvalds committed
	write_unlock(&fs->lock);

Jan Blunck's avatar
Jan Blunck committed
	if (old_pwd.dentry)
		path_put(&old_pwd);
static void chroot_fs_refs(struct path *old_root, struct path *new_root)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct task_struct *g, *p;
	struct fs_struct *fs;

	read_lock(&tasklist_lock);
	do_each_thread(g, p) {
		task_lock(p);
		fs = p->fs;
		if (fs) {
			atomic_inc(&fs->count);
			task_unlock(p);
			if (fs->root.dentry == old_root->dentry
			    && fs->root.mnt == old_root->mnt)
				set_fs_root(fs, new_root);
			if (fs->pwd.dentry == old_root->dentry
			    && fs->pwd.mnt == old_root->mnt)
				set_fs_pwd(fs, new_root);
Linus Torvalds's avatar
Linus Torvalds committed
			put_fs_struct(fs);
		} else
			task_unlock(p);
	} while_each_thread(g, p);
	read_unlock(&tasklist_lock);
}

/*
 * pivot_root Semantics:
 * Moves the root file system of the current process to the directory put_old,
 * makes new_root as the new root file system of the current process, and sets
 * root/cwd of all processes which had them on the current root to new_root.
 *
 * Restrictions:
 * The new_root and put_old must be directories, and  must not be on the
 * same file  system as the current process root. The put_old  must  be
 * underneath new_root,  i.e. adding a non-zero number of /.. to the string
 * pointed to by put_old must yield the same directory as new_root. No other
 * file system may be mounted on put_old. After all, new_root is a mountpoint.
 *
 * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem.
 * See Documentation/filesystems/ramfs-rootfs-initramfs.txt for alternatives
 * in this situation.
 *
Linus Torvalds's avatar
Linus Torvalds committed
 * Notes:
 *  - we don't move root/cwd if they are not at the root (reason: if something
 *    cared enough to change them, it's probably wrong to force them elsewhere)
 *  - it's okay to pick a root that isn't the root of a file system, e.g.
 *    /nfs/my_root where /nfs is the mount point. It must be a mountpoint,
 *    though, so you may need to say mount --bind /nfs/my_root /nfs/my_root
 *    first.
 */
Ram Pai's avatar
Ram Pai committed
asmlinkage long sys_pivot_root(const char __user * new_root,
			       const char __user * put_old)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct vfsmount *tmp;
	struct path new, old, parent_path, root_parent, root;
Linus Torvalds's avatar
Linus Torvalds committed
	int error;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

	error = user_path_dir(new_root, &new);
Linus Torvalds's avatar
Linus Torvalds committed
	if (error)
		goto out0;
	error = -EINVAL;
	if (!check_mnt(new.mnt))
Linus Torvalds's avatar
Linus Torvalds committed
		goto out1;

	error = user_path_dir(put_old, &old);
Linus Torvalds's avatar
Linus Torvalds committed
	if (error)
		goto out1;

	error = security_sb_pivotroot(&old, &new);
Linus Torvalds's avatar
Linus Torvalds committed
	if (error) {
		path_put(&old);
Linus Torvalds's avatar
Linus Torvalds committed
		goto out1;
	}

	read_lock(&current->fs->lock);
	root = current->fs->root;
Jan Blunck's avatar
Jan Blunck committed
	path_get(&current->fs->root);
Linus Torvalds's avatar
Linus Torvalds committed
	read_unlock(&current->fs->lock);
	down_write(&namespace_sem);
	mutex_lock(&old.dentry->d_inode->i_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
	error = -EINVAL;
	if (IS_MNT_SHARED(old.mnt) ||
		IS_MNT_SHARED(new.mnt->mnt_parent) ||
		IS_MNT_SHARED(root.mnt->mnt_parent))
		goto out2;
	if (!check_mnt(root.mnt))
Linus Torvalds's avatar
Linus Torvalds committed
		goto out2;
	error = -ENOENT;
	if (IS_DEADDIR(new.dentry->d_inode))
Linus Torvalds's avatar
Linus Torvalds committed
		goto out2;
	if (d_unhashed(new.dentry) && !IS_ROOT(new.dentry))
Linus Torvalds's avatar
Linus Torvalds committed
		goto out2;
	if (d_unhashed(old.dentry) && !IS_ROOT(old.dentry))
Linus Torvalds's avatar
Linus Torvalds committed
		goto out2;
	error = -EBUSY;
	if (new.mnt == root.mnt ||
	    old.mnt == root.mnt)
Linus Torvalds's avatar
Linus Torvalds committed
		goto out2; /* loop, on the same file system  */
	error = -EINVAL;
	if (root.mnt->mnt_root != root.dentry)
Linus Torvalds's avatar
Linus Torvalds committed
		goto out2; /* not a mountpoint */
	if (root.mnt->mnt_parent == root.mnt)
		goto out2; /* not attached */
	if (new.mnt->mnt_root != new.dentry)
Linus Torvalds's avatar
Linus Torvalds committed
		goto out2; /* not a mountpoint */
	if (new.mnt->mnt_parent == new.mnt)
		goto out2; /* not attached */
	/* make sure we can reach put_old from new_root */
	tmp = old.mnt;
Linus Torvalds's avatar
Linus Torvalds committed
	spin_lock(&vfsmount_lock);
	if (tmp != new.mnt) {
Linus Torvalds's avatar
Linus Torvalds committed
		for (;;) {
			if (tmp->mnt_parent == tmp)
				goto out3; /* already mounted on put_old */
			if (tmp->mnt_parent == new.mnt)
Linus Torvalds's avatar
Linus Torvalds committed
				break;
			tmp = tmp->mnt_parent;
		}
		if (!is_subdir(tmp->mnt_mountpoint, new.dentry))
Linus Torvalds's avatar
Linus Torvalds committed
			goto out3;
	} else if (!is_subdir(old.dentry, new.dentry))
Linus Torvalds's avatar
Linus Torvalds committed
		goto out3;
	detach_mnt(new.mnt, &parent_path);
	detach_mnt(root.mnt, &root_parent);
	/* mount old root on put_old */
	attach_mnt(root.mnt, &old);
	attach_mnt(new.mnt, &root_parent);
	touch_mnt_namespace(current->nsproxy->mnt_ns);
Linus Torvalds's avatar
Linus Torvalds committed
	spin_unlock(&vfsmount_lock);
	chroot_fs_refs(&root, &new);
	security_sb_post_pivotroot(&root, &new);
Linus Torvalds's avatar
Linus Torvalds committed
	error = 0;
	path_put(&root_parent);
	path_put(&parent_path);
Linus Torvalds's avatar
Linus Torvalds committed
out2:
	mutex_unlock(&old.dentry->d_inode->i_mutex);
	up_write(&namespace_sem);
	path_put(&old);
Linus Torvalds's avatar
Linus Torvalds committed
out1:
	path_put(&new);
Linus Torvalds's avatar
Linus Torvalds committed
out0:
	return error;
out3:
	spin_unlock(&vfsmount_lock);
	goto out2;
}

static void __init init_mount_tree(void)
{
	struct vfsmount *mnt;
	struct mnt_namespace *ns;
	struct path root;
Linus Torvalds's avatar
Linus Torvalds committed

	mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
	if (IS_ERR(mnt))
		panic("Can't create rootfs");
	ns = kmalloc(sizeof(*ns), GFP_KERNEL);
	if (!ns)
Linus Torvalds's avatar
Linus Torvalds committed
		panic("Can't allocate initial namespace");
	atomic_set(&ns->count, 1);
	INIT_LIST_HEAD(&ns->list);
	init_waitqueue_head(&ns->poll);
	ns->event = 0;
	list_add(&mnt->mnt_list, &ns->list);
	ns->root = mnt;
	mnt->mnt_ns = ns;

	init_task.nsproxy->mnt_ns = ns;
	get_mnt_ns(ns);

	root.mnt = ns->root;
	root.dentry = ns->root->mnt_root;

	set_fs_pwd(current->fs, &root);
	set_fs_root(current->fs, &root);
void __init mnt_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
{
	unsigned u;
Linus Torvalds's avatar
Linus Torvalds committed

	init_rwsem(&namespace_sem);

Linus Torvalds's avatar
Linus Torvalds committed
	mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),
			0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
Linus Torvalds's avatar
Linus Torvalds committed

Ram Pai's avatar
Ram Pai committed
	mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed

	if (!mount_hashtable)
		panic("Failed to allocate mount hash table\n");

	printk("Mount-cache hash table entries: %lu\n", HASH_SIZE);

	for (u = 0; u < HASH_SIZE; u++)
		INIT_LIST_HEAD(&mount_hashtable[u]);
Linus Torvalds's avatar
Linus Torvalds committed

	err = sysfs_init();
	if (err)
		printk(KERN_WARNING "%s: sysfs_init error: %d\n",
	fs_kobj = kobject_create_and_add("fs", NULL);
	if (!fs_kobj)
		printk(KERN_WARNING "%s: kobj create error\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
	init_rootfs();
	init_mount_tree();
}

void __put_mnt_ns(struct mnt_namespace *ns)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct vfsmount *root = ns->root;
	LIST_HEAD(umount_list);
	spin_unlock(&vfsmount_lock);
	down_write(&namespace_sem);
Linus Torvalds's avatar
Linus Torvalds committed
	spin_lock(&vfsmount_lock);
	umount_tree(root, 0, &umount_list);
Linus Torvalds's avatar
Linus Torvalds committed
	spin_unlock(&vfsmount_lock);
	up_write(&namespace_sem);
	release_mounts(&umount_list);
Linus Torvalds's avatar
Linus Torvalds committed
}