📄 namespace.c
字号:
up_write(&sb->s_umount); return err;}static int do_move_mount(struct nameidata *nd, char *old_name){ struct nameidata old_nd, parent_nd; struct vfsmount *p; int err = 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (!old_name || !*old_name) return -EINVAL; if (path_init(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd)) err = path_walk(old_name, &old_nd); if (err) return err; down(&mount_sem); while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry)) ; err = -EINVAL; if (!check_mnt(nd->mnt) || !check_mnt(old_nd.mnt)) goto out; err = -ENOENT; down(&nd->dentry->d_inode->i_zombie); if (IS_DEADDIR(nd->dentry->d_inode)) goto out1; spin_lock(&dcache_lock); if (!IS_ROOT(nd->dentry) && d_unhashed(nd->dentry)) goto out2; err = -EINVAL; if (old_nd.dentry != old_nd.mnt->mnt_root) goto out2; if (old_nd.mnt == old_nd.mnt->mnt_parent) goto out2; if (S_ISDIR(nd->dentry->d_inode->i_mode) != S_ISDIR(old_nd.dentry->d_inode->i_mode)) goto out2; err = -ELOOP; for (p = nd->mnt; p->mnt_parent!=p; p = p->mnt_parent) if (p == old_nd.mnt) goto out2; err = 0; detach_mnt(old_nd.mnt, &parent_nd); attach_mnt(old_nd.mnt, nd);out2: spin_unlock(&dcache_lock);out1: up(&nd->dentry->d_inode->i_zombie);out: up(&mount_sem); if (!err) path_release(&parent_nd); path_release(&old_nd); return err;}static int do_add_mount(struct nameidata *nd, char *type, int flags, int mnt_flags, char *name, void *data){ struct vfsmount *mnt = do_kern_mount(type, flags, name, data); int err = PTR_ERR(mnt); if (IS_ERR(mnt)) goto out; down(&mount_sem); /* Something was mounted here while we slept */ while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry)) ; err = -EINVAL; if (!check_mnt(nd->mnt)) goto unlock; /* Refuse the same filesystem on the same mount point */ err = -EBUSY; if (nd->mnt->mnt_sb == mnt->mnt_sb && nd->mnt->mnt_root == nd->dentry) goto unlock; mnt->mnt_flags = mnt_flags; err = graft_tree(mnt, nd);unlock: up(&mount_sem); mntput(mnt);out: return err;}static int copy_mount_options (const void *data, unsigned long *where){ int i; unsigned long page; unsigned long size; *where = 0; if (!data) return 0; if (!(page = __get_free_page(GFP_KERNEL))) return -ENOMEM; /* We only care that *some* data at the address the user * gave us is valid. Just in case, we'll zero * the remainder of the page. */ /* copy_from_user cannot cross TASK_SIZE ! */ size = TASK_SIZE - (unsigned long)data; if (size > PAGE_SIZE) size = PAGE_SIZE; i = size - copy_from_user((void *)page, data, size); if (!i) { free_page(page); return -EFAULT; } if (i != PAGE_SIZE) memset((char *)page + i, 0, PAGE_SIZE - i); *where = page; return 0;}/* * Flags is a 32-bit value that allows up to 31 non-fs dependent flags to * be given to the mount() call (ie: read-only, no-dev, no-suid etc). * * data is a (void *) that can point to any structure up to * PAGE_SIZE-1 bytes, which can contain arbitrary fs-dependent * information (or be NULL). * * Pre-0.97 versions of mount() didn't have a flags word. * When the flags word was introduced its top half was required * to have the magic value 0xC0ED, and this remained so until 2.4.0-test9. * Therefore, if this magic number is present, it carries no information * and must be discarded. */long do_mount(char * dev_name, char * dir_name, char *type_page, unsigned long flags, void *data_page){ struct nameidata nd; int retval = 0; int mnt_flags = 0; /* Discard magic */ if ((flags & MS_MGC_MSK) == MS_MGC_VAL) flags &= ~MS_MGC_MSK; /* Basic sanity checks */ if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE)) return -EINVAL; if (dev_name && !memchr(dev_name, 0, PAGE_SIZE)) return -EINVAL; /* Separate the per-mountpoint flags */ if (flags & MS_NOSUID) mnt_flags |= MNT_NOSUID; if (flags & MS_NODEV) mnt_flags |= MNT_NODEV; if (flags & MS_NOEXEC) mnt_flags |= MNT_NOEXEC; flags &= ~(MS_NOSUID|MS_NOEXEC|MS_NODEV); /* ... and get the mountpoint */ if (path_init(dir_name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd)) retval = path_walk(dir_name, &nd); if (retval) return retval; if (flags & MS_REMOUNT) retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags, data_page); else if (flags & MS_BIND) retval = do_loopback(&nd, dev_name, flags & MS_REC); else if (flags & MS_MOVE) retval = do_move_mount(&nd, dev_name); else retval = do_add_mount(&nd, type_page, flags, mnt_flags, dev_name, data_page); path_release(&nd); return retval;}asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, unsigned long flags, void * data){ int retval; unsigned long data_page; unsigned long type_page; unsigned long dev_page; char *dir_page; retval = copy_mount_options (type, &type_page); if (retval < 0) return retval; dir_page = getname(dir_name); retval = PTR_ERR(dir_page); if (IS_ERR(dir_page)) goto out1; retval = copy_mount_options (dev_name, &dev_page); if (retval < 0) goto out2; retval = copy_mount_options (data, &data_page); if (retval < 0) goto out3; lock_kernel(); retval = do_mount((char*)dev_page, dir_page, (char*)type_page, flags, (void*)data_page); unlock_kernel(); free_page(data_page);out3: free_page(dev_page);out2: putname(dir_page);out1: free_page(type_page); return retval;}static void chroot_fs_refs(struct nameidata *old_nd, struct nameidata *new_nd){ struct task_struct *p; struct fs_struct *fs; read_lock(&tasklist_lock); for_each_task(p) { task_lock(p); fs = p->fs; if (fs) { atomic_inc(&fs->count); task_unlock(p); if (fs->root==old_nd->dentry&&fs->rootmnt==old_nd->mnt) set_fs_root(fs, new_nd->mnt, new_nd->dentry); if (fs->pwd==old_nd->dentry&&fs->pwdmnt==old_nd->mnt) set_fs_pwd(fs, new_nd->mnt, new_nd->dentry); put_fs_struct(fs); } else task_unlock(p); } read_unlock(&tasklist_lock);}/* * Moves the current root to put_root, and sets root/cwd of all processes * which had them on the old root to new_root. * * Note: * - 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. */asmlinkage long sys_pivot_root(const char *new_root, const char *put_old){ struct vfsmount *tmp; struct nameidata new_nd, old_nd, parent_nd, root_parent, user_nd; char *name; int error; if (!capable(CAP_SYS_ADMIN)) return -EPERM; lock_kernel(); name = getname(new_root); error = PTR_ERR(name); if (IS_ERR(name)) goto out0; error = 0; if (path_init(name, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &new_nd)) error = path_walk(name, &new_nd); putname(name); if (error) goto out0; error = -EINVAL; if (!check_mnt(new_nd.mnt)) goto out1; name = getname(put_old); error = PTR_ERR(name); if (IS_ERR(name)) goto out1; error = 0; if (path_init(name, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &old_nd)) error = path_walk(name, &old_nd); putname(name); if (error) goto out1; read_lock(¤t->fs->lock); user_nd.mnt = mntget(current->fs->rootmnt); user_nd.dentry = dget(current->fs->root); read_unlock(¤t->fs->lock); down(&mount_sem); down(&old_nd.dentry->d_inode->i_zombie); error = -EINVAL; if (!check_mnt(user_nd.mnt)) goto out2; error = -ENOENT; if (IS_DEADDIR(new_nd.dentry->d_inode)) goto out2; if (d_unhashed(new_nd.dentry) && !IS_ROOT(new_nd.dentry)) goto out2; if (d_unhashed(old_nd.dentry) && !IS_ROOT(old_nd.dentry)) goto out2; error = -EBUSY; if (new_nd.mnt == user_nd.mnt || old_nd.mnt == user_nd.mnt) goto out2; /* loop */ error = -EINVAL; if (user_nd.mnt->mnt_root != user_nd.dentry) goto out2; if (new_nd.mnt->mnt_root != new_nd.dentry) goto out2; /* not a mountpoint */ tmp = old_nd.mnt; /* make sure we can reach put_old from new_root */ spin_lock(&dcache_lock); if (tmp != new_nd.mnt) { for (;;) { if (tmp->mnt_parent == tmp) goto out3; if (tmp->mnt_parent == new_nd.mnt) break; tmp = tmp->mnt_parent; } if (!is_subdir(tmp->mnt_mountpoint, new_nd.dentry)) goto out3; } else if (!is_subdir(old_nd.dentry, new_nd.dentry)) goto out3; detach_mnt(new_nd.mnt, &parent_nd); detach_mnt(user_nd.mnt, &root_parent); attach_mnt(user_nd.mnt, &old_nd); attach_mnt(new_nd.mnt, &root_parent); spin_unlock(&dcache_lock); chroot_fs_refs(&user_nd, &new_nd); error = 0; path_release(&root_parent); path_release(&parent_nd);out2: up(&old_nd.dentry->d_inode->i_zombie); up(&mount_sem); path_release(&user_nd); path_release(&old_nd);out1: path_release(&new_nd);out0: unlock_kernel(); return error;out3: spin_unlock(&dcache_lock); goto out2;}/* * Absolutely minimal fake fs - only empty root directory and nothing else. * In 2.5 we'll use ramfs or tmpfs, but for now it's all we need - just * something to go with root vfsmount. */static struct dentry *rootfs_lookup(struct inode *dir, struct dentry *dentry){ d_add(dentry, NULL); return NULL;}static struct file_operations rootfs_dir_operations = { read: generic_read_dir, readdir: dcache_readdir,};static struct inode_operations rootfs_dir_inode_operations = { lookup: rootfs_lookup,};static struct super_block *rootfs_read_super(struct super_block * sb, void * data, int silent){ struct inode * inode; struct dentry * root; static struct super_operations s_ops = {}; sb->s_op = &s_ops; inode = new_inode(sb); if (!inode) return NULL; inode->i_mode = S_IFDIR|0555; inode->i_uid = inode->i_gid = 0; inode->i_op = &rootfs_dir_inode_operations; inode->i_fop = &rootfs_dir_operations; root = d_alloc_root(inode); if (!root) { iput(inode); return NULL; } sb->s_root = root; return sb;}static DECLARE_FSTYPE(root_fs_type, "rootfs", rootfs_read_super, FS_NOMOUNT);static void __init init_mount_tree(void){ register_filesystem(&root_fs_type); root_vfsmnt = do_kern_mount("rootfs", 0, "rootfs", NULL); if (IS_ERR(root_vfsmnt)) panic("can't allocate root vfsmount");}void __init mnt_init(unsigned long mempages){ struct list_head *d; unsigned long order; unsigned int nr_hash; int i; mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if (!mnt_cache) panic("Cannot create vfsmount cache"); mempages >>= (16 - PAGE_SHIFT); mempages *= sizeof(struct list_head); for (order = 0; ((1UL << order) << PAGE_SHIFT) < mempages; order++) ; do { mount_hashtable = (struct list_head *) __get_free_pages(GFP_ATOMIC, order); } while (mount_hashtable == NULL && --order >= 0); if (!mount_hashtable) panic("Failed to allocate mount hash table\n"); /* * Find the power-of-two list-heads that can fit into the allocation.. * We don't guarantee that "sizeof(struct list_head)" is necessarily * a power-of-two. */ nr_hash = (1UL << order) * PAGE_SIZE / sizeof(struct list_head); hash_bits = 0; do { hash_bits++; } while ((nr_hash >> hash_bits) != 0); hash_bits--; /* * Re-calculate the actual number of entries and the mask * from the number of bits we can fit. */ nr_hash = 1UL << hash_bits; hash_mask = nr_hash-1; printk("Mount-cache hash table entries: %d (order: %ld, %ld bytes)\n", nr_hash, order, (PAGE_SIZE << order)); /* And initialize the newly allocated array */ d = mount_hashtable; i = nr_hash; do { INIT_LIST_HEAD(d); d++; i--; } while (i); init_mount_tree();}#ifdef CONFIG_BLK_DEV_INITRDint __init change_root(kdev_t new_root_dev,const char *put_old){ struct vfsmount *old_rootmnt; struct nameidata devfs_nd, nd; struct nameidata parent_nd; char *new_devname = kmalloc(strlen("/dev/root.old")+1, GFP_KERNEL); int error = 0; if (new_devname) strcpy(new_devname, "/dev/root.old"); read_lock(¤t->fs->lock); old_rootmnt = mntget(current->fs->rootmnt); read_unlock(¤t->fs->lock); /* First unmount devfs if mounted */ if (path_init("/dev", LOOKUP_FOLLOW|LOOKUP_POSITIVE, &devfs_nd)) error = path_walk("/dev", &devfs_nd); if (!error) { if (devfs_nd.mnt->mnt_sb->s_magic == DEVFS_SUPER_MAGIC && devfs_nd.dentry == devfs_nd.mnt->mnt_root) { do_umount(devfs_nd.mnt, 0); } path_release(&devfs_nd); } spin_lock(&dcache_lock); detach_mnt(old_rootmnt, &parent_nd); spin_unlock(&dcache_lock); ROOT_DEV = new_root_dev; mount_root();#if 1 shrink_dcache(); printk("change_root: old root has d_count=%d\n", atomic_read(&old_rootmnt->mnt_root->d_count));#endif mount_devfs_fs (); /* * Get the new mount directory */ error = 0; if (path_init(put_old, LOOKUP_FOLLOW|LOOKUP_POSITIVE|LOOKUP_DIRECTORY, &nd)) error = path_walk(put_old, &nd); if (error) { int blivet; struct block_device *ramdisk = old_rootmnt->mnt_sb->s_bdev; atomic_inc(&ramdisk->bd_count); blivet = blkdev_get(ramdisk, FMODE_READ, 0, BDEV_FS); printk(KERN_NOTICE "Trying to unmount old root ... "); if (!blivet) { spin_lock(&dcache_lock); list_del_init(&old_rootmnt->mnt_list); spin_unlock(&dcache_lock); mntput(old_rootmnt); mntput(old_rootmnt); blivet = ioctl_by_bdev(ramdisk, BLKFLSBUF, 0); path_release(&parent_nd); blkdev_put(ramdisk, BDEV_FS); } if (blivet) { printk(KERN_ERR "error %d\n", blivet); } else { printk("okay\n"); error = 0; } kfree(new_devname); return error; } spin_lock(&dcache_lock); attach_mnt(old_rootmnt, &nd); if (new_devname) { if (old_rootmnt->mnt_devname) kfree(old_rootmnt->mnt_devname); old_rootmnt->mnt_devname = new_devname; } spin_unlock(&dcache_lock); /* put the old stuff */ path_release(&parent_nd); mntput(old_rootmnt); path_release(&nd); return 0;}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -