X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Fnamespace.c;h=61bf376e29e85bfda2456cb929c0a0eeb38af1b3;hb=2c6b8c030cfca334c3d700ee504036c585c4c6a3;hp=b168dc37eaabe23e37e04cbacc6d7098a5896d40;hpb=484e389c63472a7f8cfb491cf11b047364e59365;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/namespace.c b/fs/namespace.c index b168dc3..61bf376 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -8,62 +8,65 @@ * Heavily rewritten. */ -#include #include #include #include #include #include +#include #include #include +#include #include +#include #include -#include +#include #include #include #include +#include #include #include +#include "pnode.h" +#include "internal.h" -extern int __init init_rootfs(void); +/* spinlock for vfsmount related operations, inplace of dcache_lock */ +__cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); -#ifdef CONFIG_SYSFS -extern int __init sysfs_init(void); -#else -static inline int sysfs_init(void) -{ - return 0; -} -#endif +static int event; -/* spinlock for vfsmount related operations, inplace of dcache_lock */ - __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); +static struct list_head *mount_hashtable __read_mostly; +static int hash_mask __read_mostly, hash_bits __read_mostly; +static struct kmem_cache *mnt_cache __read_mostly; +static struct rw_semaphore namespace_sem; -static struct list_head *mount_hashtable; -static int hash_mask, hash_bits; -static kmem_cache_t *mnt_cache; +/* /sys/fs */ +struct kobject *fs_kobj; +EXPORT_SYMBOL_GPL(fs_kobj); static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) { - unsigned long tmp = ((unsigned long) mnt / L1_CACHE_BYTES); - tmp += ((unsigned long) dentry / L1_CACHE_BYTES); + unsigned long tmp = ((unsigned long)mnt / L1_CACHE_BYTES); + tmp += ((unsigned long)dentry / L1_CACHE_BYTES); tmp = tmp + (tmp >> hash_bits); return tmp & hash_mask; } struct vfsmount *alloc_vfsmnt(const char *name) { - struct vfsmount *mnt = kmem_cache_alloc(mnt_cache, GFP_KERNEL); + struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); if (mnt) { - memset(mnt, 0, sizeof(struct vfsmount)); - atomic_set(&mnt->mnt_count,1); + atomic_set(&mnt->mnt_count, 1); INIT_LIST_HEAD(&mnt->mnt_hash); INIT_LIST_HEAD(&mnt->mnt_child); INIT_LIST_HEAD(&mnt->mnt_mounts); INIT_LIST_HEAD(&mnt->mnt_list); - INIT_LIST_HEAD(&mnt->mnt_fslink); + 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; + int size = strlen(name) + 1; char *newname = kmalloc(size, GFP_KERNEL); if (newname) { memcpy(newname, name, size); @@ -74,6 +77,15 @@ struct vfsmount *alloc_vfsmnt(const char *name) return mnt; } +int simple_set_mnt(struct vfsmount *mnt, struct super_block *sb) +{ + mnt->mnt_sb = sb; + mnt->mnt_root = dget(sb->s_root); + return 0; +} + +EXPORT_SYMBOL(simple_set_mnt); + void free_vfsmnt(struct vfsmount *mnt) { kfree(mnt->mnt_devname); @@ -81,34 +93,63 @@ 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 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; + return mnt->mnt_ns == current->nsproxy->mnt_ns; +} + +static void touch_mnt_namespace(struct mnt_namespace *ns) +{ + if (ns) { + ns->event = ++event; + wake_up_interruptible(&ns->poll); + } +} + +static void __touch_mnt_namespace(struct mnt_namespace *ns) +{ + if (ns && ns->event != event) { + ns->event = event; + wake_up_interruptible(&ns->poll); + } } static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd) @@ -122,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 mnt_namespace *n = parent->mnt_ns; + + BUG_ON(parent == mnt); + + list_add_tail(&head, &mnt->mnt_list); + list_for_each_entry(m, &head, mnt_list) + m->mnt_ns = 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_mnt_namespace(n); } static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root) @@ -147,8 +218,18 @@ static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root) return list_entry(next, struct vfsmount, mnt_child); } -static struct vfsmount * -clone_mnt(struct vfsmount *old, struct dentry *root) +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) { struct super_block *sb = old->mnt_sb; struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname); @@ -160,19 +241,34 @@ 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 = old->mnt_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_PRIVATE)) { + 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 */ - spin_lock(&vfsmount_lock); - if (!list_empty(&old->mnt_fslink)) - list_add(&mnt->mnt_fslink, &old->mnt_fslink); - spin_unlock(&vfsmount_lock); + if (flag & CL_EXPIRE) { + spin_lock(&vfsmount_lock); + if (!list_empty(&old->mnt_expire)) + list_add(&mnt->mnt_expire, &old->mnt_expire); + spin_unlock(&vfsmount_lock); + } } return mnt; } -void __mntput(struct vfsmount *mnt) +static inline void __mntput(struct vfsmount *mnt) { struct super_block *sb = mnt->mnt_sb; dput(mnt->mnt_root); @@ -180,34 +276,66 @@ void __mntput(struct vfsmount *mnt) deactivate_super(sb); } -EXPORT_SYMBOL(__mntput); +void mntput_no_expire(struct vfsmount *mnt) +{ +repeat: + if (atomic_dec_and_lock(&mnt->mnt_count, &vfsmount_lock)) { + if (likely(!mnt->mnt_pinned)) { + spin_unlock(&vfsmount_lock); + __mntput(mnt); + return; + } + atomic_add(mnt->mnt_pinned + 1, &mnt->mnt_count); + mnt->mnt_pinned = 0; + spin_unlock(&vfsmount_lock); + acct_auto_close_mnt(mnt); + security_sb_umount_close(mnt); + goto repeat; + } +} + +EXPORT_SYMBOL(mntput_no_expire); + +void mnt_pin(struct vfsmount *mnt) +{ + spin_lock(&vfsmount_lock); + mnt->mnt_pinned++; + spin_unlock(&vfsmount_lock); +} + +EXPORT_SYMBOL(mnt_pin); + +void mnt_unpin(struct vfsmount *mnt) +{ + spin_lock(&vfsmount_lock); + if (mnt->mnt_pinned) { + atomic_inc(&mnt->mnt_count); + mnt->mnt_pinned--; + } + spin_unlock(&vfsmount_lock); +} + +EXPORT_SYMBOL(mnt_unpin); /* iterator */ static void *m_start(struct seq_file *m, loff_t *pos) { - struct namespace *n = m->private; - struct list_head *p; - loff_t l = *pos; + struct mnt_namespace *n = m->private; - down_read(&n->sem); - list_for_each(p, &n->list) - if (!l--) - return list_entry(p, struct vfsmount, mnt_list); - return NULL; + down_read(&namespace_sem); + return seq_list_start(&n->list, *pos); } static void *m_next(struct seq_file *m, void *v, loff_t *pos) { - struct namespace *n = m->private; - struct list_head *p = ((struct vfsmount *)v)->mnt_list.next; - (*pos)++; - return p==&n->list ? NULL : list_entry(p, struct vfsmount, mnt_list); + struct mnt_namespace *n = m->private; + + return seq_list_next(v, &n->list, 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) @@ -217,7 +345,7 @@ static inline void mangle(struct seq_file *m, const char *s) static int show_vfsmnt(struct seq_file *m, void *v) { - struct vfsmount *mnt = v; + struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); int err = 0; static struct proc_fs_info { int flag; @@ -226,14 +354,15 @@ 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" }, + { MNT_RELATIME, ",relatime" }, { 0, NULL } }; struct proc_fs_info *fs_infop; @@ -243,6 +372,10 @@ static int show_vfsmnt(struct seq_file *m, void *v) seq_path(m, mnt, mnt->mnt_root, " \t\n\\"); seq_putc(m, ' '); mangle(m, mnt->mnt_sb->s_type->name); + if (mnt->mnt_sb->s_subtype && mnt->mnt_sb->s_subtype[0]) { + seq_putc(m, '.'); + mangle(m, mnt->mnt_sb->s_subtype); + } seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? " ro" : " rw"); for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { if (mnt->mnt_sb->s_flags & fs_infop->flag) @@ -265,6 +398,44 @@ struct seq_operations mounts_op = { .show = show_vfsmnt }; +static int show_vfsstat(struct seq_file *m, void *v) +{ + struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); + int err = 0; + + /* device */ + if (mnt->mnt_devname) { + seq_puts(m, "device "); + mangle(m, mnt->mnt_devname); + } else + seq_puts(m, "no device"); + + /* mount point */ + seq_puts(m, " mounted on "); + seq_path(m, mnt, mnt->mnt_root, " \t\n\\"); + seq_putc(m, ' '); + + /* file system type */ + seq_puts(m, "with fstype "); + mangle(m, mnt->mnt_sb->s_type->name); + + /* optional statistics */ + if (mnt->mnt_sb->s_op->show_stats) { + seq_putc(m, ' '); + err = mnt->mnt_sb->s_op->show_stats(m, mnt); + } + + seq_putc(m, '\n'); + return err; +} + +struct seq_operations mountstats_op = { + .start = m_start, + .next = m_next, + .stop = m_stop, + .show = show_vfsstat, +}; + /** * may_umount_tree - check if a mount tree is busy * @mnt: root of mount tree @@ -275,42 +446,21 @@ struct seq_operations mounts_op = { */ int may_umount_tree(struct vfsmount *mnt) { - struct list_head *next; - struct vfsmount *this_parent = mnt; - int actual_refs; - int minimum_refs; + int actual_refs = 0; + int minimum_refs = 0; + struct vfsmount *p; spin_lock(&vfsmount_lock); - actual_refs = atomic_read(&mnt->mnt_count); - minimum_refs = 2; -repeat: - next = this_parent->mnt_mounts.next; -resume: - while (next != &this_parent->mnt_mounts) { - struct vfsmount *p = list_entry(next, struct vfsmount, mnt_child); - - next = next->next; - + for (p = mnt; p; p = next_mnt(p, mnt)) { actual_refs += atomic_read(&p->mnt_count); minimum_refs += 2; - - if (!list_empty(&p->mnt_mounts)) { - this_parent = p; - goto repeat; - } - } - - if (this_parent != mnt) { - next = this_parent->mnt_child.next; - this_parent = this_parent->mnt_parent; - goto resume; } spin_unlock(&vfsmount_lock); if (actual_refs > minimum_refs) - return -EBUSY; + return 0; - return 0; + return 1; } EXPORT_SYMBOL(may_umount_tree); @@ -330,45 +480,65 @@ EXPORT_SYMBOL(may_umount_tree); */ int may_umount(struct vfsmount *mnt) { - if (atomic_read(&mnt->mnt_count) > 2) - return -EBUSY; - return 0; + int ret = 1; + spin_lock(&vfsmount_lock); + if (propagate_mount_busy(mnt, 2)) + ret = 0; + spin_unlock(&vfsmount_lock); + return ret; } EXPORT_SYMBOL(may_umount); -static void umount_tree(struct vfsmount *mnt) +void release_mounts(struct list_head *head) { - struct vfsmount *p; - LIST_HEAD(kill); - - for (p = mnt; p; p = next_mnt(p, mnt)) { - list_del(&p->mnt_list); - list_add(&p->mnt_list, &kill); - p->mnt_namespace = NULL; - } - - while (!list_empty(&kill)) { - mnt = list_entry(kill.next, struct vfsmount, mnt_list); - list_del_init(&mnt->mnt_list); - list_del_init(&mnt->mnt_fslink); - if (mnt->mnt_parent == mnt) { - spin_unlock(&vfsmount_lock); - } else { - struct nameidata old_nd; - detach_mnt(mnt, &old_nd); + struct vfsmount *mnt; + while (!list_empty(head)) { + mnt = list_first_entry(head, struct vfsmount, mnt_hash); + list_del_init(&mnt->mnt_hash); + if (mnt->mnt_parent != mnt) { + struct dentry *dentry; + struct vfsmount *m; + spin_lock(&vfsmount_lock); + dentry = mnt->mnt_mountpoint; + m = mnt->mnt_parent; + mnt->mnt_mountpoint = mnt->mnt_root; + mnt->mnt_parent = mnt; spin_unlock(&vfsmount_lock); - path_release(&old_nd); + dput(dentry); + mntput(m); } mntput(mnt); - spin_lock(&vfsmount_lock); + } +} + +void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill) +{ + struct vfsmount *p; + + for (p = mnt; p; p = next_mnt(p, mnt)) + list_move(&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); + __touch_mnt_namespace(p->mnt_ns); + p->mnt_ns = NULL; + list_del_init(&p->mnt_child); + if (p->mnt_parent != p) + p->mnt_mountpoint->d_mounted--; + change_mnt_propagation(p, MS_PRIVATE); } } static int do_umount(struct vfsmount *mnt, int flags) { - struct super_block * sb = mnt->mnt_sb; + struct super_block *sb = mnt->mnt_sb; int retval; + LIST_HEAD(umount_list); retval = security_sb_umount(mnt, flags); if (retval) @@ -403,8 +573,8 @@ static int do_umount(struct vfsmount *mnt, int flags) */ lock_kernel(); - if( (flags&MNT_FORCE) && sb->s_op->umount_begin) - sb->s_op->umount_begin(sb); + if (sb->s_op->umount_begin) + sb->s_op->umount_begin(mnt, flags); unlock_kernel(); /* @@ -432,29 +602,21 @@ 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++; - if (atomic_read(&sb->s_active) == 1) { - /* last instance - try to be smart */ - spin_unlock(&vfsmount_lock); - lock_kernel(); - DQUOT_OFF(sb); - acct_auto_close(sb); - unlock_kernel(); - security_sb_umount_close(mnt); - spin_lock(&vfsmount_lock); - } 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_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; } @@ -494,12 +656,11 @@ out: #ifdef __ARCH_WANT_SYS_OLDUMOUNT /* - * The 2.0 compatible umount. No flags. + * The 2.0 compatible umount. No flags. */ - asmlinkage long sys_oldumount(char __user * name) { - return sys_umount(name,0); + return sys_umount(name, 0); } #endif @@ -516,14 +677,13 @@ 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 } -static int -lives_below_in_same_fs(struct dentry *d, struct dentry *dentry) +static int lives_below_in_same_fs(struct dentry *d, struct dentry *dentry) { while (1) { if (d == dentry) @@ -534,24 +694,30 @@ 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 list_head *h; struct nameidata nd; - res = q = clone_mnt(mnt, dentry); + if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt)) + return NULL; + + res = q = clone_mnt(mnt, dentry, flag); if (!q) goto Enomem; q->mnt_mountpoint = mnt->mnt_mountpoint; p = mnt; - for (h = mnt->mnt_mounts.next; h != &mnt->mnt_mounts; h = h->next) { - r = list_entry(h, struct vfsmount, mnt_child); + list_for_each_entry(r, &mnt->mnt_mounts, mnt_child) { if (!lives_below_in_same_fs(r->mnt_mountpoint, 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; @@ -559,7 +725,7 @@ static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry) p = s; nd.mnt = q; nd.dentry = p->mnt_mountpoint; - q = clone_mnt(p, p->mnt_root); + q = clone_mnt(p, p->mnt_root, flag); if (!q) goto Enomem; spin_lock(&vfsmount_lock); @@ -569,15 +735,134 @@ static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry) } } return res; - Enomem: +Enomem: if (res) { + LIST_HEAD(umount_list); spin_lock(&vfsmount_lock); - umount_tree(res); + umount_tree(res, 0, &umount_list); spin_unlock(&vfsmount_lock); + release_mounts(&umount_list); } return NULL; } +struct vfsmount *collect_mounts(struct vfsmount *mnt, struct dentry *dentry) +{ + struct vfsmount *tree; + down_read(&namespace_sem); + tree = copy_tree(mnt, dentry, CL_COPY_ALL | CL_PRIVATE); + up_read(&namespace_sem); + return tree; +} + +void drop_collected_mounts(struct vfsmount *mnt) +{ + LIST_HEAD(umount_list); + down_read(&namespace_sem); + spin_lock(&vfsmount_lock); + umount_tree(mnt, 0, &umount_list); + spin_unlock(&vfsmount_lock); + up_read(&namespace_sem); + release_mounts(&umount_list); +} + +/* + * @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_mnt_namespace(current->nsproxy->mnt_ns); + } 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; @@ -589,7 +874,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; @@ -598,25 +883,40 @@ 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); - mntget(mnt); - err = 0; - } - 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 (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + 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. */ static int do_loopback(struct nameidata *nd, char *old_name, int recurse) @@ -632,32 +932,34 @@ 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 (check_mnt(nd->mnt) && (!recurse || check_mnt(old_nd.mnt))) { - err = -ENOMEM; - if (recurse) - mnt = copy_tree(old_nd.mnt, old_nd.dentry); - else - mnt = clone_mnt(old_nd.mnt, old_nd.dentry); - } + if (IS_MNT_UNBINDABLE(old_nd.mnt)) + goto out; - if (mnt) { - /* stop bind mounts from expiring */ + if (!check_mnt(nd->mnt) || !check_mnt(old_nd.mnt)) + goto out; + + err = -ENOMEM; + if (recurse) + mnt = copy_tree(old_nd.mnt, old_nd.dentry, 0); + else + mnt = clone_mnt(old_nd.mnt, old_nd.dentry, 0); + + if (!mnt) + goto out; + + err = graft_tree(mnt, nd); + if (err) { + LIST_HEAD(umount_list); spin_lock(&vfsmount_lock); - list_del_init(&mnt->mnt_fslink); + umount_tree(mnt, 0, &umount_list); spin_unlock(&vfsmount_lock); - - err = graft_tree(mnt, nd); - if (err) { - spin_lock(&vfsmount_lock); - umount_tree(mnt); - spin_unlock(&vfsmount_lock); - } else - mntput(mnt); + release_mounts(&umount_list); } - up_write(¤t->namespace->sem); +out: + up_write(&namespace_sem); path_release(&old_nd); return err; } @@ -667,12 +969,11 @@ static int do_loopback(struct nameidata *nd, char *old_name, int recurse) * If you've mounted a non-root directory somewhere and want to do remount * on it - tough luck. */ - static int do_remount(struct nameidata *nd, int flags, int mnt_flags, void *data) { int err; - struct super_block * sb = nd->mnt->mnt_sb; + struct super_block *sb = nd->mnt->mnt_sb; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -686,13 +987,23 @@ static int do_remount(struct nameidata *nd, int flags, int mnt_flags, down_write(&sb->s_umount); err = do_remount_sb(sb, flags, data, 0); if (!err) - nd->mnt->mnt_flags=mnt_flags; + nd->mnt->mnt_flags = mnt_flags; up_write(&sb->s_umount); if (!err) security_sb_post_remount(nd->mnt, flags, data); 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; @@ -706,51 +1017,59 @@ static int do_move_mount(struct nameidata *nd, char *old_name) if (err) return err; - down_write(¤t->namespace->sem); - while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry)) + down_write(&namespace_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_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) + 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); + 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_fslink); -out2: + list_del_init(&old_nd.mnt->mnt_expire); 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); @@ -789,9 +1108,9 @@ 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)) + while (d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry)) ; err = -EINVAL; if (!check_mnt(nd->mnt)) @@ -808,25 +1127,28 @@ int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd, goto unlock; newmnt->mnt_flags = mnt_flags; - newmnt->mnt_namespace = current->namespace; - err = graft_tree(newmnt, nd); + if ((err = graft_tree(newmnt, nd))) + goto unlock; - if (err == 0 && fslist) { + if (fslist) { /* add to the specified expiration list */ spin_lock(&vfsmount_lock); - list_add_tail(&newmnt->mnt_fslink, fslist); + list_add_tail(&newmnt->mnt_expire, fslist); spin_unlock(&vfsmount_lock); } + up_write(&namespace_sem); + return 0; unlock: - up_write(¤t->namespace->sem); + up_write(&namespace_sem); mntput(newmnt); return err; } EXPORT_SYMBOL_GPL(do_add_mount); -static void expire_mount(struct vfsmount *mnt, struct list_head *mounts) +static void expire_mount(struct vfsmount *mnt, struct list_head *mounts, + struct list_head *umounts) { spin_lock(&vfsmount_lock); @@ -843,45 +1165,64 @@ 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) { - struct nameidata old_nd; - + if (!propagate_mount_busy(mnt, 2)) { /* delete from the namespace */ + touch_mnt_namespace(mnt->mnt_ns); list_del_init(&mnt->mnt_list); - mnt->mnt_namespace = NULL; - detach_mnt(mnt, &old_nd); + mnt->mnt_ns = NULL; + umount_tree(mnt, 1, umounts); spin_unlock(&vfsmount_lock); - path_release(&old_nd); - - /* - * Now lay it to rest if this was the last ref on the superblock - */ - if (atomic_read(&mnt->mnt_sb->s_active) == 1) { - /* last instance - try to be smart */ - lock_kernel(); - DQUOT_OFF(mnt->mnt_sb); - acct_auto_close(mnt->mnt_sb); - unlock_kernel(); - } - mntput(mnt); } else { /* * Someone brought it back to life whilst we didn't have any * locks held so return it to the expiration list */ - list_add_tail(&mnt->mnt_fslink, mounts); + list_add_tail(&mnt->mnt_expire, mounts); spin_unlock(&vfsmount_lock); } } /* + * go through the vfsmounts we've just consigned to the graveyard to + * - check that they're still dead + * - delete the vfsmount from the appropriate namespace under lock + * - dispose of the corpse + */ +static void expire_mount_list(struct list_head *graveyard, struct list_head *mounts) +{ + struct mnt_namespace *ns; + struct vfsmount *mnt; + + while (!list_empty(graveyard)) { + LIST_HEAD(umounts); + mnt = list_first_entry(graveyard, struct vfsmount, mnt_expire); + list_del_init(&mnt->mnt_expire); + + /* don't do anything if the namespace is dead - all the + * vfsmounts from it are going away anyway */ + ns = mnt->mnt_ns; + if (!ns || !ns->root) + continue; + get_mnt_ns(ns); + + spin_unlock(&vfsmount_lock); + down_write(&namespace_sem); + expire_mount(mnt, mounts, &umounts); + up_write(&namespace_sem); + release_mounts(&umounts); + mntput(mnt); + put_mnt_ns(ns); + spin_lock(&vfsmount_lock); + } +} + +/* * process a list of expirable mountpoints with the intent of discarding any * mountpoints that aren't in use and haven't been touched since last we came * here */ void mark_mounts_for_expiry(struct list_head *mounts) { - struct namespace *namespace; struct vfsmount *mnt, *next; LIST_HEAD(graveyard); @@ -896,47 +1237,88 @@ void mark_mounts_for_expiry(struct list_head *mounts) * - still marked for expiry (marked on the last call here; marks are * cleared by mntput()) */ - list_for_each_entry_safe(mnt, next, mounts, mnt_fslink) { + list_for_each_entry_safe(mnt, next, mounts, mnt_expire) { if (!xchg(&mnt->mnt_expiry_mark, 1) || atomic_read(&mnt->mnt_count) != 1) continue; mntget(mnt); - list_move(&mnt->mnt_fslink, &graveyard); + list_move(&mnt->mnt_expire, &graveyard); } - /* - * go through the vfsmounts we've just consigned to the graveyard to - * - check that they're still dead - * - delete the vfsmount from the appropriate namespace under lock - * - dispose of the corpse - */ - while (!list_empty(&graveyard)) { - mnt = list_entry(graveyard.next, struct vfsmount, mnt_fslink); - list_del_init(&mnt->mnt_fslink); + expire_mount_list(&graveyard, mounts); - /* don't do anything if the namespace is dead - all the - * vfsmounts from it are going away anyway */ - namespace = mnt->mnt_namespace; - if (!namespace || !namespace->root) - continue; - get_namespace(namespace); + spin_unlock(&vfsmount_lock); +} - spin_unlock(&vfsmount_lock); - down_write(&namespace->sem); - expire_mount(mnt, mounts); - up_write(&namespace->sem); +EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); - mntput(mnt); - put_namespace(namespace); +/* + * Ripoff of 'select_parent()' + * + * search the list of submounts for a given mountpoint, and move any + * shrinkable submounts to the 'graveyard' list. + */ +static int select_submounts(struct vfsmount *parent, struct list_head *graveyard) +{ + struct vfsmount *this_parent = parent; + struct list_head *next; + int found = 0; - spin_lock(&vfsmount_lock); +repeat: + next = this_parent->mnt_mounts.next; +resume: + while (next != &this_parent->mnt_mounts) { + struct list_head *tmp = next; + struct vfsmount *mnt = list_entry(tmp, struct vfsmount, mnt_child); + + next = tmp->next; + if (!(mnt->mnt_flags & MNT_SHRINKABLE)) + continue; + /* + * Descend a level if the d_mounts list is non-empty. + */ + if (!list_empty(&mnt->mnt_mounts)) { + this_parent = mnt; + goto repeat; + } + + if (!propagate_mount_busy(mnt, 1)) { + mntget(mnt); + list_move_tail(&mnt->mnt_expire, graveyard); + found++; + } } + /* + * All done at this level ... ascend and resume the search + */ + if (this_parent != parent) { + next = this_parent->mnt_child.next; + this_parent = this_parent->mnt_parent; + goto resume; + } + return found; +} + +/* + * process a list of expirable mountpoints with the intent of discarding any + * submounts of a specific parent mountpoint + */ +void shrink_submounts(struct vfsmount *mountpoint, struct list_head *mounts) +{ + LIST_HEAD(graveyard); + int found; + + spin_lock(&vfsmount_lock); + + /* extract submounts of 'mountpoint' from the expiration list */ + while ((found = select_submounts(mountpoint, &graveyard)) != 0) + expire_mount_list(&graveyard, mounts); spin_unlock(&vfsmount_lock); } -EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); +EXPORT_SYMBOL_GPL(shrink_submounts); /* * Some copy_from_user() implementations do not return the exact number of @@ -944,8 +1326,8 @@ EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); * Note that this function differs from copy_from_user() in that it will oops * on bad values of `to', rather than returning a short copy. */ -static long -exact_copy_from_user(void *to, const void __user *from, unsigned long n) +static long exact_copy_from_user(void *to, const void __user * from, + unsigned long n) { char *t = to; const char __user *f = from; @@ -966,12 +1348,12 @@ exact_copy_from_user(void *to, const void __user *from, unsigned long n) return n; } -int copy_mount_options(const void __user *data, unsigned long *where) +int copy_mount_options(const void __user * data, unsigned long *where) { int i; unsigned long page; unsigned long size; - + *where = 0; if (!data) return 0; @@ -990,7 +1372,7 @@ int copy_mount_options(const void __user *data, unsigned long *where) i = size - exact_copy_from_user((void *)page, data, size); if (!i) { - free_page(page); + free_page(page); return -EFAULT; } if (i != PAGE_SIZE) @@ -1013,7 +1395,7 @@ int copy_mount_options(const void __user *data, unsigned long *where) * 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, +long do_mount(char *dev_name, char *dir_name, char *type_page, unsigned long flags, void *data_page) { struct nameidata nd; @@ -1041,7 +1423,15 @@ 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; + if (flags & MS_RELATIME) + mnt_flags |= MNT_RELATIME; + + flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | + MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT); /* ... and get the mountpoint */ retval = path_lookup(dir_name, LOOKUP_FOLLOW, &nd); @@ -1057,6 +1447,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 @@ -1067,42 +1459,34 @@ dput_out: return retval; } -int copy_namespace(int flags, struct task_struct *tsk) +/* + * Allocate a new namespace structure and populate it with contents + * copied from the namespace of the passed in task structure. + */ +static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, + struct fs_struct *fs) { - struct namespace *namespace = tsk->namespace; - struct namespace *new_ns; + struct mnt_namespace *new_ns; struct vfsmount *rootmnt = NULL, *pwdmnt = NULL, *altrootmnt = NULL; - struct fs_struct *fs = tsk->fs; struct vfsmount *p, *q; - if (!namespace) - return 0; - - get_namespace(namespace); - - if (!(flags & CLONE_NEWNS)) - return 0; - - if (!capable(CAP_SYS_ADMIN)) { - put_namespace(namespace); - return -EPERM; - } - - new_ns = kmalloc(sizeof(struct namespace), GFP_KERNEL); + new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL); if (!new_ns) - goto out; + return ERR_PTR(-ENOMEM); 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); + new_ns->root = copy_tree(mnt_ns->root, mnt_ns->root->mnt_root, + CL_COPY_ALL | CL_EXPIRE); if (!new_ns->root) { - up_write(&tsk->namespace->sem); + up_write(&namespace_sem); kfree(new_ns); - goto out; + return ERR_PTR(-ENOMEM);; } spin_lock(&vfsmount_lock); list_add_tail(&new_ns->list, &new_ns->root->mnt_list); @@ -1113,10 +1497,10 @@ int copy_namespace(int flags, struct task_struct *tsk) * as belonging to new namespace. We have already acquired a private * fs_struct, so tsk->fs->lock is not needed. */ - p = namespace->root; + p = mnt_ns->root; q = new_ns->root; while (p) { - q->mnt_namespace = new_ns; + q->mnt_ns = new_ns; if (fs) { if (p == fs->rootmnt) { rootmnt = p; @@ -1131,12 +1515,10 @@ int copy_namespace(int flags, struct task_struct *tsk) fs->altrootmnt = mntget(q); } } - p = next_mnt(p, namespace->root); + p = next_mnt(p, mnt_ns->root); q = next_mnt(q, new_ns->root); } - up_write(&tsk->namespace->sem); - - tsk->namespace = new_ns; + up_write(&namespace_sem); if (rootmnt) mntput(rootmnt); @@ -1145,12 +1527,24 @@ int copy_namespace(int flags, struct task_struct *tsk) if (altrootmnt) mntput(altrootmnt); - put_namespace(namespace); - return 0; + return new_ns; +} -out: - put_namespace(namespace); - return -ENOMEM; +struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns, + struct fs_struct *new_fs) +{ + struct mnt_namespace *new_ns; + + BUG_ON(!ns); + get_mnt_ns(ns); + + if (!(flags & CLONE_NEWNS)) + return ns; + + new_ns = dup_mnt_ns(ns, new_fs); + + put_mnt_ns(ns); + return new_ns; } asmlinkage long sys_mount(char __user * dev_name, char __user * dir_name, @@ -1163,7 +1557,7 @@ asmlinkage long sys_mount(char __user * dev_name, char __user * dir_name, unsigned long dev_page; char *dir_page; - retval = copy_mount_options (type, &type_page); + retval = copy_mount_options(type, &type_page); if (retval < 0) return retval; @@ -1172,17 +1566,17 @@ asmlinkage long sys_mount(char __user * dev_name, char __user * dir_name, if (IS_ERR(dir_page)) goto out1; - retval = copy_mount_options (dev_name, &dev_page); + retval = copy_mount_options(dev_name, &dev_page); if (retval < 0) goto out2; - retval = copy_mount_options (data, &data_page); + 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); + retval = do_mount((char *)dev_page, dir_page, (char *)type_page, + flags, (void *)data_page); unlock_kernel(); free_page(data_page); @@ -1251,9 +1645,11 @@ static void chroot_fs_refs(struct nameidata *old_nd, struct nameidata *new_nd) if (fs) { atomic_inc(&fs->count); task_unlock(p); - if (fs->root==old_nd->dentry&&fs->rootmnt==old_nd->mnt) + 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) + 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 @@ -1275,6 +1671,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) @@ -1283,8 +1683,8 @@ static void chroot_fs_refs(struct nameidata *old_nd, struct nameidata *new_nd) * though, so you may need to say mount --bind /nfs/my_root /nfs/my_root * first. */ - -asmlinkage long sys_pivot_root(const char __user *new_root, const char __user *put_old) +asmlinkage long sys_pivot_root(const char __user * new_root, + const char __user * put_old) { struct vfsmount *tmp; struct nameidata new_nd, old_nd, parent_nd, root_parent, user_nd; @@ -1295,14 +1695,15 @@ asmlinkage long sys_pivot_root(const char __user *new_root, const char __user *p lock_kernel(); - error = __user_walk(new_root, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &new_nd); + error = __user_walk(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, + &new_nd); if (error) goto out0; error = -EINVAL; if (!check_mnt(new_nd.mnt)) goto out1; - error = __user_walk(put_old, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &old_nd); + error = __user_walk(put_old, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old_nd); if (error) goto out1; @@ -1316,9 +1717,13 @@ asmlinkage long sys_pivot_root(const char __user *new_root, const char __user *p 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; @@ -1334,8 +1739,12 @@ asmlinkage long sys_pivot_root(const char __user *new_root, const char __user *p error = -EINVAL; if (user_nd.mnt->mnt_root != user_nd.dentry) goto out2; /* not a mountpoint */ + if (user_nd.mnt->mnt_parent == user_nd.mnt) + goto out2; /* not attached */ if (new_nd.mnt->mnt_root != new_nd.dentry) goto out2; /* not a mountpoint */ + if (new_nd.mnt->mnt_parent == new_nd.mnt) + goto out2; /* not attached */ tmp = old_nd.mnt; /* make sure we can reach put_old from new_root */ spin_lock(&vfsmount_lock); if (tmp != new_nd.mnt) { @@ -1354,6 +1763,7 @@ asmlinkage long sys_pivot_root(const char __user *new_root, const char __user *p detach_mnt(user_nd.mnt, &root_parent); attach_mnt(user_nd.mnt, &old_nd); /* mount old root on put_old */ attach_mnt(new_nd.mnt, &root_parent); /* mount new_root on / */ + touch_mnt_namespace(current->nsproxy->mnt_ns); spin_unlock(&vfsmount_lock); chroot_fs_refs(&user_nd, &new_nd); security_sb_post_pivotroot(&user_nd, &new_nd); @@ -1361,8 +1771,8 @@ asmlinkage long sys_pivot_root(const char __user *new_root, const char __user *p 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: @@ -1378,45 +1788,42 @@ out3: static void __init init_mount_tree(void) { struct vfsmount *mnt; - struct namespace *namespace; - struct task_struct *g, *p; + struct mnt_namespace *ns; mnt = do_kern_mount("rootfs", 0, "rootfs", NULL); if (IS_ERR(mnt)) panic("Can't create rootfs"); - namespace = kmalloc(sizeof(*namespace), GFP_KERNEL); - if (!namespace) + ns = kmalloc(sizeof(*ns), GFP_KERNEL); + if (!ns) panic("Can't allocate initial namespace"); - atomic_set(&namespace->count, 1); - INIT_LIST_HEAD(&namespace->list); - init_rwsem(&namespace->sem); - list_add(&mnt->mnt_list, &namespace->list); - namespace->root = mnt; - mnt->mnt_namespace = namespace; - - init_task.namespace = namespace; - read_lock(&tasklist_lock); - do_each_thread(g, p) { - get_namespace(namespace); - p->namespace = namespace; - } while_each_thread(g, p); - read_unlock(&tasklist_lock); - - set_fs_pwd(current->fs, namespace->root, namespace->root->mnt_root); - set_fs_root(current->fs, namespace->root, namespace->root->mnt_root); + 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); + + set_fs_pwd(current->fs, ns->root, ns->root->mnt_root); + set_fs_root(current->fs, ns->root, ns->root->mnt_root); } -void __init mnt_init(unsigned long mempages) +void __init mnt_init(void) { struct list_head *d; unsigned int nr_hash; int i; + int err; + + init_rwsem(&namespace_sem); mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount), - 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); + 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); - mount_hashtable = (struct list_head *) - __get_free_page(GFP_ATOMIC); + mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC); if (!mount_hashtable) panic("Failed to allocate mount hash table\n"); @@ -1438,7 +1845,7 @@ void __init mnt_init(unsigned long mempages) * from the number of bits we can fit. */ nr_hash = 1UL << hash_bits; - hash_mask = nr_hash-1; + hash_mask = nr_hash - 1; printk("Mount-cache hash table entries: %d\n", nr_hash); @@ -1450,20 +1857,28 @@ void __init mnt_init(unsigned long mempages) d++; i--; } while (i); - sysfs_init(); + 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__); init_rootfs(); init_mount_tree(); } -void __put_namespace(struct namespace *namespace) +void __put_mnt_ns(struct mnt_namespace *ns) { - struct vfsmount *root = namespace->root; - namespace->root = NULL; + struct vfsmount *root = ns->root; + LIST_HEAD(umount_list); + ns->root = NULL; spin_unlock(&vfsmount_lock); - down_write(&namespace->sem); + down_write(&namespace_sem); spin_lock(&vfsmount_lock); - umount_tree(root); + umount_tree(root, 0, &umount_list); spin_unlock(&vfsmount_lock); - up_write(&namespace->sem); - kfree(namespace); + up_write(&namespace_sem); + release_mounts(&umount_list); + kfree(ns); }