diff options
author | Ram Pai <linuxram@us.ibm.com> | 2005-11-07 17:19:50 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-11-07 21:18:11 -0500 |
commit | b90fa9ae8f51f098ee480bbaabd6867992e9fc58 (patch) | |
tree | 2ad583b3a7399face7a78730b001928413c8269e /fs/namespace.c | |
parent | 03e06e68ff76294e53ffa898cb844d2a997b043e (diff) |
[PATCH] shared mount handling: bind and rbind
Implement handling of MS_BIND in presense of shared mounts (see
Documentation/sharedsubtree.txt in the end of patch series for detailed
description).
Signed-off-by: Ram Pai <linuxram@us.ibm.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/namespace.c')
-rw-r--r-- | fs/namespace.c | 126 |
1 files changed, 105 insertions, 21 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index f6861a5487df..9f5a084b239f 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -28,8 +28,6 @@ | |||
28 | 28 | ||
29 | extern int __init init_rootfs(void); | 29 | extern int __init init_rootfs(void); |
30 | 30 | ||
31 | #define CL_EXPIRE 0x01 | ||
32 | |||
33 | #ifdef CONFIG_SYSFS | 31 | #ifdef CONFIG_SYSFS |
34 | extern int __init sysfs_init(void); | 32 | extern int __init sysfs_init(void); |
35 | #else | 33 | #else |
@@ -145,13 +143,43 @@ static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd) | |||
145 | old_nd->dentry->d_mounted--; | 143 | old_nd->dentry->d_mounted--; |
146 | } | 144 | } |
147 | 145 | ||
146 | void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry, | ||
147 | struct vfsmount *child_mnt) | ||
148 | { | ||
149 | child_mnt->mnt_parent = mntget(mnt); | ||
150 | child_mnt->mnt_mountpoint = dget(dentry); | ||
151 | dentry->d_mounted++; | ||
152 | } | ||
153 | |||
148 | static void attach_mnt(struct vfsmount *mnt, struct nameidata *nd) | 154 | static void attach_mnt(struct vfsmount *mnt, struct nameidata *nd) |
149 | { | 155 | { |
150 | mnt->mnt_parent = mntget(nd->mnt); | 156 | mnt_set_mountpoint(nd->mnt, nd->dentry, mnt); |
151 | mnt->mnt_mountpoint = dget(nd->dentry); | 157 | list_add_tail(&mnt->mnt_hash, mount_hashtable + |
152 | list_add(&mnt->mnt_hash, mount_hashtable + hash(nd->mnt, nd->dentry)); | 158 | hash(nd->mnt, nd->dentry)); |
153 | list_add_tail(&mnt->mnt_child, &nd->mnt->mnt_mounts); | 159 | list_add_tail(&mnt->mnt_child, &nd->mnt->mnt_mounts); |
154 | nd->dentry->d_mounted++; | 160 | } |
161 | |||
162 | /* | ||
163 | * the caller must hold vfsmount_lock | ||
164 | */ | ||
165 | static void commit_tree(struct vfsmount *mnt) | ||
166 | { | ||
167 | struct vfsmount *parent = mnt->mnt_parent; | ||
168 | struct vfsmount *m; | ||
169 | LIST_HEAD(head); | ||
170 | struct namespace *n = parent->mnt_namespace; | ||
171 | |||
172 | BUG_ON(parent == mnt); | ||
173 | |||
174 | list_add_tail(&head, &mnt->mnt_list); | ||
175 | list_for_each_entry(m, &head, mnt_list) | ||
176 | m->mnt_namespace = n; | ||
177 | list_splice(&head, n->list.prev); | ||
178 | |||
179 | list_add_tail(&mnt->mnt_hash, mount_hashtable + | ||
180 | hash(parent, mnt->mnt_mountpoint)); | ||
181 | list_add_tail(&mnt->mnt_child, &parent->mnt_mounts); | ||
182 | touch_namespace(n); | ||
155 | } | 183 | } |
156 | 184 | ||
157 | static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root) | 185 | static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root) |
@@ -183,7 +211,11 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, | |||
183 | mnt->mnt_root = dget(root); | 211 | mnt->mnt_root = dget(root); |
184 | mnt->mnt_mountpoint = mnt->mnt_root; | 212 | mnt->mnt_mountpoint = mnt->mnt_root; |
185 | mnt->mnt_parent = mnt; | 213 | mnt->mnt_parent = mnt; |
186 | mnt->mnt_namespace = current->namespace; | 214 | |
215 | if ((flag & CL_PROPAGATION) || IS_MNT_SHARED(old)) | ||
216 | list_add(&mnt->mnt_share, &old->mnt_share); | ||
217 | if (flag & CL_MAKE_SHARED) | ||
218 | set_mnt_shared(mnt); | ||
187 | 219 | ||
188 | /* stick the duplicate mount on the same expiry list | 220 | /* stick the duplicate mount on the same expiry list |
189 | * as the original if that was on one */ | 221 | * as the original if that was on one */ |
@@ -379,7 +411,7 @@ int may_umount(struct vfsmount *mnt) | |||
379 | 411 | ||
380 | EXPORT_SYMBOL(may_umount); | 412 | EXPORT_SYMBOL(may_umount); |
381 | 413 | ||
382 | static void release_mounts(struct list_head *head) | 414 | void release_mounts(struct list_head *head) |
383 | { | 415 | { |
384 | struct vfsmount *mnt; | 416 | struct vfsmount *mnt; |
385 | while(!list_empty(head)) { | 417 | while(!list_empty(head)) { |
@@ -401,7 +433,7 @@ static void release_mounts(struct list_head *head) | |||
401 | } | 433 | } |
402 | } | 434 | } |
403 | 435 | ||
404 | static void umount_tree(struct vfsmount *mnt, struct list_head *kill) | 436 | void umount_tree(struct vfsmount *mnt, struct list_head *kill) |
405 | { | 437 | { |
406 | struct vfsmount *p; | 438 | struct vfsmount *p; |
407 | 439 | ||
@@ -581,7 +613,7 @@ static int lives_below_in_same_fs(struct dentry *d, struct dentry *dentry) | |||
581 | } | 613 | } |
582 | } | 614 | } |
583 | 615 | ||
584 | static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry, | 616 | struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry, |
585 | int flag) | 617 | int flag) |
586 | { | 618 | { |
587 | struct vfsmount *res, *p, *q, *r, *s; | 619 | struct vfsmount *res, *p, *q, *r, *s; |
@@ -626,6 +658,67 @@ Enomem: | |||
626 | return NULL; | 658 | return NULL; |
627 | } | 659 | } |
628 | 660 | ||
661 | /* | ||
662 | * @source_mnt : mount tree to be attached | ||
663 | * @nd : place the mount tree @source_mnt is attached | ||
664 | * | ||
665 | * NOTE: in the table below explains the semantics when a source mount | ||
666 | * of a given type is attached to a destination mount of a given type. | ||
667 | * --------------------------------------------- | ||
668 | * | BIND MOUNT OPERATION | | ||
669 | * |******************************************** | ||
670 | * | source-->| shared | private | | ||
671 | * | dest | | | | ||
672 | * | | | | | | ||
673 | * | v | | | | ||
674 | * |******************************************** | ||
675 | * | shared | shared (++) | shared (+) | | ||
676 | * | | | | | ||
677 | * |non-shared| shared (+) | private | | ||
678 | * ********************************************* | ||
679 | * A bind operation clones the source mount and mounts the clone on the | ||
680 | * destination mount. | ||
681 | * | ||
682 | * (++) the cloned mount is propagated to all the mounts in the propagation | ||
683 | * tree of the destination mount and the cloned mount is added to | ||
684 | * the peer group of the source mount. | ||
685 | * (+) the cloned mount is created under the destination mount and is marked | ||
686 | * as shared. The cloned mount is added to the peer group of the source | ||
687 | * mount. | ||
688 | * | ||
689 | * if the source mount is a tree, the operations explained above is | ||
690 | * applied to each mount in the tree. | ||
691 | * Must be called without spinlocks held, since this function can sleep | ||
692 | * in allocations. | ||
693 | */ | ||
694 | static int attach_recursive_mnt(struct vfsmount *source_mnt, | ||
695 | struct nameidata *nd) | ||
696 | { | ||
697 | LIST_HEAD(tree_list); | ||
698 | struct vfsmount *dest_mnt = nd->mnt; | ||
699 | struct dentry *dest_dentry = nd->dentry; | ||
700 | struct vfsmount *child, *p; | ||
701 | |||
702 | if (propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list)) | ||
703 | return -EINVAL; | ||
704 | |||
705 | if (IS_MNT_SHARED(dest_mnt)) { | ||
706 | for (p = source_mnt; p; p = next_mnt(p, source_mnt)) | ||
707 | set_mnt_shared(p); | ||
708 | } | ||
709 | |||
710 | spin_lock(&vfsmount_lock); | ||
711 | mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt); | ||
712 | commit_tree(source_mnt); | ||
713 | |||
714 | list_for_each_entry_safe(child, p, &tree_list, mnt_hash) { | ||
715 | list_del_init(&child->mnt_hash); | ||
716 | commit_tree(child); | ||
717 | } | ||
718 | spin_unlock(&vfsmount_lock); | ||
719 | return 0; | ||
720 | } | ||
721 | |||
629 | static int graft_tree(struct vfsmount *mnt, struct nameidata *nd) | 722 | static int graft_tree(struct vfsmount *mnt, struct nameidata *nd) |
630 | { | 723 | { |
631 | int err; | 724 | int err; |
@@ -646,17 +739,8 @@ static int graft_tree(struct vfsmount *mnt, struct nameidata *nd) | |||
646 | goto out_unlock; | 739 | goto out_unlock; |
647 | 740 | ||
648 | err = -ENOENT; | 741 | err = -ENOENT; |
649 | spin_lock(&vfsmount_lock); | 742 | if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry)) |
650 | if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry)) { | 743 | err = attach_recursive_mnt(mnt, nd); |
651 | struct list_head head; | ||
652 | |||
653 | attach_mnt(mnt, nd); | ||
654 | list_add_tail(&head, &mnt->mnt_list); | ||
655 | list_splice(&head, current->namespace->list.prev); | ||
656 | err = 0; | ||
657 | touch_namespace(current->namespace); | ||
658 | } | ||
659 | spin_unlock(&vfsmount_lock); | ||
660 | out_unlock: | 744 | out_unlock: |
661 | up(&nd->dentry->d_inode->i_sem); | 745 | up(&nd->dentry->d_inode->i_sem); |
662 | if (!err) | 746 | if (!err) |