aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2011-01-14 13:45:21 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2011-01-15 20:05:03 -0500
commit9875cf806403fae66b2410a3c2cc820d97731e04 (patch)
tree6f9546b400716766af95e0f78e3d600e765b2b51 /fs
parent1a8edf40e7c3eee955e0dd0316a7c9d85e36f597 (diff)
Add a dentry op to handle automounting rather than abusing follow_link()
Add a dentry op (d_automount) to handle automounting directories rather than abusing the follow_link() inode operation. The operation is keyed off a new dentry flag (DCACHE_NEED_AUTOMOUNT). This also makes it easier to add an AT_ flag to suppress terminal segment automount during pathwalk and removes the need for the kludge code in the pathwalk algorithm to handle directories with follow_link() semantics. The ->d_automount() dentry operation: struct vfsmount *(*d_automount)(struct path *mountpoint); takes a pointer to the directory to be mounted upon, which is expected to provide sufficient data to determine what should be mounted. If successful, it should return the vfsmount struct it creates (which it should also have added to the namespace using do_add_mount() or similar). If there's a collision with another automount attempt, NULL should be returned. If the directory specified by the parameter should be used directly rather than being mounted upon, -EISDIR should be returned. In any other case, an error code should be returned. The ->d_automount() operation is called with no locks held and may sleep. At this point the pathwalk algorithm will be in ref-walk mode. Within fs/namei.c itself, a new pathwalk subroutine (follow_automount()) is added to handle mountpoints. It will return -EREMOTE if the automount flag was set, but no d_automount() op was supplied, -ELOOP if we've encountered too many symlinks or mountpoints, -EISDIR if the walk point should be used without mounting and 0 if successful. The path will be updated to point to the mounted filesystem if a successful automount took place. __follow_mount() is replaced by follow_managed() which is more generic (especially with the patch that adds ->d_manage()). This handles transits from directories during pathwalk, including automounting and skipping over mountpoints (and holding processes with the next patch). __follow_mount_rcu() will jump out of RCU-walk mode if it encounters an automount point with nothing mounted on it. follow_dotdot*() does not handle automounts as you don't want to trigger them whilst following "..". I've also extracted the mount/don't-mount logic from autofs4 and included it here. It makes the mount go ahead anyway if someone calls open() or creat(), tries to traverse the directory, tries to chdir/chroot/etc. into the directory, or sticks a '/' on the end of the pathname. If they do a stat(), however, they'll only trigger the automount if they didn't also say O_NOFOLLOW. I've also added an inode flag (S_AUTOMOUNT) so that filesystems can mark their inodes as automount points. This flag is automatically propagated to the dentry as DCACHE_NEED_AUTOMOUNT by __d_instantiate(). This saves NFS and could save AFS a private flag bit apiece, but is not strictly necessary. It would be preferable to do the propagation in d_set_d_op(), but that doesn't normally have access to the inode. [AV: fixed breakage in case if __follow_mount_rcu() fails and nameidata_drop_rcu() succeeds in RCU case of do_lookup(); we need to fall through to non-RCU case after that, rather than just returning with ungrabbed *path] Signed-off-by: David Howells <dhowells@redhat.com> Was-Acked-by: Ian Kent <raven@themaw.net> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r--fs/dcache.c5
-rw-r--r--fs/namei.c226
2 files changed, 174 insertions, 57 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 0c6d5c549d84..51f7bb6463af 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1380,8 +1380,11 @@ EXPORT_SYMBOL(d_set_d_op);
1380static void __d_instantiate(struct dentry *dentry, struct inode *inode) 1380static void __d_instantiate(struct dentry *dentry, struct inode *inode)
1381{ 1381{
1382 spin_lock(&dentry->d_lock); 1382 spin_lock(&dentry->d_lock);
1383 if (inode) 1383 if (inode) {
1384 if (unlikely(IS_AUTOMOUNT(inode)))
1385 dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
1384 list_add(&dentry->d_alias, &inode->i_dentry); 1386 list_add(&dentry->d_alias, &inode->i_dentry);
1387 }
1385 dentry->d_inode = inode; 1388 dentry->d_inode = inode;
1386 dentry_rcuwalk_barrier(dentry); 1389 dentry_rcuwalk_barrier(dentry);
1387 spin_unlock(&dentry->d_lock); 1390 spin_unlock(&dentry->d_lock);
diff --git a/fs/namei.c b/fs/namei.c
index 529e917ad2fc..16109da68bbf 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -896,51 +896,120 @@ int follow_up(struct path *path)
896} 896}
897 897
898/* 898/*
899 * serialization is taken care of in namespace.c 899 * Perform an automount
900 * - return -EISDIR to tell follow_managed() to stop and return the path we
901 * were called with.
900 */ 902 */
901static void __follow_mount_rcu(struct nameidata *nd, struct path *path, 903static int follow_automount(struct path *path, unsigned flags,
902 struct inode **inode) 904 bool *need_mntput)
903{ 905{
904 while (d_mountpoint(path->dentry)) { 906 struct vfsmount *mnt;
905 struct vfsmount *mounted; 907
906 mounted = __lookup_mnt(path->mnt, path->dentry, 1); 908 if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
907 if (!mounted) 909 return -EREMOTE;
908 return; 910
909 path->mnt = mounted; 911 /* We want to mount if someone is trying to open/create a file of any
910 path->dentry = mounted->mnt_root; 912 * type under the mountpoint, wants to traverse through the mountpoint
911 nd->seq = read_seqcount_begin(&path->dentry->d_seq); 913 * or wants to open the mounted directory.
912 *inode = path->dentry->d_inode; 914 *
915 * We don't want to mount if someone's just doing a stat and they've
916 * set AT_SYMLINK_NOFOLLOW - unless they're stat'ing a directory and
917 * appended a '/' to the name.
918 */
919 if (!(flags & LOOKUP_FOLLOW) &&
920 !(flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY |
921 LOOKUP_OPEN | LOOKUP_CREATE)))
922 return -EISDIR;
923
924 current->total_link_count++;
925 if (current->total_link_count >= 40)
926 return -ELOOP;
927
928 mnt = path->dentry->d_op->d_automount(path);
929 if (IS_ERR(mnt)) {
930 /*
931 * The filesystem is allowed to return -EISDIR here to indicate
932 * it doesn't want to automount. For instance, autofs would do
933 * this so that its userspace daemon can mount on this dentry.
934 *
935 * However, we can only permit this if it's a terminal point in
936 * the path being looked up; if it wasn't then the remainder of
937 * the path is inaccessible and we should say so.
938 */
939 if (PTR_ERR(mnt) == -EISDIR && (flags & LOOKUP_CONTINUE))
940 return -EREMOTE;
941 return PTR_ERR(mnt);
913 } 942 }
914} 943 if (!mnt) /* mount collision */
944 return 0;
915 945
916static int __follow_mount(struct path *path) 946 if (mnt->mnt_sb == path->mnt->mnt_sb &&
917{ 947 mnt->mnt_root == path->dentry) {
918 int res = 0; 948 mntput(mnt);
919 while (d_mountpoint(path->dentry)) { 949 return -ELOOP;
920 struct vfsmount *mounted = lookup_mnt(path);
921 if (!mounted)
922 break;
923 dput(path->dentry);
924 if (res)
925 mntput(path->mnt);
926 path->mnt = mounted;
927 path->dentry = dget(mounted->mnt_root);
928 res = 1;
929 } 950 }
930 return res; 951
952 dput(path->dentry);
953 if (*need_mntput)
954 mntput(path->mnt);
955 path->mnt = mnt;
956 path->dentry = dget(mnt->mnt_root);
957 *need_mntput = true;
958 return 0;
931} 959}
932 960
933static void follow_mount(struct path *path) 961/*
962 * Handle a dentry that is managed in some way.
963 * - Flagged as mountpoint
964 * - Flagged as automount point
965 *
966 * This may only be called in refwalk mode.
967 *
968 * Serialization is taken care of in namespace.c
969 */
970static int follow_managed(struct path *path, unsigned flags)
934{ 971{
935 while (d_mountpoint(path->dentry)) { 972 unsigned managed;
936 struct vfsmount *mounted = lookup_mnt(path); 973 bool need_mntput = false;
937 if (!mounted) 974 int ret;
938 break; 975
939 dput(path->dentry); 976 /* Given that we're not holding a lock here, we retain the value in a
940 mntput(path->mnt); 977 * local variable for each dentry as we look at it so that we don't see
941 path->mnt = mounted; 978 * the components of that value change under us */
942 path->dentry = dget(mounted->mnt_root); 979 while (managed = ACCESS_ONCE(path->dentry->d_flags),
980 managed &= DCACHE_MANAGED_DENTRY,
981 unlikely(managed != 0)) {
982 /* Transit to a mounted filesystem. */
983 if (managed & DCACHE_MOUNTED) {
984 struct vfsmount *mounted = lookup_mnt(path);
985 if (mounted) {
986 dput(path->dentry);
987 if (need_mntput)
988 mntput(path->mnt);
989 path->mnt = mounted;
990 path->dentry = dget(mounted->mnt_root);
991 need_mntput = true;
992 continue;
993 }
994
995 /* Something is mounted on this dentry in another
996 * namespace and/or whatever was mounted there in this
997 * namespace got unmounted before we managed to get the
998 * vfsmount_lock */
999 }
1000
1001 /* Handle an automount point */
1002 if (managed & DCACHE_NEED_AUTOMOUNT) {
1003 ret = follow_automount(path, flags, &need_mntput);
1004 if (ret < 0)
1005 return ret == -EISDIR ? 0 : ret;
1006 continue;
1007 }
1008
1009 /* We didn't change the current path point */
1010 break;
943 } 1011 }
1012 return 0;
944} 1013}
945 1014
946int follow_down(struct path *path) 1015int follow_down(struct path *path)
@@ -958,13 +1027,37 @@ int follow_down(struct path *path)
958 return 0; 1027 return 0;
959} 1028}
960 1029
1030/*
1031 * Skip to top of mountpoint pile in rcuwalk mode. We abort the rcu-walk if we
1032 * meet an automount point and we're not walking to "..". True is returned to
1033 * continue, false to abort.
1034 */
1035static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
1036 struct inode **inode, bool reverse_transit)
1037{
1038 while (d_mountpoint(path->dentry)) {
1039 struct vfsmount *mounted;
1040 mounted = __lookup_mnt(path->mnt, path->dentry, 1);
1041 if (!mounted)
1042 break;
1043 path->mnt = mounted;
1044 path->dentry = mounted->mnt_root;
1045 nd->seq = read_seqcount_begin(&path->dentry->d_seq);
1046 *inode = path->dentry->d_inode;
1047 }
1048
1049 if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
1050 return reverse_transit;
1051 return true;
1052}
1053
961static int follow_dotdot_rcu(struct nameidata *nd) 1054static int follow_dotdot_rcu(struct nameidata *nd)
962{ 1055{
963 struct inode *inode = nd->inode; 1056 struct inode *inode = nd->inode;
964 1057
965 set_root_rcu(nd); 1058 set_root_rcu(nd);
966 1059
967 while(1) { 1060 while (1) {
968 if (nd->path.dentry == nd->root.dentry && 1061 if (nd->path.dentry == nd->root.dentry &&
969 nd->path.mnt == nd->root.mnt) { 1062 nd->path.mnt == nd->root.mnt) {
970 break; 1063 break;
@@ -987,12 +1080,28 @@ static int follow_dotdot_rcu(struct nameidata *nd)
987 nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); 1080 nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
988 inode = nd->path.dentry->d_inode; 1081 inode = nd->path.dentry->d_inode;
989 } 1082 }
990 __follow_mount_rcu(nd, &nd->path, &inode); 1083 __follow_mount_rcu(nd, &nd->path, &inode, true);
991 nd->inode = inode; 1084 nd->inode = inode;
992 1085
993 return 0; 1086 return 0;
994} 1087}
995 1088
1089/*
1090 * Skip to top of mountpoint pile in refwalk mode for follow_dotdot()
1091 */
1092static void follow_mount(struct path *path)
1093{
1094 while (d_mountpoint(path->dentry)) {
1095 struct vfsmount *mounted = lookup_mnt(path);
1096 if (!mounted)
1097 break;
1098 dput(path->dentry);
1099 mntput(path->mnt);
1100 path->mnt = mounted;
1101 path->dentry = dget(mounted->mnt_root);
1102 }
1103}
1104
996static void follow_dotdot(struct nameidata *nd) 1105static void follow_dotdot(struct nameidata *nd)
997{ 1106{
998 set_root(nd); 1107 set_root(nd);
@@ -1057,12 +1166,14 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
1057 struct vfsmount *mnt = nd->path.mnt; 1166 struct vfsmount *mnt = nd->path.mnt;
1058 struct dentry *dentry, *parent = nd->path.dentry; 1167 struct dentry *dentry, *parent = nd->path.dentry;
1059 struct inode *dir; 1168 struct inode *dir;
1169 int err;
1170
1060 /* 1171 /*
1061 * See if the low-level filesystem might want 1172 * See if the low-level filesystem might want
1062 * to use its own hash.. 1173 * to use its own hash..
1063 */ 1174 */
1064 if (unlikely(parent->d_flags & DCACHE_OP_HASH)) { 1175 if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
1065 int err = parent->d_op->d_hash(parent, nd->inode, name); 1176 err = parent->d_op->d_hash(parent, nd->inode, name);
1066 if (err < 0) 1177 if (err < 0)
1067 return err; 1178 return err;
1068 } 1179 }
@@ -1092,20 +1203,25 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
1092done2: 1203done2:
1093 path->mnt = mnt; 1204 path->mnt = mnt;
1094 path->dentry = dentry; 1205 path->dentry = dentry;
1095 __follow_mount_rcu(nd, path, inode); 1206 if (likely(__follow_mount_rcu(nd, path, inode, false)))
1096 } else { 1207 return 0;
1097 dentry = __d_lookup(parent, name); 1208 if (nameidata_drop_rcu(nd))
1098 if (!dentry) 1209 return -ECHILD;
1099 goto need_lookup; 1210 /* fallthru */
1211 }
1212 dentry = __d_lookup(parent, name);
1213 if (!dentry)
1214 goto need_lookup;
1100found: 1215found:
1101 if (dentry->d_flags & DCACHE_OP_REVALIDATE) 1216 if (dentry->d_flags & DCACHE_OP_REVALIDATE)
1102 goto need_revalidate; 1217 goto need_revalidate;
1103done: 1218done:
1104 path->mnt = mnt; 1219 path->mnt = mnt;
1105 path->dentry = dentry; 1220 path->dentry = dentry;
1106 __follow_mount(path); 1221 err = follow_managed(path, nd->flags);
1107 *inode = path->dentry->d_inode; 1222 if (unlikely(err < 0))
1108 } 1223 return err;
1224 *inode = path->dentry->d_inode;
1109 return 0; 1225 return 0;
1110 1226
1111need_lookup: 1227need_lookup:
@@ -2203,11 +2319,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
2203 if (open_flag & O_EXCL) 2319 if (open_flag & O_EXCL)
2204 goto exit_dput; 2320 goto exit_dput;
2205 2321
2206 if (__follow_mount(path)) { 2322 error = follow_managed(path, nd->flags);
2207 error = -ELOOP; 2323 if (error < 0)
2208 if (open_flag & O_NOFOLLOW) 2324 goto exit_dput;
2209 goto exit_dput;
2210 }
2211 2325
2212 error = -ENOENT; 2326 error = -ENOENT;
2213 if (!path->dentry->d_inode) 2327 if (!path->dentry->d_inode)