diff options
-rw-r--r-- | Documentation/filesystems/Locking | 2 | ||||
-rw-r--r-- | Documentation/filesystems/vfs.txt | 21 | ||||
-rw-r--r-- | drivers/staging/autofs/dirhash.c | 5 | ||||
-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 | ||||
-rw-r--r-- | include/linux/dcache.h | 11 | ||||
-rw-r--r-- | include/linux/namei.h | 3 |
15 files changed, 126 insertions, 50 deletions
diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 5f0c52a07386..cbf98b989b11 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking | |||
@@ -20,6 +20,7 @@ prototypes: | |||
20 | void (*d_iput)(struct dentry *, struct inode *); | 20 | void (*d_iput)(struct dentry *, struct inode *); |
21 | char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); | 21 | char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); |
22 | struct vfsmount *(*d_automount)(struct path *path); | 22 | struct vfsmount *(*d_automount)(struct path *path); |
23 | int (*d_manage)(struct dentry *, bool); | ||
23 | 24 | ||
24 | locking rules: | 25 | locking rules: |
25 | rename_lock ->d_lock may block rcu-walk | 26 | rename_lock ->d_lock may block rcu-walk |
@@ -31,6 +32,7 @@ d_release: no no yes no | |||
31 | d_iput: no no yes no | 32 | d_iput: no no yes no |
32 | d_dname: no no no no | 33 | d_dname: no no no no |
33 | d_automount: no no yes no | 34 | d_automount: no no yes no |
35 | d_manage: no no yes no | ||
34 | 36 | ||
35 | --------------------------- inode_operations --------------------------- | 37 | --------------------------- inode_operations --------------------------- |
36 | prototypes: | 38 | prototypes: |
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 726a4f6fa3c9..4682586b147a 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt | |||
@@ -865,6 +865,7 @@ struct dentry_operations { | |||
865 | void (*d_iput)(struct dentry *, struct inode *); | 865 | void (*d_iput)(struct dentry *, struct inode *); |
866 | char *(*d_dname)(struct dentry *, char *, int); | 866 | char *(*d_dname)(struct dentry *, char *, int); |
867 | struct vfsmount *(*d_automount)(struct path *); | 867 | struct vfsmount *(*d_automount)(struct path *); |
868 | int (*d_manage)(struct dentry *, bool); | ||
868 | }; | 869 | }; |
869 | 870 | ||
870 | d_revalidate: called when the VFS needs to revalidate a dentry. This | 871 | d_revalidate: called when the VFS needs to revalidate a dentry. This |
@@ -938,12 +939,30 @@ struct dentry_operations { | |||
938 | target and the parent VFS mount record to provide inheritable mount | 939 | target and the parent VFS mount record to provide inheritable mount |
939 | parameters. NULL should be returned if someone else managed to make | 940 | parameters. NULL should be returned if someone else managed to make |
940 | the automount first. If the automount failed, then an error code | 941 | the automount first. If the automount failed, then an error code |
941 | should be returned. | 942 | should be returned. If -EISDIR is returned, then the directory will |
943 | be treated as an ordinary directory and returned to pathwalk to | ||
944 | continue walking. | ||
942 | 945 | ||
943 | This function is only used if DCACHE_NEED_AUTOMOUNT is set on the | 946 | This function is only used if DCACHE_NEED_AUTOMOUNT is set on the |
944 | dentry. This is set by __d_instantiate() if S_AUTOMOUNT is set on the | 947 | dentry. This is set by __d_instantiate() if S_AUTOMOUNT is set on the |
945 | inode being added. | 948 | inode being added. |
946 | 949 | ||
950 | d_manage: called to allow the filesystem to manage the transition from a | ||
951 | dentry (optional). This allows autofs, for example, to hold up clients | ||
952 | waiting to explore behind a 'mountpoint' whilst letting the daemon go | ||
953 | past and construct the subtree there. 0 should be returned to let the | ||
954 | calling process continue. -EISDIR can be returned to tell pathwalk to | ||
955 | use this directory as an ordinary directory and to ignore anything | ||
956 | mounted on it and not to check the automount flag. Any other error | ||
957 | code will abort pathwalk completely. | ||
958 | |||
959 | If the 'mounting_here' parameter is true, then namespace_sem is being | ||
960 | held by the caller and the function should not initiate any mounts or | ||
961 | unmounts that it will then wait for. | ||
962 | |||
963 | This function is only used if DCACHE_MANAGE_TRANSIT is set on the | ||
964 | dentry being transited from. | ||
965 | |||
947 | Example : | 966 | Example : |
948 | 967 | ||
949 | static char *pipefs_dname(struct dentry *dent, char *buffer, int buflen) | 968 | static char *pipefs_dname(struct dentry *dent, char *buffer, int buflen) |
diff --git a/drivers/staging/autofs/dirhash.c b/drivers/staging/autofs/dirhash.c index d3f42c8325f7..a08bd7355035 100644 --- a/drivers/staging/autofs/dirhash.c +++ b/drivers/staging/autofs/dirhash.c | |||
@@ -88,14 +88,13 @@ struct autofs_dir_ent *autofs_expire(struct super_block *sb, | |||
88 | } | 88 | } |
89 | path.mnt = mnt; | 89 | path.mnt = mnt; |
90 | path_get(&path); | 90 | path_get(&path); |
91 | if (!follow_down(&path)) { | 91 | if (!follow_down_one(&path)) { |
92 | path_put(&path); | 92 | path_put(&path); |
93 | DPRINTK(("autofs: not expirable\ | 93 | DPRINTK(("autofs: not expirable\ |
94 | (not a mounted directory): %s\n", ent->name)); | 94 | (not a mounted directory): %s\n", ent->name)); |
95 | continue; | 95 | continue; |
96 | } | 96 | } |
97 | while (d_mountpoint(path.dentry) && follow_down(&path)) | 97 | follow_down(&path, false); // TODO: need to check error |
98 | ; | ||
99 | umount_ok = may_umount(path.mnt); | 98 | umount_ok = may_umount(path.mnt); |
100 | path_put(&path); | 99 | path_put(&path); |
101 | 100 | ||
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)) { |
diff --git a/include/linux/dcache.h b/include/linux/dcache.h index ee6c26d142c3..1a87760d6532 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h | |||
@@ -168,6 +168,7 @@ struct dentry_operations { | |||
168 | void (*d_iput)(struct dentry *, struct inode *); | 168 | void (*d_iput)(struct dentry *, struct inode *); |
169 | char *(*d_dname)(struct dentry *, char *, int); | 169 | char *(*d_dname)(struct dentry *, char *, int); |
170 | struct vfsmount *(*d_automount)(struct path *); | 170 | struct vfsmount *(*d_automount)(struct path *); |
171 | int (*d_manage)(struct dentry *, bool); | ||
171 | } ____cacheline_aligned; | 172 | } ____cacheline_aligned; |
172 | 173 | ||
173 | /* | 174 | /* |
@@ -214,8 +215,9 @@ struct dentry_operations { | |||
214 | 215 | ||
215 | #define DCACHE_MOUNTED 0x10000 /* is a mountpoint */ | 216 | #define DCACHE_MOUNTED 0x10000 /* is a mountpoint */ |
216 | #define DCACHE_NEED_AUTOMOUNT 0x20000 /* handle automount on this dir */ | 217 | #define DCACHE_NEED_AUTOMOUNT 0x20000 /* handle automount on this dir */ |
218 | #define DCACHE_MANAGE_TRANSIT 0x40000 /* manage transit from this dirent */ | ||
217 | #define DCACHE_MANAGED_DENTRY \ | 219 | #define DCACHE_MANAGED_DENTRY \ |
218 | (DCACHE_MOUNTED|DCACHE_NEED_AUTOMOUNT) | 220 | (DCACHE_MOUNTED|DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT) |
219 | 221 | ||
220 | extern seqlock_t rename_lock; | 222 | extern seqlock_t rename_lock; |
221 | 223 | ||
@@ -404,7 +406,12 @@ static inline void dont_mount(struct dentry *dentry) | |||
404 | 406 | ||
405 | extern void dput(struct dentry *); | 407 | extern void dput(struct dentry *); |
406 | 408 | ||
407 | static inline int d_mountpoint(struct dentry *dentry) | 409 | static inline bool d_managed(struct dentry *dentry) |
410 | { | ||
411 | return dentry->d_flags & DCACHE_MANAGED_DENTRY; | ||
412 | } | ||
413 | |||
414 | static inline bool d_mountpoint(struct dentry *dentry) | ||
408 | { | 415 | { |
409 | return dentry->d_flags & DCACHE_MOUNTED; | 416 | return dentry->d_flags & DCACHE_MOUNTED; |
410 | } | 417 | } |
diff --git a/include/linux/namei.h b/include/linux/namei.h index 18d06add0a40..8ef2c789c2a8 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h | |||
@@ -79,7 +79,8 @@ extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry | |||
79 | 79 | ||
80 | extern struct dentry *lookup_one_len(const char *, struct dentry *, int); | 80 | extern struct dentry *lookup_one_len(const char *, struct dentry *, int); |
81 | 81 | ||
82 | extern int follow_down(struct path *); | 82 | extern int follow_down_one(struct path *); |
83 | extern int follow_down(struct path *, bool); | ||
83 | extern int follow_up(struct path *); | 84 | extern int follow_up(struct path *); |
84 | 85 | ||
85 | extern struct dentry *lock_rename(struct dentry *, struct dentry *); | 86 | extern struct dentry *lock_rename(struct dentry *, struct dentry *); |