X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=fs%2Fnamespace.c;h=ce97becff4611c0fae4e90ce655bd8cffe850c77;hb=222f1d69183f10d70a37de5785698fe0aa363c12;hp=65f9c0ecc21c9c0a327ac5508602483dcecc8cf3;hpb=36341f64569b0c4572478237ec5ed318f0762510;p=linux-2.6 diff --git a/fs/namespace.c b/fs/namespace.c index 65f9c0ecc2..ce97becff4 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -24,11 +25,10 @@ #include #include #include +#include "pnode.h" extern int __init init_rootfs(void); -#define CL_EXPIRE 0x01 - #ifdef CONFIG_SYSFS extern int __init sysfs_init(void); #else @@ -46,6 +46,11 @@ static int event; static struct list_head *mount_hashtable; static int hash_mask __read_mostly, hash_bits __read_mostly; static kmem_cache_t *mnt_cache; +static struct rw_semaphore namespace_sem; + +/* /sys/fs */ +decl_subsys(fs, NULL, NULL); +EXPORT_SYMBOL_GPL(fs_subsys); static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) { @@ -66,6 +71,9 @@ struct vfsmount *alloc_vfsmnt(const char *name) INIT_LIST_HEAD(&mnt->mnt_mounts); INIT_LIST_HEAD(&mnt->mnt_list); INIT_LIST_HEAD(&mnt->mnt_expire); + INIT_LIST_HEAD(&mnt->mnt_share); + INIT_LIST_HEAD(&mnt->mnt_slave_list); + INIT_LIST_HEAD(&mnt->mnt_slave); if (name) { int size = strlen(name) + 1; char *newname = kmalloc(size, GFP_KERNEL); @@ -85,31 +93,44 @@ void free_vfsmnt(struct vfsmount *mnt) } /* - * Now, lookup_mnt increments the ref count before returning - * the vfsmount struct. + * find the first or last mount at @dentry on vfsmount @mnt depending on + * @dir. If @dir is set return the first mount else return the last mount. */ -struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry) +struct vfsmount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry, + int dir) { struct list_head *head = mount_hashtable + hash(mnt, dentry); struct list_head *tmp = head; struct vfsmount *p, *found = NULL; - spin_lock(&vfsmount_lock); for (;;) { - tmp = tmp->next; + tmp = dir ? tmp->next : tmp->prev; p = NULL; if (tmp == head) break; p = list_entry(tmp, struct vfsmount, mnt_hash); if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) { - found = mntget(p); + found = p; break; } } - spin_unlock(&vfsmount_lock); return found; } +/* + * lookup_mnt increments the ref count before returning + * the vfsmount struct. + */ +struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry) +{ + struct vfsmount *child_mnt; + spin_lock(&vfsmount_lock); + if ((child_mnt = __lookup_mnt(mnt, dentry, 1))) + mntget(child_mnt); + spin_unlock(&vfsmount_lock); + return child_mnt; +} + static inline int check_mnt(struct vfsmount *mnt) { return mnt->mnt_namespace == current->namespace; @@ -142,13 +163,43 @@ static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd) old_nd->dentry->d_mounted--; } +void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry, + struct vfsmount *child_mnt) +{ + child_mnt->mnt_parent = mntget(mnt); + child_mnt->mnt_mountpoint = dget(dentry); + dentry->d_mounted++; +} + static void attach_mnt(struct vfsmount *mnt, struct nameidata *nd) { - mnt->mnt_parent = mntget(nd->mnt); - mnt->mnt_mountpoint = dget(nd->dentry); - list_add(&mnt->mnt_hash, mount_hashtable + hash(nd->mnt, nd->dentry)); + mnt_set_mountpoint(nd->mnt, nd->dentry, mnt); + list_add_tail(&mnt->mnt_hash, mount_hashtable + + hash(nd->mnt, nd->dentry)); list_add_tail(&mnt->mnt_child, &nd->mnt->mnt_mounts); - nd->dentry->d_mounted++; +} + +/* + * the caller must hold vfsmount_lock + */ +static void commit_tree(struct vfsmount *mnt) +{ + struct vfsmount *parent = mnt->mnt_parent; + struct vfsmount *m; + LIST_HEAD(head); + struct namespace *n = parent->mnt_namespace; + + BUG_ON(parent == mnt); + + list_add_tail(&head, &mnt->mnt_list); + list_for_each_entry(m, &head, mnt_list) + m->mnt_namespace = n; + list_splice(&head, n->list.prev); + + list_add_tail(&mnt->mnt_hash, mount_hashtable + + hash(parent, mnt->mnt_mountpoint)); + list_add_tail(&mnt->mnt_child, &parent->mnt_mounts); + touch_namespace(n); } static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root) @@ -167,6 +218,16 @@ static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root) return list_entry(next, struct vfsmount, mnt_child); } +static struct vfsmount *skip_mnt_tree(struct vfsmount *p) +{ + struct list_head *prev = p->mnt_mounts.prev; + while (prev != &p->mnt_mounts) { + p = list_entry(prev, struct vfsmount, mnt_child); + prev = p->mnt_mounts.prev; + } + return p; +} + static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, int flag) { @@ -180,7 +241,20 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, mnt->mnt_root = dget(root); mnt->mnt_mountpoint = mnt->mnt_root; mnt->mnt_parent = mnt; - mnt->mnt_namespace = current->namespace; + + if (flag & CL_SLAVE) { + list_add(&mnt->mnt_slave, &old->mnt_slave_list); + mnt->mnt_master = old; + CLEAR_MNT_SHARED(mnt); + } else { + if ((flag & CL_PROPAGATION) || IS_MNT_SHARED(old)) + list_add(&mnt->mnt_share, &old->mnt_share); + if (IS_MNT_SLAVE(old)) + list_add(&mnt->mnt_slave, &old->mnt_slave); + mnt->mnt_master = old->mnt_master; + } + if (flag & CL_MAKE_SHARED) + set_mnt_shared(mnt); /* stick the duplicate mount on the same expiry list * as the original if that was on one */ @@ -250,7 +324,7 @@ static void *m_start(struct seq_file *m, loff_t *pos) struct list_head *p; loff_t l = *pos; - down_read(&n->sem); + down_read(&namespace_sem); list_for_each(p, &n->list) if (!l--) return list_entry(p, struct vfsmount, mnt_list); @@ -267,8 +341,7 @@ static void *m_next(struct seq_file *m, void *v, loff_t *pos) static void m_stop(struct seq_file *m, void *v) { - struct namespace *n = m->private; - up_read(&n->sem); + up_read(&namespace_sem); } static inline void mangle(struct seq_file *m, const char *s) @@ -287,14 +360,14 @@ static int show_vfsmnt(struct seq_file *m, void *v) { MS_SYNCHRONOUS, ",sync" }, { MS_DIRSYNC, ",dirsync" }, { MS_MANDLOCK, ",mand" }, - { MS_NOATIME, ",noatime" }, - { MS_NODIRATIME, ",nodiratime" }, { 0, NULL } }; static struct proc_fs_info mnt_info[] = { { MNT_NOSUID, ",nosuid" }, { MNT_NODEV, ",nodev" }, { MNT_NOEXEC, ",noexec" }, + { MNT_NOATIME, ",noatime" }, + { MNT_NODIRATIME, ",nodiratime" }, { 0, NULL } }; struct proc_fs_info *fs_infop; @@ -370,17 +443,20 @@ EXPORT_SYMBOL(may_umount_tree); */ int may_umount(struct vfsmount *mnt) { - if (atomic_read(&mnt->mnt_count) > 2) - return -EBUSY; - return 0; + int ret = 0; + spin_lock(&vfsmount_lock); + if (propagate_mount_busy(mnt, 2)) + ret = -EBUSY; + spin_unlock(&vfsmount_lock); + return ret; } EXPORT_SYMBOL(may_umount); -static void release_mounts(struct list_head *head) +void release_mounts(struct list_head *head) { struct vfsmount *mnt; - while(!list_empty(head)) { + while (!list_empty(head)) { mnt = list_entry(head->next, struct vfsmount, mnt_hash); list_del_init(&mnt->mnt_hash); if (mnt->mnt_parent != mnt) { @@ -399,7 +475,7 @@ static void release_mounts(struct list_head *head) } } -static void umount_tree(struct vfsmount *mnt, struct list_head *kill) +void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill) { struct vfsmount *p; @@ -408,6 +484,9 @@ static void umount_tree(struct vfsmount *mnt, struct list_head *kill) list_add(&p->mnt_hash, kill); } + if (propagate) + propagate_umount(kill); + list_for_each_entry(p, kill, mnt_hash) { list_del_init(&p->mnt_expire); list_del_init(&p->mnt_list); @@ -416,6 +495,7 @@ static void umount_tree(struct vfsmount *mnt, struct list_head *kill) list_del_init(&p->mnt_child); if (p->mnt_parent != p) mnt->mnt_mountpoint->d_mounted--; + change_mnt_propagation(p, MS_PRIVATE); } } @@ -487,20 +567,20 @@ static int do_umount(struct vfsmount *mnt, int flags) return retval; } - down_write(¤t->namespace->sem); + down_write(&namespace_sem); spin_lock(&vfsmount_lock); event++; retval = -EBUSY; - if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) { + if (flags & MNT_DETACH || !propagate_mount_busy(mnt, 2)) { if (!list_empty(&mnt->mnt_list)) - umount_tree(mnt, &umount_list); + umount_tree(mnt, 1, &umount_list); retval = 0; } spin_unlock(&vfsmount_lock); if (retval) security_sb_umount_busy(mnt); - up_write(¤t->namespace->sem); + up_write(&namespace_sem); release_mounts(&umount_list); return retval; } @@ -562,7 +642,7 @@ static int mount_is_safe(struct nameidata *nd) if (current->uid != nd->dentry->d_inode->i_uid) return -EPERM; } - if (permission(nd->dentry->d_inode, MAY_WRITE, nd)) + if (vfs_permission(nd, MAY_WRITE)) return -EPERM; return 0; #endif @@ -579,12 +659,15 @@ static int lives_below_in_same_fs(struct dentry *d, struct dentry *dentry) } } -static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry, +struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry, int flag) { struct vfsmount *res, *p, *q, *r, *s; struct nameidata nd; + if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt)) + return NULL; + res = q = clone_mnt(mnt, dentry, flag); if (!q) goto Enomem; @@ -596,6 +679,10 @@ static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry, continue; for (s = r; s; s = next_mnt(s, r)) { + if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(s)) { + s = skip_mnt_tree(s); + continue; + } while (p != s->mnt_parent) { p = p->mnt_parent; q = q->mnt_parent; @@ -617,13 +704,110 @@ Enomem: if (res) { LIST_HEAD(umount_list); spin_lock(&vfsmount_lock); - umount_tree(res, &umount_list); + umount_tree(res, 0, &umount_list); spin_unlock(&vfsmount_lock); release_mounts(&umount_list); } return NULL; } +/* + * @source_mnt : mount tree to be attached + * @nd : place the mount tree @source_mnt is attached + * @parent_nd : if non-null, detach the source_mnt from its parent and + * store the parent mount and mountpoint dentry. + * (done when source_mnt is moved) + * + * NOTE: in the table below explains the semantics when a source mount + * of a given type is attached to a destination mount of a given type. + * --------------------------------------------------------------------------- + * | BIND MOUNT OPERATION | + * |************************************************************************** + * | source-->| shared | private | slave | unbindable | + * | dest | | | | | + * | | | | | | | + * | v | | | | | + * |************************************************************************** + * | shared | shared (++) | shared (+) | shared(+++)| invalid | + * | | | | | | + * |non-shared| shared (+) | private | slave (*) | invalid | + * *************************************************************************** + * A bind operation clones the source mount and mounts the clone on the + * destination mount. + * + * (++) the cloned mount is propagated to all the mounts in the propagation + * tree of the destination mount and the cloned mount is added to + * the peer group of the source mount. + * (+) the cloned mount is created under the destination mount and is marked + * as shared. The cloned mount is added to the peer group of the source + * mount. + * (+++) the mount is propagated to all the mounts in the propagation tree + * of the destination mount and the cloned mount is made slave + * of the same master as that of the source mount. The cloned mount + * is marked as 'shared and slave'. + * (*) the cloned mount is made a slave of the same master as that of the + * source mount. + * + * --------------------------------------------------------------------------- + * | MOVE MOUNT OPERATION | + * |************************************************************************** + * | source-->| shared | private | slave | unbindable | + * | dest | | | | | + * | | | | | | | + * | v | | | | | + * |************************************************************************** + * | shared | shared (+) | shared (+) | shared(+++) | invalid | + * | | | | | | + * |non-shared| shared (+*) | private | slave (*) | unbindable | + * *************************************************************************** + * + * (+) the mount is moved to the destination. And is then propagated to + * all the mounts in the propagation tree of the destination mount. + * (+*) the mount is moved to the destination. + * (+++) the mount is moved to the destination and is then propagated to + * all the mounts belonging to the destination mount's propagation tree. + * the mount is marked as 'shared and slave'. + * (*) the mount continues to be a slave at the new location. + * + * if the source mount is a tree, the operations explained above is + * applied to each mount in the tree. + * Must be called without spinlocks held, since this function can sleep + * in allocations. + */ +static int attach_recursive_mnt(struct vfsmount *source_mnt, + struct nameidata *nd, struct nameidata *parent_nd) +{ + LIST_HEAD(tree_list); + struct vfsmount *dest_mnt = nd->mnt; + struct dentry *dest_dentry = nd->dentry; + struct vfsmount *child, *p; + + if (propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list)) + return -EINVAL; + + if (IS_MNT_SHARED(dest_mnt)) { + for (p = source_mnt; p; p = next_mnt(p, source_mnt)) + set_mnt_shared(p); + } + + spin_lock(&vfsmount_lock); + if (parent_nd) { + detach_mnt(source_mnt, parent_nd); + attach_mnt(source_mnt, nd); + touch_namespace(current->namespace); + } else { + mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt); + commit_tree(source_mnt); + } + + list_for_each_entry_safe(child, p, &tree_list, mnt_hash) { + list_del_init(&child->mnt_hash); + commit_tree(child); + } + spin_unlock(&vfsmount_lock); + return 0; +} + static int graft_tree(struct vfsmount *mnt, struct nameidata *nd) { int err; @@ -635,7 +819,7 @@ static int graft_tree(struct vfsmount *mnt, struct nameidata *nd) return -ENOTDIR; err = -ENOENT; - down(&nd->dentry->d_inode->i_sem); + mutex_lock(&nd->dentry->d_inode->i_mutex); if (IS_DEADDIR(nd->dentry->d_inode)) goto out_unlock; @@ -644,24 +828,36 @@ static int graft_tree(struct vfsmount *mnt, struct nameidata *nd) goto out_unlock; err = -ENOENT; - spin_lock(&vfsmount_lock); - if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry)) { - struct list_head head; - - attach_mnt(mnt, nd); - list_add_tail(&head, &mnt->mnt_list); - list_splice(&head, current->namespace->list.prev); - err = 0; - touch_namespace(current->namespace); - } - spin_unlock(&vfsmount_lock); + if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry)) + err = attach_recursive_mnt(mnt, nd, NULL); out_unlock: - up(&nd->dentry->d_inode->i_sem); + mutex_unlock(&nd->dentry->d_inode->i_mutex); if (!err) security_sb_post_addmount(mnt, nd); return err; } +/* + * recursively change the type of the mountpoint. + */ +static int do_change_type(struct nameidata *nd, int flag) +{ + struct vfsmount *m, *mnt = nd->mnt; + int recurse = flag & MS_REC; + int type = flag & ~MS_REC; + + if (nd->dentry != nd->mnt->mnt_root) + return -EINVAL; + + down_write(&namespace_sem); + spin_lock(&vfsmount_lock); + for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL)) + change_mnt_propagation(m, type); + spin_unlock(&vfsmount_lock); + up_write(&namespace_sem); + return 0; +} + /* * do loopback mount. */ @@ -678,8 +874,11 @@ static int do_loopback(struct nameidata *nd, char *old_name, int recurse) if (err) return err; - down_write(¤t->namespace->sem); + down_write(&namespace_sem); err = -EINVAL; + if (IS_MNT_UNBINDABLE(old_nd.mnt)) + goto out; + if (!check_mnt(nd->mnt) || !check_mnt(old_nd.mnt)) goto out; @@ -696,13 +895,13 @@ static int do_loopback(struct nameidata *nd, char *old_name, int recurse) if (err) { LIST_HEAD(umount_list); spin_lock(&vfsmount_lock); - umount_tree(mnt, &umount_list); + umount_tree(mnt, 0, &umount_list); spin_unlock(&vfsmount_lock); release_mounts(&umount_list); } out: - up_write(¤t->namespace->sem); + up_write(&namespace_sem); path_release(&old_nd); return err; } @@ -737,6 +936,16 @@ static int do_remount(struct nameidata *nd, int flags, int mnt_flags, return err; } +static inline int tree_contains_unbindable(struct vfsmount *mnt) +{ + struct vfsmount *p; + for (p = mnt; p; p = next_mnt(p, mnt)) { + if (IS_MNT_UNBINDABLE(p)) + return 1; + } + return 0; +} + static int do_move_mount(struct nameidata *nd, char *old_name) { struct nameidata old_nd, parent_nd; @@ -750,7 +959,7 @@ static int do_move_mount(struct nameidata *nd, char *old_name) if (err) return err; - down_write(¤t->namespace->sem); + down_write(&namespace_sem); while (d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry)) ; err = -EINVAL; @@ -758,44 +967,51 @@ static int do_move_mount(struct nameidata *nd, char *old_name) goto out; err = -ENOENT; - down(&nd->dentry->d_inode->i_sem); + mutex_lock(&nd->dentry->d_inode->i_mutex); if (IS_DEADDIR(nd->dentry->d_inode)) goto out1; - spin_lock(&vfsmount_lock); if (!IS_ROOT(nd->dentry) && d_unhashed(nd->dentry)) - goto out2; + goto out1; err = -EINVAL; if (old_nd.dentry != old_nd.mnt->mnt_root) - goto out2; + goto out1; if (old_nd.mnt == old_nd.mnt->mnt_parent) - goto out2; + goto out1; if (S_ISDIR(nd->dentry->d_inode->i_mode) != S_ISDIR(old_nd.dentry->d_inode->i_mode)) - goto out2; - + goto out1; + /* + * Don't move a mount residing in a shared parent. + */ + if (old_nd.mnt->mnt_parent && IS_MNT_SHARED(old_nd.mnt->mnt_parent)) + goto out1; + /* + * Don't move a mount tree containing unbindable mounts to a destination + * mount which is shared. + */ + if (IS_MNT_SHARED(nd->mnt) && tree_contains_unbindable(old_nd.mnt)) + goto out1; err = -ELOOP; for (p = nd->mnt; p->mnt_parent != p; p = p->mnt_parent) if (p == old_nd.mnt) - goto out2; - err = 0; + goto out1; - detach_mnt(old_nd.mnt, &parent_nd); - attach_mnt(old_nd.mnt, nd); - touch_namespace(current->namespace); + if ((err = attach_recursive_mnt(old_nd.mnt, nd, &parent_nd))) + goto out1; + spin_lock(&vfsmount_lock); /* if the mount is moved, it should no longer be expire * automatically */ list_del_init(&old_nd.mnt->mnt_expire); -out2: spin_unlock(&vfsmount_lock); out1: - up(&nd->dentry->d_inode->i_sem); + mutex_unlock(&nd->dentry->d_inode->i_mutex); out: - up_write(¤t->namespace->sem); + up_write(&namespace_sem); if (!err) path_release(&parent_nd); path_release(&old_nd); @@ -834,7 +1050,7 @@ int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd, { int err; - down_write(¤t->namespace->sem); + down_write(&namespace_sem); /* Something was mounted here while we slept */ while (d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry)) ; @@ -862,11 +1078,11 @@ int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd, list_add_tail(&newmnt->mnt_expire, fslist); spin_unlock(&vfsmount_lock); } - up_write(¤t->namespace->sem); + up_write(&namespace_sem); return 0; unlock: - up_write(¤t->namespace->sem); + up_write(&namespace_sem); mntput(newmnt); return err; } @@ -891,12 +1107,12 @@ static void expire_mount(struct vfsmount *mnt, struct list_head *mounts, * Check that it is still dead: the count should now be 2 - as * contributed by the vfsmount parent and the mntget above */ - if (atomic_read(&mnt->mnt_count) == 2) { + if (!propagate_mount_busy(mnt, 2)) { /* delete from the namespace */ touch_namespace(mnt->mnt_namespace); list_del_init(&mnt->mnt_list); mnt->mnt_namespace = NULL; - umount_tree(mnt, umounts); + umount_tree(mnt, 1, umounts); spin_unlock(&vfsmount_lock); } else { /* @@ -958,9 +1174,9 @@ void mark_mounts_for_expiry(struct list_head *mounts) get_namespace(namespace); spin_unlock(&vfsmount_lock); - down_write(&namespace->sem); + down_write(&namespace_sem); expire_mount(mnt, mounts, &umounts); - up_write(&namespace->sem); + up_write(&namespace_sem); release_mounts(&umounts); mntput(mnt); put_namespace(namespace); @@ -1075,7 +1291,13 @@ long do_mount(char *dev_name, char *dir_name, char *type_page, mnt_flags |= MNT_NODEV; if (flags & MS_NOEXEC) mnt_flags |= MNT_NOEXEC; - flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE); + if (flags & MS_NOATIME) + mnt_flags |= MNT_NOATIME; + if (flags & MS_NODIRATIME) + mnt_flags |= MNT_NODIRATIME; + + flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | + MS_NOATIME | MS_NODIRATIME); /* ... and get the mountpoint */ retval = path_lookup(dir_name, LOOKUP_FOLLOW, &nd); @@ -1091,6 +1313,8 @@ long do_mount(char *dev_name, char *dir_name, char *type_page, data_page); else if (flags & MS_BIND) retval = do_loopback(&nd, dev_name, flags & MS_REC); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) + retval = do_change_type(&nd, flags); else if (flags & MS_MOVE) retval = do_move_mount(&nd, dev_name); else @@ -1127,17 +1351,16 @@ int copy_namespace(int flags, struct task_struct *tsk) goto out; atomic_set(&new_ns->count, 1); - init_rwsem(&new_ns->sem); INIT_LIST_HEAD(&new_ns->list); init_waitqueue_head(&new_ns->poll); new_ns->event = 0; - down_write(&tsk->namespace->sem); + down_write(&namespace_sem); /* First pass: copy the tree topology */ new_ns->root = copy_tree(namespace->root, namespace->root->mnt_root, - CL_EXPIRE); + CL_COPY_ALL | CL_EXPIRE); if (!new_ns->root) { - up_write(&tsk->namespace->sem); + up_write(&namespace_sem); kfree(new_ns); goto out; } @@ -1171,7 +1394,7 @@ int copy_namespace(int flags, struct task_struct *tsk) p = next_mnt(p, namespace->root); q = next_mnt(q, new_ns->root); } - up_write(&tsk->namespace->sem); + up_write(&namespace_sem); tsk->namespace = new_ns; @@ -1314,6 +1537,10 @@ static void chroot_fs_refs(struct nameidata *old_nd, struct nameidata *new_nd) * 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. + * * 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) @@ -1356,9 +1583,13 @@ asmlinkage long sys_pivot_root(const char __user * new_root, user_nd.mnt = mntget(current->fs->rootmnt); user_nd.dentry = dget(current->fs->root); read_unlock(¤t->fs->lock); - down_write(¤t->namespace->sem); - down(&old_nd.dentry->d_inode->i_sem); + down_write(&namespace_sem); + mutex_lock(&old_nd.dentry->d_inode->i_mutex); error = -EINVAL; + if (IS_MNT_SHARED(old_nd.mnt) || + IS_MNT_SHARED(new_nd.mnt->mnt_parent) || + IS_MNT_SHARED(user_nd.mnt->mnt_parent)) + goto out2; if (!check_mnt(user_nd.mnt)) goto out2; error = -ENOENT; @@ -1406,8 +1637,8 @@ asmlinkage long sys_pivot_root(const char __user * new_root, path_release(&root_parent); path_release(&parent_nd); out2: - up(&old_nd.dentry->d_inode->i_sem); - up_write(¤t->namespace->sem); + mutex_unlock(&old_nd.dentry->d_inode->i_mutex); + up_write(&namespace_sem); path_release(&user_nd); path_release(&old_nd); out1: @@ -1434,7 +1665,6 @@ static void __init init_mount_tree(void) panic("Can't allocate initial namespace"); atomic_set(&namespace->count, 1); INIT_LIST_HEAD(&namespace->list); - init_rwsem(&namespace->sem); init_waitqueue_head(&namespace->poll); namespace->event = 0; list_add(&mnt->mnt_list, &namespace->list); @@ -1459,6 +1689,8 @@ void __init mnt_init(unsigned long mempages) unsigned int nr_hash; int i; + init_rwsem(&namespace_sem); + mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount), 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL, NULL); @@ -1497,6 +1729,7 @@ void __init mnt_init(unsigned long mempages) i--; } while (i); sysfs_init(); + subsystem_register(&fs_subsys); init_rootfs(); init_mount_tree(); } @@ -1507,11 +1740,11 @@ void __put_namespace(struct namespace *namespace) LIST_HEAD(umount_list); namespace->root = NULL; spin_unlock(&vfsmount_lock); - down_write(&namespace->sem); + down_write(&namespace_sem); spin_lock(&vfsmount_lock); - umount_tree(root, &umount_list); + umount_tree(root, 0, &umount_list); spin_unlock(&vfsmount_lock); - up_write(&namespace->sem); + up_write(&namespace_sem); release_mounts(&umount_list); kfree(namespace); }