Skip to content
Snippets Groups Projects
namespace.c 54.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out0;
    	error = -EINVAL;
    
    	if (!check_mnt(new_nd.path.mnt))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out1;
    
    
    Ram Pai's avatar
    Ram Pai committed
    	error = __user_walk(put_old, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old_nd);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (error)
    		goto out1;
    
    	error = security_sb_pivotroot(&old_nd, &new_nd);
    	if (error) {
    
    Jan Blunck's avatar
    Jan Blunck committed
    		path_put(&old_nd.path);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out1;
    	}
    
    	read_lock(&current->fs->lock);
    
    Jan Blunck's avatar
    Jan Blunck committed
    	user_nd.path = current->fs->root;
    	path_get(&current->fs->root);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	read_unlock(&current->fs->lock);
    
    	down_write(&namespace_sem);
    
    	mutex_lock(&old_nd.path.dentry->d_inode->i_mutex);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	error = -EINVAL;
    
    	if (IS_MNT_SHARED(old_nd.path.mnt) ||
    		IS_MNT_SHARED(new_nd.path.mnt->mnt_parent) ||
    		IS_MNT_SHARED(user_nd.path.mnt->mnt_parent))
    
    		goto out2;
    
    	if (!check_mnt(user_nd.path.mnt))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out2;
    	error = -ENOENT;
    
    	if (IS_DEADDIR(new_nd.path.dentry->d_inode))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out2;
    
    	if (d_unhashed(new_nd.path.dentry) && !IS_ROOT(new_nd.path.dentry))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out2;
    
    	if (d_unhashed(old_nd.path.dentry) && !IS_ROOT(old_nd.path.dentry))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out2;
    	error = -EBUSY;
    
    	if (new_nd.path.mnt == user_nd.path.mnt ||
    	    old_nd.path.mnt == user_nd.path.mnt)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out2; /* loop, on the same file system  */
    	error = -EINVAL;
    
    	if (user_nd.path.mnt->mnt_root != user_nd.path.dentry)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out2; /* not a mountpoint */
    
    	if (user_nd.path.mnt->mnt_parent == user_nd.path.mnt)
    
    		goto out2; /* not attached */
    
    	if (new_nd.path.mnt->mnt_root != new_nd.path.dentry)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out2; /* not a mountpoint */
    
    	if (new_nd.path.mnt->mnt_parent == new_nd.path.mnt)
    
    		goto out2; /* not attached */
    
    	/* make sure we can reach put_old from new_root */
    	tmp = old_nd.path.mnt;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	spin_lock(&vfsmount_lock);
    
    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_nd.path.mnt)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				break;
    			tmp = tmp->mnt_parent;
    		}
    
    		if (!is_subdir(tmp->mnt_mountpoint, new_nd.path.dentry))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			goto out3;
    
    	} else if (!is_subdir(old_nd.path.dentry, new_nd.path.dentry))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out3;
    
    	detach_mnt(new_nd.path.mnt, &parent_path);
    
    	detach_mnt(user_nd.path.mnt, &root_parent);
    	/* mount old root on put_old */
    
    	attach_mnt(user_nd.path.mnt, &old_nd.path);
    
    	/* mount new_root on / */
    	attach_mnt(new_nd.path.mnt, &root_parent);
    
    	touch_mnt_namespace(current->nsproxy->mnt_ns);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	spin_unlock(&vfsmount_lock);
    
    	chroot_fs_refs(&user_nd.path, &new_nd.path);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	security_sb_post_pivotroot(&user_nd, &new_nd);
    	error = 0;
    
    	path_put(&root_parent);
    	path_put(&parent_path);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out2:
    
    	mutex_unlock(&old_nd.path.dentry->d_inode->i_mutex);
    
    	up_write(&namespace_sem);
    
    Jan Blunck's avatar
    Jan Blunck committed
    	path_put(&user_nd.path);
    	path_put(&old_nd.path);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out1:
    
    Jan Blunck's avatar
    Jan Blunck committed
    	path_put(&new_nd.path);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out0:
    	unlock_kernel();
    	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",
    			__FUNCTION__, err);
    
    	fs_kobj = kobject_create_and_add("fs", NULL);
    	if (!fs_kobj)
    		printk(KERN_WARNING "%s: kobj create error\n", __FUNCTION__);
    
    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
    }