diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/afs/mntpt.c | 5 | ||||
-rw-r--r-- | fs/autofs4/autofs_i.h | 13 | ||||
-rw-r--r-- | fs/autofs4/dev-ioctl.c | 2 | ||||
-rw-r--r-- | fs/autofs4/expire.c | 2 | ||||
-rw-r--r-- | fs/autofs4/root.c | 11 | ||||
-rw-r--r-- | fs/cifs/cifs_dfs_ref.c | 5 | ||||
-rw-r--r-- | fs/namei.c | 72 | ||||
-rw-r--r-- | fs/namespace.c | 14 | ||||
-rw-r--r-- | fs/nfs/namespace.c | 5 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 5 |
10 files changed, 91 insertions, 43 deletions
diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index e83c0336e7b5..f3e891d57a2c 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c | |||
@@ -273,10 +273,7 @@ static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
273 | break; | 273 | break; |
274 | case -EBUSY: | 274 | case -EBUSY: |
275 | /* someone else made a mount here whilst we were busy */ | 275 | /* someone else made a mount here whilst we were busy */ |
276 | while (d_mountpoint(nd->path.dentry) && | 276 | err = follow_down(&nd->path, false); |
277 | follow_down(&nd->path)) | ||
278 | ; | ||
279 | err = 0; | ||
280 | default: | 277 | default: |
281 | mntput(newmnt); | 278 | mntput(newmnt); |
282 | break; | 279 | break; |
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 0fffe1c24cec..eb67953452bb 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h | |||
@@ -229,19 +229,6 @@ int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); | |||
229 | int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); | 229 | int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); |
230 | void autofs4_catatonic_mode(struct autofs_sb_info *); | 230 | void autofs4_catatonic_mode(struct autofs_sb_info *); |
231 | 231 | ||
232 | static inline int autofs4_follow_mount(struct path *path) | ||
233 | { | ||
234 | int res = 0; | ||
235 | |||
236 | while (d_mountpoint(path->dentry)) { | ||
237 | int followed = follow_down(path); | ||
238 | if (!followed) | ||
239 | break; | ||
240 | res = 1; | ||
241 | } | ||
242 | return res; | ||
243 | } | ||
244 | |||
245 | static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi) | 232 | static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi) |
246 | { | 233 | { |
247 | return new_encode_dev(sbi->sb->s_dev); | 234 | return new_encode_dev(sbi->sb->s_dev); |
diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c index eff9a419469a..1442da4860e5 100644 --- a/fs/autofs4/dev-ioctl.c +++ b/fs/autofs4/dev-ioctl.c | |||
@@ -551,7 +551,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp, | |||
551 | 551 | ||
552 | err = have_submounts(path.dentry); | 552 | err = have_submounts(path.dentry); |
553 | 553 | ||
554 | if (follow_down(&path)) | 554 | if (follow_down_one(&path)) |
555 | magic = path.mnt->mnt_sb->s_magic; | 555 | magic = path.mnt->mnt_sb->s_magic; |
556 | } | 556 | } |
557 | 557 | ||
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index cc1d01365905..6a930b90d389 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c | |||
@@ -56,7 +56,7 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) | |||
56 | 56 | ||
57 | path_get(&path); | 57 | path_get(&path); |
58 | 58 | ||
59 | if (!follow_down(&path)) | 59 | if (!follow_down_one(&path)) |
60 | goto done; | 60 | goto done; |
61 | 61 | ||
62 | if (is_autofs4_dentry(path.dentry)) { | 62 | if (is_autofs4_dentry(path.dentry)) { |
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 651e4ef563b1..20225636a4e9 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c | |||
@@ -234,7 +234,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
234 | nd->flags); | 234 | nd->flags); |
235 | /* | 235 | /* |
236 | * For an expire of a covered direct or offset mount we need | 236 | * For an expire of a covered direct or offset mount we need |
237 | * to break out of follow_down() at the autofs mount trigger | 237 | * to break out of follow_down_one() at the autofs mount trigger |
238 | * (d_mounted--), so we can see the expiring flag, and manage | 238 | * (d_mounted--), so we can see the expiring flag, and manage |
239 | * the blocking and following here until the expire is completed. | 239 | * the blocking and following here until the expire is completed. |
240 | */ | 240 | */ |
@@ -243,7 +243,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
243 | if (ino->flags & AUTOFS_INF_EXPIRING) { | 243 | if (ino->flags & AUTOFS_INF_EXPIRING) { |
244 | spin_unlock(&sbi->fs_lock); | 244 | spin_unlock(&sbi->fs_lock); |
245 | /* Follow down to our covering mount. */ | 245 | /* Follow down to our covering mount. */ |
246 | if (!follow_down(&nd->path)) | 246 | if (!follow_down_one(&nd->path)) |
247 | goto done; | 247 | goto done; |
248 | goto follow; | 248 | goto follow; |
249 | } | 249 | } |
@@ -292,11 +292,10 @@ follow: | |||
292 | * multi-mount with no root offset so we don't need | 292 | * multi-mount with no root offset so we don't need |
293 | * to follow it. | 293 | * to follow it. |
294 | */ | 294 | */ |
295 | if (d_mountpoint(dentry)) { | 295 | if (d_managed(dentry)) { |
296 | if (!autofs4_follow_mount(&nd->path)) { | 296 | status = follow_down(&nd->path, false); |
297 | status = -ENOENT; | 297 | if (status < 0) |
298 | goto out_error; | 298 | goto out_error; |
299 | } | ||
300 | } | 299 | } |
301 | 300 | ||
302 | done: | 301 | done: |
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index c68a056f27fd..83479cf63f96 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c | |||
@@ -273,10 +273,7 @@ static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd, | |||
273 | break; | 273 | break; |
274 | case -EBUSY: | 274 | case -EBUSY: |
275 | /* someone else made a mount here whilst we were busy */ | 275 | /* someone else made a mount here whilst we were busy */ |
276 | while (d_mountpoint(nd->path.dentry) && | 276 | err = follow_down(&nd->path, false); |
277 | follow_down(&nd->path)) | ||
278 | ; | ||
279 | err = 0; | ||
280 | default: | 277 | default: |
281 | mntput(newmnt); | 278 | mntput(newmnt); |
282 | break; | 279 | break; |
diff --git a/fs/namei.c b/fs/namei.c index 16109da68bbf..9d3033dc22e9 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -960,6 +960,7 @@ static int follow_automount(struct path *path, unsigned flags, | |||
960 | 960 | ||
961 | /* | 961 | /* |
962 | * Handle a dentry that is managed in some way. | 962 | * Handle a dentry that is managed in some way. |
963 | * - Flagged for transit management (autofs) | ||
963 | * - Flagged as mountpoint | 964 | * - Flagged as mountpoint |
964 | * - Flagged as automount point | 965 | * - Flagged as automount point |
965 | * | 966 | * |
@@ -979,6 +980,16 @@ static int follow_managed(struct path *path, unsigned flags) | |||
979 | while (managed = ACCESS_ONCE(path->dentry->d_flags), | 980 | while (managed = ACCESS_ONCE(path->dentry->d_flags), |
980 | managed &= DCACHE_MANAGED_DENTRY, | 981 | managed &= DCACHE_MANAGED_DENTRY, |
981 | unlikely(managed != 0)) { | 982 | unlikely(managed != 0)) { |
983 | /* Allow the filesystem to manage the transit without i_mutex | ||
984 | * being held. */ | ||
985 | if (managed & DCACHE_MANAGE_TRANSIT) { | ||
986 | BUG_ON(!path->dentry->d_op); | ||
987 | BUG_ON(!path->dentry->d_op->d_manage); | ||
988 | ret = path->dentry->d_op->d_manage(path->dentry, false); | ||
989 | if (ret < 0) | ||
990 | return ret == -EISDIR ? 0 : ret; | ||
991 | } | ||
992 | |||
982 | /* Transit to a mounted filesystem. */ | 993 | /* Transit to a mounted filesystem. */ |
983 | if (managed & DCACHE_MOUNTED) { | 994 | if (managed & DCACHE_MOUNTED) { |
984 | struct vfsmount *mounted = lookup_mnt(path); | 995 | struct vfsmount *mounted = lookup_mnt(path); |
@@ -1012,7 +1023,7 @@ static int follow_managed(struct path *path, unsigned flags) | |||
1012 | return 0; | 1023 | return 0; |
1013 | } | 1024 | } |
1014 | 1025 | ||
1015 | int follow_down(struct path *path) | 1026 | int follow_down_one(struct path *path) |
1016 | { | 1027 | { |
1017 | struct vfsmount *mounted; | 1028 | struct vfsmount *mounted; |
1018 | 1029 | ||
@@ -1029,14 +1040,19 @@ int follow_down(struct path *path) | |||
1029 | 1040 | ||
1030 | /* | 1041 | /* |
1031 | * Skip to top of mountpoint pile in rcuwalk mode. We abort the rcu-walk if we | 1042 | * 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 | 1043 | * meet a managed dentry and we're not walking to "..". True is returned to |
1033 | * continue, false to abort. | 1044 | * continue, false to abort. |
1034 | */ | 1045 | */ |
1035 | static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, | 1046 | static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, |
1036 | struct inode **inode, bool reverse_transit) | 1047 | struct inode **inode, bool reverse_transit) |
1037 | { | 1048 | { |
1049 | unsigned abort_mask = | ||
1050 | reverse_transit ? 0 : DCACHE_MANAGE_TRANSIT; | ||
1051 | |||
1038 | while (d_mountpoint(path->dentry)) { | 1052 | while (d_mountpoint(path->dentry)) { |
1039 | struct vfsmount *mounted; | 1053 | struct vfsmount *mounted; |
1054 | if (path->dentry->d_flags & abort_mask) | ||
1055 | return true; | ||
1040 | mounted = __lookup_mnt(path->mnt, path->dentry, 1); | 1056 | mounted = __lookup_mnt(path->mnt, path->dentry, 1); |
1041 | if (!mounted) | 1057 | if (!mounted) |
1042 | break; | 1058 | break; |
@@ -1087,6 +1103,57 @@ static int follow_dotdot_rcu(struct nameidata *nd) | |||
1087 | } | 1103 | } |
1088 | 1104 | ||
1089 | /* | 1105 | /* |
1106 | * Follow down to the covering mount currently visible to userspace. At each | ||
1107 | * point, the filesystem owning that dentry may be queried as to whether the | ||
1108 | * caller is permitted to proceed or not. | ||
1109 | * | ||
1110 | * Care must be taken as namespace_sem may be held (indicated by mounting_here | ||
1111 | * being true). | ||
1112 | */ | ||
1113 | int follow_down(struct path *path, bool mounting_here) | ||
1114 | { | ||
1115 | unsigned managed; | ||
1116 | int ret; | ||
1117 | |||
1118 | while (managed = ACCESS_ONCE(path->dentry->d_flags), | ||
1119 | unlikely(managed & DCACHE_MANAGED_DENTRY)) { | ||
1120 | /* Allow the filesystem to manage the transit without i_mutex | ||
1121 | * being held. | ||
1122 | * | ||
1123 | * We indicate to the filesystem if someone is trying to mount | ||
1124 | * something here. This gives autofs the chance to deny anyone | ||
1125 | * other than its daemon the right to mount on its | ||
1126 | * superstructure. | ||
1127 | * | ||
1128 | * The filesystem may sleep at this point. | ||
1129 | */ | ||
1130 | if (managed & DCACHE_MANAGE_TRANSIT) { | ||
1131 | BUG_ON(!path->dentry->d_op); | ||
1132 | BUG_ON(!path->dentry->d_op->d_manage); | ||
1133 | ret = path->dentry->d_op->d_manage(path->dentry, mounting_here); | ||
1134 | if (ret < 0) | ||
1135 | return ret == -EISDIR ? 0 : ret; | ||
1136 | } | ||
1137 | |||
1138 | /* Transit to a mounted filesystem. */ | ||
1139 | if (managed & DCACHE_MOUNTED) { | ||
1140 | struct vfsmount *mounted = lookup_mnt(path); | ||
1141 | if (!mounted) | ||
1142 | break; | ||
1143 | dput(path->dentry); | ||
1144 | mntput(path->mnt); | ||
1145 | path->mnt = mounted; | ||
1146 | path->dentry = dget(mounted->mnt_root); | ||
1147 | continue; | ||
1148 | } | ||
1149 | |||
1150 | /* Don't handle automount points here */ | ||
1151 | break; | ||
1152 | } | ||
1153 | return 0; | ||
1154 | } | ||
1155 | |||
1156 | /* | ||
1090 | * Skip to top of mountpoint pile in refwalk mode for follow_dotdot() | 1157 | * Skip to top of mountpoint pile in refwalk mode for follow_dotdot() |
1091 | */ | 1158 | */ |
1092 | static void follow_mount(struct path *path) | 1159 | static void follow_mount(struct path *path) |
@@ -3530,6 +3597,7 @@ const struct inode_operations page_symlink_inode_operations = { | |||
3530 | }; | 3597 | }; |
3531 | 3598 | ||
3532 | EXPORT_SYMBOL(user_path_at); | 3599 | EXPORT_SYMBOL(user_path_at); |
3600 | EXPORT_SYMBOL(follow_down_one); | ||
3533 | EXPORT_SYMBOL(follow_down); | 3601 | EXPORT_SYMBOL(follow_down); |
3534 | EXPORT_SYMBOL(follow_up); | 3602 | EXPORT_SYMBOL(follow_up); |
3535 | EXPORT_SYMBOL(get_write_access); /* binfmt_aout */ | 3603 | EXPORT_SYMBOL(get_write_access); /* binfmt_aout */ |
diff --git a/fs/namespace.c b/fs/namespace.c index 3ddfd9046c44..d94ccd6ddafd 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -1844,9 +1844,10 @@ static int do_move_mount(struct path *path, char *old_name) | |||
1844 | return err; | 1844 | return err; |
1845 | 1845 | ||
1846 | down_write(&namespace_sem); | 1846 | down_write(&namespace_sem); |
1847 | while (d_mountpoint(path->dentry) && | 1847 | err = follow_down(path, true); |
1848 | follow_down(path)) | 1848 | if (err < 0) |
1849 | ; | 1849 | goto out; |
1850 | |||
1850 | err = -EINVAL; | 1851 | err = -EINVAL; |
1851 | if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt)) | 1852 | if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt)) |
1852 | goto out; | 1853 | goto out; |
@@ -1940,9 +1941,10 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path, | |||
1940 | 1941 | ||
1941 | down_write(&namespace_sem); | 1942 | down_write(&namespace_sem); |
1942 | /* Something was mounted here while we slept */ | 1943 | /* Something was mounted here while we slept */ |
1943 | while (d_mountpoint(path->dentry) && | 1944 | err = follow_down(path, true); |
1944 | follow_down(path)) | 1945 | if (err < 0) |
1945 | ; | 1946 | goto unlock; |
1947 | |||
1946 | err = -EINVAL; | 1948 | err = -EINVAL; |
1947 | if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt)) | 1949 | if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt)) |
1948 | goto unlock; | 1950 | goto unlock; |
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index 74aaf3963c10..bfcb933e5755 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c | |||
@@ -176,10 +176,7 @@ out_err: | |||
176 | path_put(&nd->path); | 176 | path_put(&nd->path); |
177 | goto out; | 177 | goto out; |
178 | out_follow: | 178 | out_follow: |
179 | while (d_mountpoint(nd->path.dentry) && | 179 | err = follow_down(&nd->path, false); |
180 | follow_down(&nd->path)) | ||
181 | ; | ||
182 | err = 0; | ||
183 | goto out; | 180 | goto out; |
184 | } | 181 | } |
185 | 182 | ||
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 230b79fbf005..0f79e33a65d7 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -88,8 +88,9 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, | |||
88 | .dentry = dget(dentry)}; | 88 | .dentry = dget(dentry)}; |
89 | int err = 0; | 89 | int err = 0; |
90 | 90 | ||
91 | while (d_mountpoint(path.dentry) && follow_down(&path)) | 91 | err = follow_down(&path, false); |
92 | ; | 92 | if (err < 0) |
93 | goto out; | ||
93 | 94 | ||
94 | exp2 = rqst_exp_get_by_name(rqstp, &path); | 95 | exp2 = rqst_exp_get_by_name(rqstp, &path); |
95 | if (IS_ERR(exp2)) { | 96 | if (IS_ERR(exp2)) { |