diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 72 |
1 files changed, 70 insertions, 2 deletions
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 */ |