diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 226 |
1 files changed, 170 insertions, 56 deletions
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 | */ |
901 | static void __follow_mount_rcu(struct nameidata *nd, struct path *path, | 903 | static 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 | ||
916 | static 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 | ||
933 | static 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 | */ | ||
970 | static 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 | ||
946 | int follow_down(struct path *path) | 1015 | int 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 | */ | ||
1035 | static 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 | |||
961 | static int follow_dotdot_rcu(struct nameidata *nd) | 1054 | static 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 | */ | ||
1092 | static 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 | |||
996 | static void follow_dotdot(struct nameidata *nd) | 1105 | static 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, | |||
1092 | done2: | 1203 | done2: |
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; | ||
1100 | found: | 1215 | found: |
1101 | if (dentry->d_flags & DCACHE_OP_REVALIDATE) | 1216 | if (dentry->d_flags & DCACHE_OP_REVALIDATE) |
1102 | goto need_revalidate; | 1217 | goto need_revalidate; |
1103 | done: | 1218 | done: |
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 | ||
1111 | need_lookup: | 1227 | need_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) |