diff options
| author | J. Bruce Fields <bfields@redhat.com> | 2013-10-17 11:13:00 -0400 |
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-11-09 00:16:37 -0500 |
| commit | bbf7a8a3562f2de49ce24db3be0f514459dd7f8b (patch) | |
| tree | e70d36de179acd9cabe89299674acf1a16e58e8d /fs/exportfs | |
| parent | e4b70ebeeba954cb9cbcf0f19016bb9c2b8711c1 (diff) | |
exportfs: move most of reconnect_path to helper function
Also replace 3 easily-confused three-letter acronyms by more helpful
variable names.
Just cleanup, no change in functionality, with one exception: the
dentry_connected() check in the "out_reconnected" case will now only
check the ancestors of the current dentry instead of checking all the
way from target_dir. Since we've already verified connectivity up to
this dentry, that should be sufficient.
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/exportfs')
| -rw-r--r-- | fs/exportfs/expfs.c | 164 |
1 files changed, 86 insertions, 78 deletions
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index d8ba88ac10e5..d32ead9026f0 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c | |||
| @@ -126,6 +126,86 @@ static void clear_disconnected(struct dentry *dentry) | |||
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | /* | 128 | /* |
| 129 | * Reconnect a directory dentry with its parent. | ||
| 130 | * | ||
| 131 | * This can return a dentry, or NULL, or an error. | ||
| 132 | * | ||
| 133 | * In the first case the returned dentry is the parent of the given | ||
| 134 | * dentry, and may itself need to be reconnected to its parent. | ||
| 135 | * | ||
| 136 | * In the NULL case, a concurrent VFS operation has either renamed or | ||
| 137 | * removed this directory. The concurrent operation has reconnected our | ||
| 138 | * dentry, so we no longer need to. | ||
| 139 | */ | ||
| 140 | static struct dentry *reconnect_one(struct vfsmount *mnt, | ||
| 141 | struct dentry *dentry, char *nbuf) | ||
| 142 | { | ||
| 143 | struct dentry *parent; | ||
| 144 | struct dentry *tmp; | ||
| 145 | int err; | ||
| 146 | |||
| 147 | parent = ERR_PTR(-EACCES); | ||
| 148 | mutex_lock(&dentry->d_inode->i_mutex); | ||
| 149 | if (mnt->mnt_sb->s_export_op->get_parent) | ||
| 150 | parent = mnt->mnt_sb->s_export_op->get_parent(dentry); | ||
| 151 | mutex_unlock(&dentry->d_inode->i_mutex); | ||
| 152 | |||
| 153 | if (IS_ERR(parent)) { | ||
| 154 | dprintk("%s: get_parent of %ld failed, err %d\n", | ||
| 155 | __func__, dentry->d_inode->i_ino, PTR_ERR(parent)); | ||
| 156 | return parent; | ||
| 157 | } | ||
| 158 | |||
| 159 | dprintk("%s: find name of %lu in %lu\n", __func__, | ||
| 160 | dentry->d_inode->i_ino, parent->d_inode->i_ino); | ||
| 161 | err = exportfs_get_name(mnt, parent, nbuf, dentry); | ||
| 162 | if (err == -ENOENT) | ||
| 163 | goto out_reconnected; | ||
| 164 | if (err) | ||
| 165 | goto out_err; | ||
| 166 | dprintk("%s: found name: %s\n", __func__, nbuf); | ||
| 167 | mutex_lock(&parent->d_inode->i_mutex); | ||
| 168 | tmp = lookup_one_len(nbuf, parent, strlen(nbuf)); | ||
| 169 | mutex_unlock(&parent->d_inode->i_mutex); | ||
| 170 | if (IS_ERR(tmp)) { | ||
| 171 | dprintk("%s: lookup failed: %d\n", __func__, PTR_ERR(tmp)); | ||
| 172 | goto out_err; | ||
| 173 | } | ||
| 174 | if (tmp != dentry) { | ||
| 175 | dput(tmp); | ||
| 176 | goto out_reconnected; | ||
| 177 | } | ||
| 178 | dput(tmp); | ||
| 179 | if (IS_ROOT(dentry)) { | ||
| 180 | err = -ESTALE; | ||
| 181 | goto out_err; | ||
| 182 | } | ||
| 183 | return parent; | ||
| 184 | |||
| 185 | out_err: | ||
| 186 | dput(parent); | ||
| 187 | return ERR_PTR(err); | ||
| 188 | out_reconnected: | ||
| 189 | dput(parent); | ||
| 190 | /* | ||
| 191 | * Someone must have renamed our entry into another parent, in | ||
| 192 | * which case it has been reconnected by the rename. | ||
| 193 | * | ||
| 194 | * Or someone removed it entirely, in which case filehandle | ||
| 195 | * lookup will succeed but the directory is now IS_DEAD and | ||
| 196 | * subsequent operations on it will fail. | ||
| 197 | * | ||
| 198 | * Alternatively, maybe there was no race at all, and the | ||
| 199 | * filesystem is just corrupt and gave us a parent that doesn't | ||
| 200 | * actually contain any entry pointing to this inode. So, | ||
| 201 | * double check that this worked and return -ESTALE if not: | ||
| 202 | */ | ||
| 203 | if (!dentry_connected(dentry)) | ||
| 204 | return ERR_PTR(-ESTALE); | ||
| 205 | return NULL; | ||
| 206 | } | ||
| 207 | |||
| 208 | /* | ||
| 129 | * Make sure target_dir is fully connected to the dentry tree. | 209 | * Make sure target_dir is fully connected to the dentry tree. |
| 130 | * | 210 | * |
| 131 | * On successful return, DCACHE_DISCONNECTED will be cleared on | 211 | * On successful return, DCACHE_DISCONNECTED will be cleared on |
| @@ -158,76 +238,19 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf) | |||
| 158 | dput(pd); | 238 | dput(pd); |
| 159 | break; | 239 | break; |
| 160 | } else { | 240 | } else { |
| 241 | struct dentry *parent; | ||
| 161 | /* | 242 | /* |
| 162 | * We have hit the top of a disconnected path, try to | 243 | * We have hit the top of a disconnected path, try to |
| 163 | * find parent and connect. | 244 | * find parent and connect. |
| 164 | * | ||
| 165 | * Racing with some other process renaming a directory | ||
| 166 | * isn't much of a problem here. If someone renames | ||
| 167 | * the directory, it will end up properly connected, | ||
| 168 | * which is what we want | ||
| 169 | * | ||
| 170 | * Getting the parent can't be supported generically, | ||
| 171 | * the locking is too icky. | ||
| 172 | * | ||
| 173 | * Instead we just return EACCES. If server reboots | ||
| 174 | * or inodes get flushed, you lose | ||
| 175 | */ | ||
| 176 | struct dentry *ppd = ERR_PTR(-EACCES); | ||
| 177 | struct dentry *npd; | ||
| 178 | |||
| 179 | mutex_lock(&pd->d_inode->i_mutex); | ||
| 180 | if (mnt->mnt_sb->s_export_op->get_parent) | ||
| 181 | ppd = mnt->mnt_sb->s_export_op->get_parent(pd); | ||
| 182 | mutex_unlock(&pd->d_inode->i_mutex); | ||
| 183 | |||
| 184 | if (IS_ERR(ppd)) { | ||
| 185 | err = PTR_ERR(ppd); | ||
| 186 | dprintk("%s: get_parent of %ld failed, err %d\n", | ||
| 187 | __func__, pd->d_inode->i_ino, err); | ||
| 188 | dput(pd); | ||
| 189 | break; | ||
| 190 | } | ||
| 191 | |||
| 192 | dprintk("%s: find name of %lu in %lu\n", __func__, | ||
| 193 | pd->d_inode->i_ino, ppd->d_inode->i_ino); | ||
| 194 | err = exportfs_get_name(mnt, ppd, nbuf, pd); | ||
| 195 | if (err) { | ||
| 196 | dput(ppd); | ||
| 197 | dput(pd); | ||
| 198 | if (err == -ENOENT) | ||
| 199 | /* some race between get_parent and | ||
| 200 | * get_name? | ||
| 201 | */ | ||
| 202 | goto out_reconnected; | ||
| 203 | break; | ||
| 204 | } | ||
| 205 | dprintk("%s: found name: %s\n", __func__, nbuf); | ||
| 206 | mutex_lock(&ppd->d_inode->i_mutex); | ||
| 207 | npd = lookup_one_len(nbuf, ppd, strlen(nbuf)); | ||
| 208 | mutex_unlock(&ppd->d_inode->i_mutex); | ||
| 209 | if (IS_ERR(npd)) { | ||
| 210 | err = PTR_ERR(npd); | ||
| 211 | dprintk("%s: lookup failed: %d\n", | ||
| 212 | __func__, err); | ||
| 213 | dput(ppd); | ||
| 214 | dput(pd); | ||
| 215 | break; | ||
| 216 | } | ||
| 217 | /* we didn't really want npd, we really wanted | ||
| 218 | * a side-effect of the lookup. | ||
| 219 | * hopefully, npd == pd, though it isn't really | ||
| 220 | * a problem if it isn't | ||
| 221 | */ | 245 | */ |
| 222 | dput(npd); | 246 | parent = reconnect_one(mnt, pd, nbuf); |
| 223 | dput(ppd); | 247 | if (!parent) |
| 224 | if (npd != pd) | ||
| 225 | goto out_reconnected; | 248 | goto out_reconnected; |
| 226 | if (IS_ROOT(pd)) { | 249 | if (IS_ERR(parent)) { |
| 227 | /* something went wrong, we have to give up */ | 250 | err = PTR_ERR(parent); |
| 228 | dput(pd); | ||
| 229 | break; | 251 | break; |
| 230 | } | 252 | } |
| 253 | dput(parent); | ||
| 231 | } | 254 | } |
| 232 | dput(pd); | 255 | dput(pd); |
| 233 | } | 256 | } |
| @@ -241,21 +264,6 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf) | |||
| 241 | 264 | ||
| 242 | return 0; | 265 | return 0; |
| 243 | out_reconnected: | 266 | out_reconnected: |
| 244 | /* | ||
| 245 | * Someone must have renamed our entry into another parent, in | ||
| 246 | * which case it has been reconnected by the rename. | ||
| 247 | * | ||
| 248 | * Or someone removed it entirely, in which case filehandle | ||
| 249 | * lookup will succeed but the directory is now IS_DEAD and | ||
| 250 | * subsequent operations on it will fail. | ||
| 251 | * | ||
| 252 | * Alternatively, maybe there was no race at all, and the | ||
| 253 | * filesystem is just corrupt and gave us a parent that doesn't | ||
| 254 | * actually contain any entry pointing to this inode. So, | ||
| 255 | * double check that this worked and return -ESTALE if not: | ||
| 256 | */ | ||
| 257 | if (!dentry_connected(target_dir)) | ||
| 258 | return -ESTALE; | ||
| 259 | clear_disconnected(target_dir); | 267 | clear_disconnected(target_dir); |
| 260 | return 0; | 268 | return 0; |
| 261 | } | 269 | } |
